轉(zhuǎn)載鏈接:http://www.reibang.com/p/5a5bea9180e7
不得不說,YYKit系列的橫空出世讓很多人對國內(nèi)的開發(fā)者都摘下了有色眼鏡,原來并非大神全是國外的,自己膜拜之余也想讀一下具體的一些實(shí)現(xiàn)細(xì)節(jié),所以到github上fork了
YYWebImage
閱讀源碼并把注釋寫在response里面,權(quán)當(dāng)筆記.
第一天,YYWebImageManager
類
頭文件
#import <UIKit/UIKit.h>
#if __has_include(<YYWebImage/YYWebImage.h>)
#import <YYWebImage/YYImageCache.h>
#else
#import "YYImageCache.h"
#endif
@class YYWebImageOperation;
NS_ASSUME_NONNULL_BEGIN
/// The options to control image operation.
// 控制圖片請求的模式
typedef NS_OPTIONS(NSUInteger, YYWebImageOptions) {
/// Show network activity on status bar when download image.
/// 當(dāng)下載圖片的時候會在狀態(tài)欄顯示一個當(dāng)前網(wǎng)絡(luò)狀況
YYWebImageOptionShowNetworkActivity = 1 << 0,
/// Display progressive/interlaced/baseline image during download (same as web browser).
/// 能夠像瀏覽器一樣,顯示一個逐漸顯示的圖片,有三種方式:漸進(jìn)顯示,中間帶交叉效果,基于基線顯示.這里可以看demo理解三種模式的區(qū)別
YYWebImageOptionProgressive = 1 << 1,
/// Display blurred progressive JPEG or interlaced PNG image during download.
/// This will ignore baseline image for better user experience.
/// 下載的時候顯示一個 `模糊的` 漸漸顯示的JPEG圖片,或者一個交錯顯示的PNG圖片,具體效果還是看demo
/// 這種模式會忽略baseline這種顯示模式來獲得更好的用戶體驗(yàn)
YYWebImageOptionProgressiveBlur = 1 << 2,
/// Use NSURLCache instead of YYImageCache.
/// 使用 NSURLCache 來代替 YYImageCache
YYWebImageOptionUseNSURLCache = 1 << 3,
/// Allows untrusted SSL ceriticates.
/// 允許未受信任的SSL證書,PS:基于我的理解以及對比SDWebImage,這種模式一般用戶調(diào)試過程,不用于生產(chǎn)過程
YYWebImageOptionAllowInvalidSSLCertificates = 1 << 4,
/// Allows background task to download image when app is in background.
/// app進(jìn)入后臺的時候允許后臺下載圖片
YYWebImageOptionAllowBackgroundTask = 1 << 5,
/// Handles cookies stored in NSHTTPCookieStore.
/// 把cookies存儲進(jìn) NSHTTPCookieStore
YYWebImageOptionHandleCookies = 1 << 6,
/// Load the image from remote and refresh the image cache.
/// 從遠(yuǎn)程下載圖片并且刷新圖片緩存, 這種模式可以用于更換了圖片內(nèi)容,但是圖片URL不替換
YYWebImageOptionRefreshImageCache = 1 << 7,
/// Do not load image from/to disk cache.
/// 不從硬盤緩存加載圖片,同時也不會把圖片緩存進(jìn)磁盤
YYWebImageOptionIgnoreDiskCache = 1 << 8,
/// Do not change the view's image before set a new URL to it.
/// 當(dāng)沒有通過一個URL下載到一個新的圖片的時候不去修改圖片,PS:字面意思,忽略占位圖肠骆;
YYWebImageOptionIgnorePlaceHolder = 1 << 9,
/// Ignore image decoding.
/// This may used for image downloading without display.
/// 忽略圖片解碼失尖。
/// 這種模式可能用于下載的時候并不去顯示該圖片愿阐;
YYWebImageOptionIgnoreImageDecoding = 1 << 10,
/// Ignore multi-frame image decoding.
/// This will handle the GIF/APNG/WebP/ICO image as single frame image.
/// 忽略多frame圖片解碼
/// 這種模式會將 GIF/APNG/WebP/ICO圖片轉(zhuǎn)換為單一frame的圖片盒蟆;PS: 開發(fā)中如果需求圖片固定顯示大小,這個模式可能會有用
YYWebImageOptionIgnoreAnimatedImage = 1 << 11,
/// Set the image to view with a fade animation.
/// This will add a "fade" animation on image view's layer for better user experience.
/// 設(shè)置圖片的時候帶有一個fade的動畫效果
/// 會給view's layer添加一個淡入淡出動畫效果來獲取更好的用戶體驗(yàn)
YYWebImageOptionSetImageWithFadeAnimation = 1 << 12,
/// Do not set the image to the view when image fetch complete.
/// You may set the image manually.
/// 當(dāng)圖片下載完成之前不去設(shè)置它Image
/// 你可以手動設(shè)置圖片
YYWebImageOptionAvoidSetImage = 1 << 13,
/// This flag will add the URL to a blacklist (in memory) when the URL fail to be downloaded,
/// so the library won't keep trying.
/// 這種模式會把URL加進(jìn)黑名單中栅迄,當(dāng)下載失敗的時候,黑名單存儲在內(nèi)存中,所以這種模式不會嘗試重復(fù)下載
YYWebImageOptionIgnoreFailedURL = 1 << 14,
};
/// Indicated where the image came from.
/// 用來告訴我們圖片來源
typedef NS_ENUM(NSUInteger, YYWebImageFromType) {
/// No value. 空
YYWebImageFromNone = 0,
/// Fetched from memory cache immediately.
/// If you called "setImageWithURL:..." and the image is already in memory,
/// then you will get this value at the same call.
/// 立刻從內(nèi)存中查找圖片,如果你調(diào)用了"setImageWithURL..."并且圖片已經(jīng)存在于內(nèi)存,你會從相同的回調(diào)里面得到這個值冲茸;
YYWebImageFromMemoryCacheFast,
/// Fetched from memory cache. 從內(nèi)存中獲取的
YYWebImageFromMemoryCache,
/// Fetched from disk cache. 從磁盤中獲取的
YYWebImageFromDiskCache,
/// Fetched from remote (web or file path). 從遠(yuǎn)程下載的,可以是web或者一個路徑
YYWebImageFromRemote,
};
/// Indicated image fetch complete stage.
/// 用來告訴我們圖片下載的完成度的
typedef NS_ENUM(NSInteger, YYWebImageStage) {
/// Incomplete, progressive image. 未完成,帶進(jìn)度的image
YYWebImageStageProgress = -1,
/// Cancelled. 已經(jīng)取消了
YYWebImageStageCancelled = 0,
/// Finished (succeed or failed). 已經(jīng)結(jié)束,可能是成功或者失敗
YYWebImageStageFinished = 1,
};
/**
The block invoked in remote image fetch progress.
從遠(yuǎn)程下載完成進(jìn)度的回調(diào),
參數(shù)receivedSize是已經(jīng)下載的大小,expectedSize是總共大小,
因此可以通過receivedSize/expectedSize獲得progress,如果expectedSize = -1代表著不知道一共有多大奠涌;
@param receivedSize Current received size in bytes.
@param expectedSize Expected total size in bytes (-1 means unknown).
*/
typedef void(^YYWebImageProgressBlock)(NSInteger receivedSize, NSInteger expectedSize);
/**
圖片從遠(yuǎn)程下載完成之前邪乍,會執(zhí)行這個block,用來執(zhí)行一些額外的操作降狠;(例如圖片裁剪、模糊庇楞、圓角)
@discussion 當(dāng)'YYWebImageCompletionBlock'這個完成回調(diào)在下載完成之前榜配,會執(zhí)行這個回調(diào)用來給你一個機(jī)會做一些額外的處理,比如用來修改圖片尺寸等. 如果這里不需要對圖片進(jìn)行transform處理,只會返回image這一個參數(shù)
@example 你可以裁剪/模糊圖片,或者添加圓角,通過以下代碼:
^(UIImage *image, NSURL *url){
// 可能你需要創(chuàng)建一個 @autoreleasepool來限制內(nèi)存開銷
image = [image yy_imageByResizeToSize:CGSizeMake(100, 100) contentMode:UIViewContentModeScaleAspectFill];
image = [image yy_imageByBlurRadius:20 tintColor:nil tintMode:kCGBlendModeNormal saturation:1.2 maskImage:nil];
image = [image yy_imageByRoundCornerRadius:5];
return image;
}
*/
/**
The block invoked before remote image fetch finished to do additional image process.
@discussion This block will be invoked before `YYWebImageCompletionBlock` to give
you a chance to do additional image process (such as resize or crop). If there's
no need to transform the image, just return the `image` parameter.
@example You can clip the image, blur it and add rounded corners with these code:
^(UIImage *image, NSURL *url) {
// Maybe you need to create an @autoreleasepool to limit memory cost.
image = [image yy_imageByResizeToSize:CGSizeMake(100, 100) contentMode:UIViewContentModeScaleAspectFill];
image = [image yy_imageByBlurRadius:20 tintColor:nil tintMode:kCGBlendModeNormal saturation:1.2 maskImage:nil];
image = [image yy_imageByRoundCornerRadius:5];
return image;
}
@param image The image fetched from url.
@param url The image url (remote or local file path).
@return The transformed image.
*/
typedef UIImage * _Nullable (^YYWebImageTransformBlock)(UIImage *image, NSURL *url);
/**
這個block會在當(dāng)圖片下載完成或者取消的時候調(diào)用
@param image The image.
@param url 圖片url,遠(yuǎn)程或者本地路徑
@param from 圖片從哪來,
@param error 圖片下載中的錯誤
@param finished 如果請求取消掉了,返回NO,其他是YES
*/
/**
The block invoked when image fetch finished or cancelled.
@param image The image.
@param url The image url (remote or local file path).
@param from Where the image came from.
@param error Error during image fetching.
@param finished If the operation is cancelled, this value is NO, otherwise YES.
*/
typedef void (^YYWebImageCompletionBlock)(UIImage * _Nullable image,
NSURL *url,
YYWebImageFromType from,
YYWebImageStage stage,
NSError * _Nullable error);
/**
A manager to create and manage web image operation.
用來創(chuàng)建和管理網(wǎng)絡(luò)圖片任務(wù)的管理器吕晌;這個類其實(shí)就一個作用,管理生成一個YYWebImageOperation實(shí)例
*/
@interface YYWebImageManager : NSObject
/**
Returns global YYWebImageManager instance.
不需要多解釋,返回單例類
@return YYWebImageManager shared instance.
*/
+ (instancetype)sharedManager;
/**
* 生成一個manager,帶有緩存與操作隊列
*
* @param cache manager用到的圖片緩存, (傳nil不使用緩存)
* @param queue 圖片請求,調(diào)度運(yùn)行的請求隊列蛋褥,(傳nil,生成新的operation立即啟動不需要隊列queue)
*
* @return 一個新的manager
*/
/**
Creates a manager with an image cache and operation queue.
@param cache Image cache used by manager (pass nil to avoid image cache).
@param queue The operation queue on which image operations are scheduled and run
(pass nil to make the new operation start immediately without queue).
@return A new manager.
*/
- (instancetype)initWithCache:(nullable YYImageCache *)cache
queue:(nullable NSOperationQueue *)queue NS_DESIGNATED_INITIALIZER;
/// UNAVAILABLE_ATTRIBUTE 取消init睛驳、new方法烙心;NS_DESIGNATED_INITIALIZER指定初始化方法
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
/**
* 創(chuàng)建返回一個新的operation,這個operation會立刻開始執(zhí)行
*
* @param url 圖片url,可以是遠(yuǎn)程或者本地路徑
* @param options 控制下載的option
* @param progress 進(jìn)度回調(diào)block,會在后臺線程的時候調(diào)用,傳空的話會禁用此特性
* @param transform 附加的transform處理block膜廊,會在后臺線程的時候調(diào)用,傳空禁用此block
* @param completion 完成回調(diào)block,會在后臺線程的時候調(diào)用,傳空禁用此block
*
* @return 一個新的圖片operation
*/
/**
Creates and returns a new image operation, the operation will start immediately.
@param url The image url (remote or local file path).
@param options The options to control image operation.
@param progress Progress block which will be invoked on background thread (pass nil to avoid).
@param transform Transform block which will be invoked on background thread (pass nil to avoid).
@param completion Completion block which will be invoked on background thread (pass nil to avoid).
@return A new image operation.
*/
- (nullable YYWebImageOperation *)requestImageWithURL:(NSURL *)url
options:(YYWebImageOptions)options
progress:(nullable YYWebImageProgressBlock)progress
transform:(nullable YYWebImageTransformBlock)transform
completion:(nullable YYWebImageCompletionBlock)completion;
/**
The image cache used by image operation.
You can set it to nil to avoid image cache.
圖片請求用到的緩存,可以設(shè)置為nil來禁用緩存
*/
@property (nullable, nonatomic, strong) YYImageCache *cache;
/**
* 圖片的請求調(diào)度運(yùn)行的隊列
* 你不通過隊列淫茵,新建一個新的operation的時候爪瓜,可以給這個值置為nil;
*
* 你可以用這個隊列來控制請求并發(fā)的最大最小數(shù)量,獲得當(dāng)前操作隊列的狀態(tài)值,或者來取消這個manager中所有的operation
*/
/**
The operation queue on which image operations are scheduled and run.
You can set it to nil to make the new operation start immediately without queue.
You can use this queue to control maximum number of concurrent operations, to obtain
the status of the current operations, or to cancel all operations in this manager.
*/
@property (nullable, nonatomic, strong) NSOperationQueue *queue;
/**
* 默認(rèn)值為nil,共享的圖片變換的過程,
* 當(dāng)調(diào)用`requestImageWithURL:options:progress:transform:completion`并且`transform`不為nil時,這個block才有用
*/
/**
The shared transform block to process image. Default is nil.
When called `requestImageWithURL:options:progress:transform:completion` and
the `transform` is not nil, this block will be used.
*/
@property (nullable, nonatomic, copy) YYWebImageTransformBlock sharedTransformBlock;
/**
The image request timeout interval in seconds. Default is 15.
請求超時時間,默認(rèn)15秒
*/
@property (nonatomic) NSTimeInterval timeout;
/**
The username used by NSURLCredential, default is nil.
NSURLCredential使用的用戶名,默認(rèn)為nil
*/
@property (nullable, nonatomic, copy) NSString *username;
/**
The password used by NSURLCredential, default is nil.
NSURLCredential使用的密碼,默認(rèn)為nil
*/
@property (nullable, nonatomic, copy) NSString *password;
/**
The image HTTP request header. Default is "Accept:image/webp,image/\*;q=0.8".
圖片HTTP的請求頭,默認(rèn)是"Accept:image/webp,image/\*;q=0.8"
*/
@property (nullable, nonatomic, copy) NSDictionary<NSString *, NSString *> *headers;
/**
每個圖片http請求做額外的 HTTP header 操作的時候會調(diào)用這個block,默認(rèn)為nil
使用這個block匙瘪,可以為指定的URL铆铆,添加或移除 HTTP header field;
*/
/**
A block which will be invoked for each image HTTP request to do additional
HTTP header process. Default is nil.
Use this block to add or remove HTTP header field for a specified URL.
*/
@property (nullable, nonatomic, copy) NSDictionary<NSString *, NSString *> *(^headersFilter)(NSURL *url, NSDictionary<NSString *, NSString *> * _Nullable header);
/**
每個圖片的操作都會調(diào)用這個block,默認(rèn)為nil
使用這個block能夠給URL提供一個自定義的的圖片
*/
/**
A block which will be invoked for each image operation. Default is nil.
Use this block to provide a custom image cache key for a specified URL.
*/
@property (nullable, nonatomic, copy) NSString *(^cacheKeyFilter)(NSURL *url);
/**
Returns the HTTP headers for a specified URL.
返回URL的 HTTP headers
@param url A specified URL.
@return HTTP headers.
*/
- (nullable NSDictionary<NSString *, NSString *> *)headersForURL:(NSURL *)url;
/**
Returns the cache key for a specified URL.
給URL返回一個指定的cacheKey
@param url A specified URL
@return Cache key used in YYImageCache. key在YYImageCache中有用到
*/
- (NSString *)cacheKeyForURL:(NSURL *)url;
/**
增加活躍的網(wǎng)絡(luò)請求數(shù)量
如果在增加前數(shù)量前為0,那么會在狀態(tài)欄開始一個網(wǎng)絡(luò)菊花動畫
該方法是線程安全的
該方法不會對APP擴(kuò)展產(chǎn)生影響
*/
/**
Increments the number of active network requests.
If this number was zero before incrementing, this will start animating the
status bar network activity indicator.
This method is thread safe.
This method has no effect in App Extension.
*/
+ (void)incrementNetworkActivityCount;
/**
與上面對應(yīng),減少活躍的網(wǎng)絡(luò)請求數(shù)量,如果執(zhí)行完畢之后數(shù)量變?yōu)?,那么會停止在狀態(tài)欄的網(wǎng)絡(luò)指示器動畫
線程安全
不會影響APP擴(kuò)展
*/
/**
Decrements the number of active network requests.
If this number becomes zero after decrementing, this will stop animating the
status bar network activity indicator.
This method is thread safe.
This method has no effect in App Extension.
*/
+ (void)decrementNetworkActivityCount;
/**
獲取當(dāng)前活躍的網(wǎng)絡(luò)請求數(shù)量
線程安全
不會影響APP擴(kuò)展
*/
/**
Get current number of active network requests.
This method is thread safe.
This method has no effect in App Extension.
*/
+ (NSInteger)currentNetworkActivityCount;
@end
NS_ASSUME_NONNULL_END
實(shí)現(xiàn)文件
#import "YYWebImageManager.h"
#import "YYImageCache.h"
#import "YYWebImageOperation.h"
#import "YYImageCoder.h"
#import <objc/runtime.h>
#define kNetworkIndicatorDelay (1/30.0)
/// App Extension中返回nil丹喻,否則返回sharedApplication薄货;
/// Returns nil in App Extension.
static UIApplication *_YYSharedApplication() {
static BOOL isAppExtension = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = NSClassFromString(@"UIApplication");
if(!cls || ![cls respondsToSelector:@selector(sharedApplication)]) isAppExtension = YES;
if ([[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]) isAppExtension = YES;
});
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
return isAppExtension ? nil : [UIApplication performSelector:@selector(sharedApplication)];
#pragma clang diagnostic pop
}
@interface _YYWebImageApplicationNetworkIndicatorInfo : NSObject
@property (nonatomic, assign) NSInteger count;
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation _YYWebImageApplicationNetworkIndicatorInfo
@end
@implementation YYWebImageManager
/**
單例類
在生成的時候會生成一個YYImageCache單例類,會新建一個 NSOperationQueue
*/
+ (instancetype)sharedManager {
static YYWebImageManager *manager;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
YYImageCache *cache = [YYImageCache sharedCache];
NSOperationQueue *queue = [NSOperationQueue new];
if ([queue respondsToSelector:@selector(setQualityOfService:)]) {
queue.qualityOfService = NSQualityOfServiceBackground; // 后臺優(yōu)先級
}
manager = [[self alloc] initWithCache:cache queue:queue];
});
return manager;
}
- (instancetype)init {
@throw [NSException exceptionWithName:@"YYWebImageManager init error" reason:@"Use the designated initializer to init." userInfo:nil];
return [self initWithCache:nil queue:nil];
}
- (instancetype)initWithCache:(YYImageCache *)cache queue:(NSOperationQueue *)queue{
self = [super init];
if (!self) return nil;
// 這里很好的遵循了蘋果規(guī)范,初始化的時候先調(diào)用父類,同時初始化了_cache,_queue,_timeout,_header這些屬性
_cache = cache;
_queue = queue;
_timeout = 15.0;
if (YYImageWebPAvailable()) {
_headers = @{ @"Accept" : @"image/webp,image/*;q=0.8" };
} else {
_headers = @{ @"Accept" : @"image/*;q=0.8" };
}
return self;
}
// 這里就是具體的下載請求方法了
- (YYWebImageOperation *)requestImageWithURL:(NSURL *)url
options:(YYWebImageOptions)options
progress:(YYWebImageProgressBlock)progress
transform:(YYWebImageTransformBlock)transform
completion:(YYWebImageCompletionBlock)completion {
// 1、先生成一個request,并且根據(jù)傳入?yún)?shù)生成request參數(shù)
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.timeoutInterval = _timeout;
request.HTTPShouldHandleCookies = (options & YYWebImageOptionHandleCookies) != 0;
request.allHTTPHeaderFields = [self headersForURL:url]; // 設(shè)置請求頭
request.HTTPShouldUsePipelining = YES;
request.cachePolicy = (options & YYWebImageOptionUseNSURLCache) ?
NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;
/**
設(shè)置緩存策略,如果加載圖片模式存在并且 = YYWebImageOptionUseNSURLCache,
使用NSURLRequestUseProtocolCachePolicy策略,否則的話使用NSURLRequestReloadIgnoringLocalCacheData
說明:NSURLRequestUseProtocolCachePolicy這個是系統(tǒng)默認(rèn)的緩存策略,緩存不存在,就去重新服務(wù)端拉去,如果存在的話,根據(jù)下一步請求的Cache-control字段來進(jìn)行下一步的操作,比如如果cache-control = must-revalidata,那么還會去詢問服務(wù)端是否有數(shù)據(jù)更新,有的話就拉取新數(shù)據(jù),沒有就返回緩存;
NSURLRequestReloadIgnoringLocalCacheData:忽略本地緩存,每次都去請求服務(wù)端
*/
// 2碍论、根據(jù)request,option,cache,cacheKey,progress,transformblock,completionblock生成一個YYWebImageOperation對象
YYWebImageOperation *operation = [[YYWebImageOperation alloc] initWithRequest:request
options:options
cache:_cache
cacheKey:[self cacheKeyForURL:url]
progress:progress
transform:transform ? transform : _sharedTransformBlock
completion:completion];
// 如果有用戶名跟密碼, operation 的 credential 屬性通過系統(tǒng)提供的 NSURLCredential 類生成
if (_username && _password) {
operation.credential = [NSURLCredential credentialWithUser:_username password:_password persistence:NSURLCredentialPersistenceForSession];
}
// 如果operation初始化成功
if (operation) {
NSOperationQueue *queue = _queue;
// 并且存在一個queue
if (queue) {
[queue addOperation:operation]; // operation 加入到 queue 就會執(zhí)行菲驴;
} else {
[operation start]; // 如果queue不存在,直接開始這個operation
}
}
return operation;
}
/**
設(shè)置請求頭的方法
可以看出在每一步操作的時候都進(jìn)行了判空處理,這對于第三方庫來說尤為重要,因?yàn)椴恢朗褂谜邥趺捶欠ǖ恼{(diào)用你的api
*/
- (NSDictionary *)headersForURL:(NSURL *)url {
if (!url) return nil;
return _headersFilter ? _headersFilter(url, _headers) : _headers;
}
/**
* 生成cackeKey的方法
*
* 如果這個cacheKeyFilterblock存在的話,就把url作為參數(shù)傳入block并且返回這個block,
_cacheKeyFilter這個block的返回值為NSString類型, 反之如果不存在的話直接以url的完整地址作為key
*
* @return cacheKey字符串
*/
- (NSString *)cacheKeyForURL:(NSURL *)url {
if (!url) return nil;
return _cacheKeyFilter ? _cacheKeyFilter(url) : url.absoluteString;
}
// 以下是網(wǎng)絡(luò)狀態(tài)指示器部分的代碼
#pragma mark Network Indicator
+ (_YYWebImageApplicationNetworkIndicatorInfo *)_networkIndicatorInfo {
return objc_getAssociatedObject(self, @selector(_networkIndicatorInfo)); // 運(yùn)行時,關(guān)聯(lián)對象骑冗,使用方法的selector赊瞬,作為key;
}
+ (void)_setNetworkIndicatorInfo:(_YYWebImageApplicationNetworkIndicatorInfo *)info {
objc_setAssociatedObject(self, @selector(_networkIndicatorInfo), info, OBJC_ASSOCIATION_RETAIN);
}
// 設(shè)置網(wǎng)絡(luò)狀態(tài),默認(rèn)1/30秒會加載一次
+ (void)_delaySetActivity:(NSTimer *)timer {
UIApplication *app = _YYSharedApplication();
if (!app) return;
NSNumber *visiable = timer.userInfo;
if (app.networkActivityIndicatorVisible != visiable.boolValue) {
[app setNetworkActivityIndicatorVisible:visiable.boolValue];
}
[timer invalidate];
}
+ (void)_changeNetworkActivityCount:(NSInteger)delta {
if (!_YYSharedApplication()) return;
// 定義block贼涩,在這個block中操作計數(shù)加減
void (^block)() = ^{
_YYWebImageApplicationNetworkIndicatorInfo *info = [self _networkIndicatorInfo];
if (!info) {
info = [_YYWebImageApplicationNetworkIndicatorInfo new];
[self _setNetworkIndicatorInfo:info];
}
NSInteger count = info.count;
count += delta;
info.count = count;
[info.timer invalidate]; // 這里緊緊銷毀計時器,不置nil會不會銷毀失敗?
// 每1/30秒執(zhí)行一次timer,同時把info.count作為參數(shù)傳遞過去,
// 其實(shí)這里有個思考,初始化就調(diào)度這個NSTimer,設(shè)置repeats屬性為YES,不需要每次增加網(wǎng)絡(luò)數(shù)量跟減少活躍數(shù)量的時候都新初始化這個timer,需要發(fā)起的時候調(diào)用setFireDate來執(zhí)行開始與停止定時器工作,豈不是效率更高?
info.timer = [NSTimer timerWithTimeInterval:kNetworkIndicatorDelay target:self selector:@selector(_delaySetActivity:) userInfo:@(info.count > 0) repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:info.timer forMode:NSRunLoopCommonModes];
};
// 保證在主線程中調(diào)用block
if ([NSThread isMainThread]) {
block();
} else {
dispatch_async(dispatch_get_main_queue(), block);
}
}
+ (void)incrementNetworkActivityCount {
[self _changeNetworkActivityCount:1];
}
+ (void)decrementNetworkActivityCount {
[self _changeNetworkActivityCount:-1];
}
+ (NSInteger)currentNetworkActivityCount {
_YYWebImageApplicationNetworkIndicatorInfo *info = [self _networkIndicatorInfo];
return info.count;
}
@end
其實(shí)有個疑問在注釋里面也寫出來了,每次調(diào)用+ (void)incrementNetworkActivityCount
與+ (void)decrementNetworkActivityCount
方法的時候都會新起一個NSTimer
,然后再在合適的時間銷毀,這樣做會不會增加額外的內(nèi)存開銷呢?假如定義一個全局的NSTimer
,這樣只需要通過setFireDate
來控制開啟與關(guān)閉定時器,豈不更好?
附上原作者對上述疑問回復(fù)
總結(jié)
- 代碼規(guī)范. 從注釋,到變量名,方法名,枚舉的定義,可以看到一個好的開源項(xiàng)目其代碼一定是讓人讀起來賞心悅目的.
- 容錯處理.因?yàn)槟悴豢赡苤朗褂谜邥绾畏欠ǖ氖褂媚愕腶pi,所以要盡可能做更多的容錯處理,最常見的情況就是判空的操作.
- 注意線程安全,如在
+ (void)_changeNetworkActivityCount:(NSInteger)delta
中,確保在主線程設(shè)置網(wǎng)絡(luò)指示器的狀態(tài).
PS: