不得不說,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
來控制開啟與關閉定時器,豈不更好?
附上原作者對上述疑問回復

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