读码笔记-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阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容

  • 转载链接:http://www.jianshu.com/p/5a5bea9180e7 不得不说,YYKit系列的横...
    kakukeme阅读 1,720评论 0 51
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 也许每一个男子全都有过这样的两个女人,至少两个。娶了红玫瑰,久而久之,红的变了墙上的一抹蚊子血,白的还是"床前明月...
    白琉璃鹿阅读 137评论 0 0
  • 心理学学习这条路,没意思了。不过是隔离身边的不爽情绪。还要隔离多久?生命被无质量的浪费十年,不甘心的劲儿也没有了气...
    东骊阅读 279评论 0 0
  • 从几年前来到深圳之后就一直在深圳没有离开过,也很少出差。之前在一个三四线城市一下到了深圳这样的一线大城市,的确有种...
    霜天晓角S阅读 1,526评论 3 8