最近回頭一看猿规,發(fā)現(xiàn)我們的項目現(xiàn)在對圖片處理都是用YYWebImage 的處理方式方式的,用了不短時間了买喧,卻沒有好好了解下,今天特此學(xué)習(xí)下箩朴。首先然而怎么下手呢岗喉?如何提高閱讀源代碼的能力?結(jié)合自己炸庞,決定在第一篇钱床,帶著一個問題,去簡單了解埠居。
問題:為什么使用下面這個方法去獲取圖片查牌?
- (void)yy_setImageWithURL:(NSURL *)imageURL
placeholder:(UIImage *)placeholder
options:(YYWebImageOptions)options
progress:(YYWebImageProgressBlock)progress
transform:(YYWebImageTransformBlock)transform
completion:(YYWebImageCompletionBlock)completion
先簡單了解下這幾個參數(shù)的含義
* imageURL:圖片的URL
* placeholder: 備用圖片
* options: YYWebImageOptions(圖片下載時同時的操作,可以仔細看一下,很強大的
(展示進度滥壕,緩存方式纸颜,HTPPS的處理,忽略其他绎橘,失敗時處理等等))
* progress: 圖片下載的進度(receivedSize/expectedSize)
* transform: 對圖片是否需要處理(大小胁孙、圓角之類的添加),為更適應(yīng)圖片的展示
* completion:完成后称鳞,可以了解的信息(url,from,error)
然后進入去看一下詳細的實現(xiàn)
1涮较、圍繞著_YYWebImageSetter進行一系列的判斷。
2冈止、異步中到 YYWebImageManager 的網(wǎng)絡(luò)處理
3狂票、同時對 operation 進行處理,在YYWebImageOperation(NSOperation的子類)中熙暴。
所以闺属,得先對:YYWebImageManager慌盯、YYWebImageOperation 有個大致了解。
YYWebImageManager
用來創(chuàng)建和管理網(wǎng)絡(luò)圖片任務(wù)的管理器,這個類其實就一個作用,管理生成一個YYWebImageOperation實例
第一掂器,我們從其三個枚舉就可以大致了解它一些東西啦
* YYWebImageOptions //控制圖片請求的模式
* YYWebImageFromType //用來告訴我們圖片來源
* YYWebImageStage //用來告訴我們圖片下載的完成度的
第二亚皂,再看看其幾個 Block
* YYWebImageProgressBlock //從遠程下載完成過程的回調(diào)
* YYWebImageTransformBlock // 圖片從遠程下載完成之前會執(zhí)行這個block,用來執(zhí)行一些額外的操作
* YYWebImageCompletionBlock //在當(dāng)圖片下載完成或者取消的時候調(diào)用
第三,看看它的初始化和屬性唉匾,有很多孕讳,選擇一個最重要的。也就是將上述 枚舉和 block 結(jié)合的體現(xiàn)巍膘。
- (YYWebImageOperation *)requestImageWithURL:(NSURL *)url
options:(YYWebImageOptions)options
progress:(YYWebImageProgressBlock)progress
transform:(YYWebImageTransformBlock)transform
completion:(YYWebImageCompletionBlock)completion;
第四,看這個初始化的實現(xiàn)
- (YYWebImageOperation *)requestImageWithURL:(NSURL *)url
options:(YYWebImageOptions)options
progress:(YYWebImageProgressBlock)progress
transform:(YYWebImageTransformBlock)transform
completion:(YYWebImageCompletionBlock)completion {
//設(shè)置request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.timeoutInterval = _timeout;
request.HTTPShouldHandleCookies = (options & YYWebImageOptionHandleCookies) != 0;
request.allHTTPHeaderFields = [self headersForURL:url];
request.HTTPShouldUsePipelining = YES;
// 設(shè)置緩存方式
request.cachePolicy = (options & YYWebImageOptionUseNSURLCache) ?
NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;
// 生成一個我們需要的YYWebImageOperation對象
YYWebImageOperation *operation = [[YYWebImageOperation alloc] initWithRequest:request
options:options
cache:_cache
cacheKey:[self cacheKeyForURL:url]
progress:progress
transform:transform ? transform : _sharedTransformBlock
completion:completion];
// 如果有用戶名跟密碼,生成系統(tǒng)提供的NSURLCredential
if (_username && _password) {
operation.credential = [NSURLCredential credentialWithUser:_username password:_password persistence:NSURLCredentialPersistenceForSession];
}
// 真正開始操作
if (operation) {
NSOperationQueue *queue = _queue;
if (queue) {
[queue addOperation:operation];
} else {
[operation start];
}
}
return operation;
}
注意 request 各個屬性的設(shè)置芋簿,特別是緩存策略峡懈,后期可以再了解 YYCache,以及NSURLCredential的身份認(rèn)證与斤。
YYWebImageOperation
YYWebImageOperation 類是NSOperation的子類,用來通過請求獲取圖片肪康。
第一,對operation中一些狀態(tài)進行正確的處理
- (void)_finish
- (void)_startOperation
- (void)_startRequest:(id)object
- (void)_cancelOperation
第二撩穿,通過不同的方式下載得到圖片進行的處理
//從磁盤緩存中接受圖片
- (void)_didReceiveImageFromDiskCache:(UIImage *)image
// 從網(wǎng)絡(luò)下載的圖片
- (void)_didReceiveImageFromWeb:(UIImage *)image
第三磷支,看NSURLColleciton 的代理方法
//即將緩存請求結(jié)果
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
//請求已經(jīng)收到相應(yīng)
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
//收到數(shù)據(jù)回調(diào)
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
//連接已經(jīng)結(jié)束加載
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
第四,真正重寫的NSOperation的方法食寡,進行處理
- (void)start
- (void)cancel
在此我們可以大致了解作者在對管理下載隊列的時候的想法,以及自定義一個operation時的大致想法雾狈。
總的說來,這一篇只是大致的思路抵皱,要去看詳細源碼善榛,注意里面一些細節(jié)的才是重點,第二篇筆記重點來挖掘細節(jié)呻畸。
額外問題的發(fā)現(xiàn)
1移盆、static 內(nèi)聯(lián)函數(shù)的使用 ?
static inline void _yy_dispatch_sync_on_main_queue(void (^block)()) {
if (pthread_main_np()) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
}
簡單理解伤为, 宏一樣的函數(shù)咒循,方便使用。深入理解C語言的define和內(nèi)聯(lián)函數(shù)
另外也可了解下iOS OC內(nèi)聯(lián)函數(shù) inline绞愚。
- static 標(biāo)識此內(nèi)聯(lián)聯(lián)函數(shù)只能在本文件中使用叙甸,限制了內(nèi)聯(lián)函數(shù)的作用域。
- 相對于宏來說爽醋,static inline具有和宏同樣級別的開銷蚁署,而且還提供了類型安全,沒有長度和格式的具體限制蚂四。
Ps: static 函數(shù)的用法
static void URLBlacklistInit() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
URLBlacklist = [NSMutableSet new];
URLBlacklistLock = dispatch_semaphore_create(1);
});
}
用 static 這樣表示該函數(shù)聲明為內(nèi)部函數(shù)(又叫靜態(tài)函數(shù))光戈,這樣該函數(shù)就只能在其定義所在的文件中使用哪痰。如果在不同的文件中有同名的內(nèi)部函數(shù),則互不干擾久妆。
2晌杰、類名和函數(shù)名 前面加下劃線的運用 ?
@interface _YYWebImageSetter : NSObject
+ (void)_delaySetActivity:(NSTimer *)timer
- (void)_startOperation
一般是我們是用前綴避免命名空間沖突,如 yy_setImageURL:
之類的筷弦,但是加上下劃線有時個什么情況呢肋演?
而且蘋果公司喜歡單用一個下劃線作為私有的前綴。此時如果我們也這樣想烂琴,假如蘋果公司提供的某個類中繼承了某一個子類爹殊,那我們在子類里可能會無意間覆寫了父類的同名方法,因此官方推薦是不要但寫一個下劃線做私有方法的前綴奸绷。
作者為什么這么寫呢梗夸,對于部分變量名,類名還好理解号醉,但是對私有方法的處理反症,可以仔細看看。
3畔派、objc_getAssociatedObject大量的使用
這個就是為了解決在分類中添加實例變量的快速方法啦铅碍。Objective-C Associated Objects 的實現(xiàn)原理
4、@implementation 后直接使用成員變量?
@implementation User : NSObject {
NSString *_name;
}
以前沒有這樣使用過线椰,但是是合法的哦胞谈。對比@interface和@implementation
5、自旋鎖 & 遞歸鎖
OSSpinLock(自旋鎖),自旋鎖在保證了多線程同時訪問本類的時候不會導(dǎo)致數(shù)據(jù)出錯的同時性能高效士嚎。
NSRecursiveLock(遞歸鎖),遞歸鎖防止死鎖,因為請求可能是有多個的呜魄。
使用案例
static OSSpinLock URLBlacklistLock;//黑名單鎖
/**
* 把url添加進黑名單
*/
static void URLInBlackListAdd(NSURL *url) {
if (!url || url == (id)[NSNull null]) return;
URLBlacklistInit();
OSSpinLockLock(&URLBlacklistLock);
[URLBlacklist addObject:url];
OSSpinLockUnlock(&URLBlacklistLock);
}
@property (nonatomic, strong) NSRecursiveLock *lock; //遞歸鎖
- (void)dealloc {
[_lock lock];
//
[_lock unlock];
}
了解更多的鎖的信息,可以看看這篇結(jié)合 thread 講的 -----Thread基礎(chǔ)知識