讀碼筆記-YYWebImage源碼 (一) - YYWebImageManager

不得不說,YYKit系列的橫空出世讓很多人對國內的開發者都摘下了有色眼鏡,原來并非大神全是國外的,自己膜拜之余也想讀一下具體的一些實現細節,所以到github上fork了YYWebImage閱讀源碼并把注釋寫在response里面,權當筆記.

第一天,YYWebImageManager
頭文件

#import <UIKit/UIKit.h>

#if __has_include(<YYWebImage/YYWebImage.h>)
#import <YYWebImage/YYImageCache.h>
#else
#import "YYImageCache.h"
#endif

@class YYWebImageOperation;


/// The options to control image operation.
///控制圖片請求的模式
typedef NS_OPTIONS(NSUInteger, YYWebImageOptions) {
    
    /// Show network activity on status bar when download image.
    ///當下載圖片的時候會在狀態欄顯示一個當前網絡狀況
    YYWebImageOptionShowNetworkActivity = 1 << 0,
    
    /// Display progressive/interlaced/baseline image during download (same as web browser).
    ///能夠像瀏覽器一樣顯示一個逐漸顯示的圖片,有三種方式:換換顯示,中間帶交叉效果,基于基線顯示.這里可以看demo理解三種模式的區別
    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這種顯示模式來獲得更好的用戶體驗
    YYWebImageOptionProgressiveBlur = 1 << 2,
    
    /// Use NSURLCache instead of YYImageCache.
    ///使用NSURLCache來代替YYImageCache
    YYWebImageOptionUseNSURLCache = 1 << 3,
    
    /// Allows untrusted SSL ceriticates.
    ///允許未受信任的SSL證書,PS:基于我的理解以及對比SDWebImage,這種模式一般用戶調試過程,不用于生產過程
    YYWebImageOptionAllowInvalidSSLCertificates = 1 << 4,
    
    /// Allows background task to download image when app is in background.
    ///app進入后臺的時候允許后臺下載圖片
    YYWebImageOptionAllowBackgroundTask = 1 << 5,
    
    /// Handles cookies stored in NSHTTPCookieStore.
    ///把cookies存儲進NSHTTPCookieStore
    YYWebImageOptionHandleCookies = 1 << 6,
    
    /// Load the image from remote and refresh the image cache.
    ///從遠程下載圖片并且刷新圖片緩存,這種模式可以用于更換了圖片內容,但是圖片URL不替換
    YYWebImageOptionRefreshImageCache = 1 << 7,
    
    /// Do not load image from/to disk cache.
    ///不從硬盤緩存加載圖片,同時也不會把圖片緩存進磁盤
    YYWebImageOptionIgnoreDiskCache = 1 << 8,
    
    /// Do not change the view's image before set a new URL to it.
    ///當沒有通過一個URL下載到一個新的圖片的時候不去修改圖片
    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圖片轉換為單一frame的圖片,開發中如果需求圖片固定顯示大小,這個模式可能會有用
    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.
    ///設置圖片的時候帶有一個fade的動畫效果
    ///會給view's layer添加一個淡入淡出動畫效果來獲取更好的用戶體驗
    YYWebImageOptionSetImageWithFadeAnimation = 1 << 12,
    
    /// Do not set the image to the view when image fetch complete.
    /// You may set the image manually.
    ///當圖片下載完成之前不去設置它
    ///你可以手動設置圖片
    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加進黑名單當下載失敗的時候,黑名單存儲在內存中,所以這種模式不會嘗試重復下載
    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.
    ///立刻從內存中查找圖片,如果你調用了"setImageWithURL..."并且圖片已經存在于內存,你會從相同的回調里面得到這個值
    YYWebImageFromMemoryCacheFast,
    
    /// Fetched from memory cache. ///從內存中來
    YYWebImageFromMemoryCache,
    
    /// Fetched from disk cache. ///從磁盤中來
    YYWebImageFromDiskCache,
    
    /// Fetched from remote (web or file path).///從遠程下載的,可以是web或者一個路徑
    YYWebImageFromRemote,
};

/// Indicated image fetch complete stage.
///用來告訴我們圖片下載的完成度的
typedef NS_ENUM(NSInteger, YYWebImageStage) {
    
    /// Incomplete, progressive image.///未完成,帶進度的image
    YYWebImageStageProgress  = -1,
    
    /// Cancelled.///已經取消了
    YYWebImageStageCancelled = 0,
    
    /// Finished (succeed or failed).///已經結束,可能是成功或者失敗
    YYWebImageStageFinished  = 1,
};


/**
 The block invoked in remote image fetch progress.
 
 @param receivedSize Current received size in bytes.
 @param expectedSize Expected total size in bytes (-1 means unknown).
 */
///從遠程下載完成過程的回調,參數receivedSize是已經下載的大小,expectedSize是總共大小,因此可以通過receivedSize/expectedSize獲得progress,如果expectedSize = -1代表著不知道一共有多大
typedef void(^YYWebImageProgressBlock)(NSInteger receivedSize, NSInteger expectedSize);

/**
 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.
 */
/**
 圖片從遠程下載完成之前會執行這個block,用來執行一些額外的操作
 @discussion 當'YYWebImageCompletionBlock'這個完成度額回調在下載完成之前會執行這個回調用來給你一個機會做一些額外的處理,比如用來修改圖片尺寸等.如果這里不需要對圖片進行transform處理,只會返回image這一個參數
 @example 你可以裁剪/模糊圖片,或者添加一些邊角通過以下代碼:
 ^(UIImage *image, NSURL *url){
 //可能你需要創建一個 @autoreleasepool來限制內存開銷
 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;
 }
 */
typedef UIImage *(^YYWebImageTransformBlock)(UIImage *image, NSURL *url);

/**
 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.
 */
/**
 這個block會在當圖片下載完成或者取消的時候調用
 
 @param image       The image.
 @param url         圖片url,遠程或者本地路徑
 @param from        圖片從哪來,
 @param error       圖片下載中的錯誤
 @param finished    如果請求取消掉了,返回NO,其他是YES
 */

typedef void (^YYWebImageCompletionBlock)(UIImage *image, NSURL *url, YYWebImageFromType from, YYWebImageStage stage, NSError *error);




/**
 A manager to create and manage web image operation.
 */
/**
 *  用來創建和管理網絡圖片任務的管理器,這個類其實就一個作用,管理生成一個YYWebImageOperation實例
 */
@interface YYWebImageManager : NSObject

/**
 Returns global YYWebImageManager instance.
 
 不需要多解釋,返回單例類
 @return YYWebImageManager shared instance.
 */
+ (instancetype)sharedManager;

/**
 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.
 */
/**
 *  生成一個manager,帶有緩存與操作隊列
 *
 *  @param cache 圖片緩存用到的manager,
 *  @param queue 圖片請求,調度運行的請求隊列
 *
 *  @return 一個新的manager
 */
- (instancetype)initWithCache:(YYImageCache *)cache queue:(NSOperationQueue *)queue NS_DESIGNATED_INITIALIZER;

- (instancetype)init UNAVAILABLE_ATTRIBUTE;
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;

/**
 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.
 */
/**
 *  創建返回一個新的operation,這個operation會立刻開始執行
 *
 *  @param url        圖片url,可以是遠程或者本地路徑
 *  @param options    控制下載的option
 *  @param progress   進度block,會在后臺線程的時候調用,傳空的話會禁用此特性
 *  @param transform  進入后臺線程會調用此block,傳空禁用此block
 *  @param completion 進入后臺線程會調用此block,傳空禁用此block
 *
 *  @return 一個新的圖片operation
 */
- (YYWebImageOperation *)requestImageWithURL:(NSURL *)url
                                     options:(YYWebImageOptions)options
                                    progress:(YYWebImageProgressBlock)progress
                                   transform:(YYWebImageTransformBlock)transform
                                  completion:(YYWebImageCompletionBlock)completion;

/**
 The image cache used by image operation. 
 You can set it to nil to avoid image cache.
 */
/**
 *  圖片請求用到的緩存,可以設置為nil來禁用緩存
 */
@property (nonatomic, strong) YYImageCache *cache;

/**
 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.
 */
/**
 *  圖片的請求調度運行的隊列
    你不通過隊列新建一個新的operation的時候可以給這個值置為nil
    
    你可以用這個隊列來控制請求的最大值最小值,獲得當前操作隊列的狀態值,或者來取消這個manager中所有的operation
 */
@property (nonatomic, strong) NSOperationQueue *queue;

/**
 The shared transform block to process image. Default is nil.
 
 When called `requestImageWithURL:options:progress:transform:completion` and
 the `transform` is nil, this block will be used.
 */
/**
 *  默認值為nil,共享的圖片變換的過程,
    當調用`requestImageWithURL:options:progress:transform:completion`并且`transform`為nil時,這個block才有用
 */
@property (nonatomic, copy) YYWebImageTransformBlock sharedTransformBlock;

/**
 The image request timeout interval in seconds. Default is 15.
 */
/**
 *  請求超時時間,默認15秒
 */
@property (nonatomic, assign) NSTimeInterval timeout;

/**
 The username used by NSURLCredential, default is nil.
 */
/**
 *  NSURLCredential使用的用戶名,默認為nil
 */
@property (nonatomic, strong) NSString *username;

/**
 The password used by NSURLCredential, default is nil.
 */
/**
 *  同上,密碼,默認為nil
 */
@property (nonatomic, strong) NSString *password;

/**
 The image HTTP request header. Default is "Accept:image/webp,image/\*;q=0.8".
 */
/**
 *  圖片TTTP的請求頭,默認是"Accept:image/webp,image/\*;q=0.8"
 */
@property (nonatomic, copy) NSDictionary *headers;

/**
 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.
 */
/**
 *  每個圖片http請求做額外的HTTP header操作的時候會調用這個block,默認為nil
 */
@property (nonatomic, copy) NSDictionary *(^headersFilter)(NSURL *url, NSDictionary *header);

/**
 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.
 */
/**
 *  每個圖片的操作都會調用這個block,默認為nil
    使用這個block能夠給URL提供一個自定義的的圖片
 */
@property (nonatomic, copy) NSString *(^cacheKeyFilter)(NSURL *url);

/**
 Returns the HTTP headers for a specified URL.
 
 @param url A specified URL.
 @return HTTP headers.
 */
/**
 *  返回URL的HTTP headers
 *
 *  @param url 當前URL
 *
 *  @return http header
 */
- (NSDictionary *)headersForURL:(NSURL *)url;

/**
 Returns the cache key for a specified URL.
 
 @param url A specified URL
 @return Cache key used in YYImageCache.
 */
/**
 *  給URL返回一個cacheKey
 *
 *  @param url 該URL
 *
 *  @return cache key在YYImageCache中有用到
 */
- (NSString *)cacheKeyForURL:(NSURL *)url;


/**
 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.
 */
/**
 *  增加活躍的網絡請求數量
    如果在增加前數量為0,那么會在狀態來開始有一個網絡菊花動畫
    該方法是線程安全的
    該方法不會對APP擴展產生影響
 */
+ (void)incrementNetworkActivityCount;

/**
 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.
 */
/**
 *  與上面對應,減少活躍的網絡請求數量,如果執行完畢之后數量變為0,那么會停止在狀態欄的網絡指示器動畫
    線程安全
    不會影響APP擴展
 */
+ (void)decrementNetworkActivityCount;

/**
 Get current number of active network requests.
 
 This method is thread safe.
 
 This method has no effect in App Extension.
 */
/**
 *  獲取當前活躍的網絡請求數量
 *  線程安全
    不會影響APP擴展
 *  @return
 */
+ (NSInteger)currentNetworkActivityCount;

@end

實現文件


#import "YYWebImageManager.h"
#import "YYImageCache.h"
#import "YYWebImageOperation.h"
#import <objc/runtime.h>

#define kNetworkIndicatorDelay (1/30.0)


/// 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
 *
 *  @return <#return value description#>
 */
+ (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;
        }
        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];
}

/**
 *  構造方法,本單例類生成的時候會調用這個方法,傳入兩個參數,一個緩存,一個隊列
 *
 *
 *  @return <#return value description#>
 */
- (instancetype)initWithCache:(YYImageCache *)cache queue:(NSOperationQueue *)queue{
    self = [super init];
    if (!self) return nil;
    //這里很好的遵循了蘋果規范,初始化的時候先調用父類,同時初始化了_cache,_queue,_timeout,_header這些屬性
    _cache = cache;
    _queue = queue;
    _timeout = 15.0;
    _headers = @{ @"Accept" : @"image/webp,image/*;q=0.8" };
    return self;
}

/**
 *  這里就是具體的下載請求方法了
 *
 *  @param url        圖片URL
 *  @param options    加載模式
 *  @param progress   進度
 *  @param transform  下載完成前對圖片進行操作形變的block
 *  @param completion 下載完成的block
 *
 *  @return 一個YYWebImageOperation對象
 */
- (YYWebImageOperation *)requestImageWithURL:(NSURL *)url
                                     options:(YYWebImageOptions)options
                                    progress:(YYWebImageProgressBlock)progress
                                   transform:(YYWebImageTransformBlock)transform
                                  completion:(YYWebImageCompletionBlock)completion {
    
    //1.先生成一個request,并且根據傳入參數生成request參數
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.timeoutInterval = _timeout;
    request.HTTPShouldHandleCookies = (options & YYWebImageOptionHandleCookies) != 0;
    //設置請求頭
    request.allHTTPHeaderFields = [self headersForURL:url];
    request.HTTPShouldUsePipelining = YES;
    //設置緩存策略,如果加載圖片模式存在并且 =YYWebImageOptionUseNSURLCache,使用NSURLRequestUseProtocolCachePolicy策略,否則的話使用NSURLRequestReloadIgnoringLocalCacheData
    //說明:NSURLRequestUseProtocolCachePolicy這個是系統默認的緩存策略,緩存不存在,就去重新服務端拉去,如果存在的話,根據下一步請求的Cache-control字段來進行下一步的操作,比如如果cache-control = must-revalidata,那么還會去詢問服務端是否有數據更新,有的話就拉取新數據,沒有就返回緩存
    //    NSURLRequestReloadIgnoringLocalCacheData:忽略本地緩存,每次都去請求服務端
    request.cachePolicy = (options & YYWebImageOptionUseNSURLCache) ?
        NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;
    
    //2.根據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屬性通過系統提供的NSURLCredential類生成
    if (_username && _password) {
        operation.credential = [NSURLCredential credentialWithUser:_username password:_password persistence:NSURLCredentialPersistenceForSession];
    }
    //如果operation初始化成功
    if (operation) {
        NSOperationQueue *queue = _queue;
        //并且存在一個queue
        if (queue) {
            //就把生成的operation添加到隊列中
            [queue addOperation:operation];
        } else {
        //如果queue不存在,直接開始這個operation
            [operation start];
        }
    }
    return operation;
}

/**
 *  設置請求頭的方法
    可以看出在每一步操作的時候都進行了判空處理,這對于第三方庫來說尤為重要,因為不知道使用者會怎么非法的調用你的api
 *
 *  @param url <#url description#>
 *
 *  @return <#return value description#>
 */
- (NSDictionary *)headersForURL:(NSURL *)url {
    if (!url) return nil;
    //如果有_headersFilter這個blcok就把url跟_headers傳遞回去并且返回這個block,沒有的返回_headers
    return _headersFilter ? _headersFilter(url, _headers) : _headers;
}

/**
 *  生成cackeKey的方法
 *
 *  如果這個cacheKeyFilterblock存在的話,就把url作為參數傳入block并且返回這個block,_cacheKeyFilter這個block的返回值為NSString類型,
    反之如果不存在的話直接以url的完整地址作為key
 *
 *  @return cacheKey字符串
 */
- (NSString *)cacheKeyForURL:(NSURL *)url {
    if (!url) return nil;
    return _cacheKeyFilter ? _cacheKeyFilter(url) : url.absoluteString;
}


/**
 *  以下是網絡狀態指示器部分的代碼
 */
#pragma mark Network Indicator

+ (_YYWebImageApplicationNetworkIndicatorInfo *)_networkIndicatorInfo {
    return objc_getAssociatedObject(self, @selector(_networkIndicatorInfo));
}

+ (void)_setNetworkIndicatorInfo:(_YYWebImageApplicationNetworkIndicatorInfo *)info {
    objc_setAssociatedObject(self, @selector(_networkIndicatorInfo), info, OBJC_ASSOCIATION_RETAIN);
}

/**
 *  設置網絡狀態,默認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];
}

/**
 *  修改數量
 *
 *  @param delta <#delta description#>
 */
+ (void)_changeNetworkActivityCount:(NSInteger)delta {
    if (!_YYSharedApplication()) return;
    
    //定義一個無參無反block,在這個block中操作計數加減
    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秒執行一次timer,同時把info.count作為參數傳遞過去,
        //其實這里有個思考,初始化就調度這個NSTimer,設置repeats屬性為YES,不需要每次增加網絡數量跟減少活躍數量的時候都新初始化這個timer,需要發起的時候調用setFireDate來執行開始與停止定時器工作,豈不是效率更高?
        info.timer = [NSTimer timerWithTimeInterval:kNetworkIndicatorDelay target:self selector:@selector(_delaySetActivity:) userInfo:@(info.count > 0) repeats:NO];
        [[NSRunLoop mainRunLoop] addTimer:info.timer forMode:NSRunLoopCommonModes];
    };
    //如果在主線程,調用block
    if ([NSThread isMainThread]) {
        block();
    } else {
        //如果不在,獲取主線程調用block
        dispatch_async(dispatch_get_main_queue(), block);
    }
}

/**
 *  增加活躍網絡數量
 */
+ (void)incrementNetworkActivityCount {
    [self _changeNetworkActivityCount:1];
}
/**
 *  減少
 */
+ (void)decrementNetworkActivityCount {
    [self _changeNetworkActivityCount:-1];
}
/**
 *  獲取當前的活躍請求數量
 *
 *  @return <#return value description#>
 */
+ (NSInteger)currentNetworkActivityCount {
    _YYWebImageApplicationNetworkIndicatorInfo *info = [self _networkIndicatorInfo];
    return info.count;
}

@end

其實有個疑問在注釋里面也寫出來了,每次調用+ (void)incrementNetworkActivityCount+ (void)decrementNetworkActivityCount方法的時候都會新起一個NSTimer,然后再在合適的時間銷毀,這樣做會不會增加額外的內存開銷呢?假如定義一個全局的NSTimer,這樣只需要通過setFireDate來控制開啟與關閉定時器,豈不更好?
附上原作者對上述疑問回復

總結

  1. 代碼規范. 從注釋,到變量名,方法名,枚舉的定義,可以看到一個好的開源項目其代碼一定是讓人讀起來賞心悅目的.
  2. 容錯處理.因為你不可能知道使用者會如何非法的使用你的api,所以要盡可能做更多的容錯處理,最常見的情況就是判空的操作.
  3. 注意線程安全,如在+ (void)_changeNetworkActivityCount:(NSInteger)delta中,確保在主線程設置網絡指示器的狀態.

PS:
YYWebImage源碼地址
我fork下來添加注釋的版本github地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,011評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,263評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,543評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,323評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,874評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,095評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,605評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,551評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,720評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,961評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,358評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,612評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,330評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,690評論 2 370

推薦閱讀更多精彩內容

  • 轉載鏈接:http://www.lxweimin.com/p/5a5bea9180e7 不得不說,YYKit系列的橫...
    kakukeme閱讀 1,801評論 0 51
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,579評論 25 707
  • 也許每一個男子全都有過這樣的兩個女人,至少兩個。娶了紅玫瑰,久而久之,紅的變了墻上的一抹蚊子血,白的還是"床前明月...
    白琉璃鹿閱讀 165評論 0 0
  • 心理學學習這條路,沒意思了。不過是隔離身邊的不爽情緒。還要隔離多久?生命被無質量的浪費十年,不甘心的勁兒也沒有了氣...
    東驪閱讀 300評論 0 0
  • 從幾年前來到深圳之后就一直在深圳沒有離開過,也很少出差。之前在一個三四線城市一下到了深圳這樣的一線大城市,的確有種...
    霜天曉角S閱讀 1,589評論 3 8