第一篇
前言
從今天開始,我將開啟一段源碼解讀的旅途了。在這里先暫時不透露具體解讀的源碼到底是哪些?因為也可能隨着解讀的進行會更改計划。但能夠肯定的是,這一系列之中肯定會有Swift版本的代碼。
說說我的目的。想成為一名好的程序員,肯定繞不開模仿這條路。之所以做出這么一個決定,也是想提高自己寫框架的能力,邏輯思維能力,擴展知識面。同時也能夠給那些想了解這些框架的同學一些基本的解釋。何樂而不為呢?
解讀方法
對於框架的源碼解讀,我個人的習慣是先看頭文件,先從頭文件最簡單的開始解讀,也就是說首先看不依賴外部文件的類。
這就說明了一個問題,我們在寫代碼的時候,import頭文件也是需要經過慎重考慮的。應該導入什么頭文件應該讓別的開發者很明白該類具體依賴那些外部文件。
NSData (ImageContentType)
很明顯這是一個NSData的分類,我們都知道分類一般是用來擴展一些方法的。我們看看它擴展了什么方法?
/**
* Compute the content type for an image data
*
* @param data the input data
*
* @return the content type as string (i.e. image/jpeg, image/gif)
*/
+ (NSString *)sd_contentTypeForImageData:(NSData *)data;
好,看這個方法,就應該明白了,目的是根據二進制的數據獲取圖片的contentType。因此,我們在這里就要插入一段contentType的介紹了。
文件頭
文件頭是位於文件開頭的一段承擔一定任務的數據,一般都在開頭的部分。
別看這個文件頭和C語言中的頭文件讀起來很像,但這兩個東西其實根本沒有一點關系:頭文件是一種包含功能函數,數據接口聲明的載體文件;而文件頭則是直接位於文件中的一段數據,是文件的一部分
說的簡單一點就是,當文件都使用二進制流作為傳輸時,需要制定一套規范,用來區分該文件到底是什么類型的。 文件頭有很多個,我們在這里就介紹一些主流的且跟圖片相關的文件頭。
- JPEG (jpg),文件頭:FFD8FFE1
- PNG (png),文件頭:89504E47
- GIF (gif),文件頭:47494638
- TIFF tif;tiff 0x49492A00
- TIFF tif;tiff 0x4D4D002A
- RAR Archive (rar),文件頭:52617221
- WebP : 524946462A73010057454250
可以看出來我們通過每個文件頭的第一個字節就能判斷出是什么類型。但是值得注意的是52開頭的。這個要做特別的判斷。
WebP這種格式很特別。是由12個字節組成的文件頭,我們如果把這些字節通過ASCII編碼后會得到下邊這樣一張表格:
+ (NSString *)sd_contentTypeForImageData:(NSData *)data {
uint8_t c;
[data getBytes:&c length:1];
switch (c) {
case 0xFF:
return @"image/jpeg";
case 0x89:
return @"image/png";
case 0x47:
return @"image/gif";
case 0x49:
case 0x4D:
return @"image/tiff";
case 0x52:
// R as RIFF for WEBP
if ([data length] < 12) {
return nil;
}
NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
return @"image/webp";
}
return nil;
}
return nil;
}
現在再看上邊方法的實現,我們就明白了。其中jpeg/png/gif/tiff 是最好判斷的。當第一個字節為52時,如果長度<12 我們就認定為不是圖片。因此返回nil。我們通過數據截取后獲得testString
,如果testString
頭部包含RIFF且尾部也包含WEBP,那么就認定該圖片格式為webp。
演示
為了更好的演示webp,我們用代碼打印出結果來看看。
首先我們獲得一個webp圖片:google@2x.webp
NSString *path = [[NSBundle mainBundle] pathForResource:@"google@2x" ofType:@"webp"];
NSData *data = [NSData dataWithContentsOfFile:path];
NSString *contentType = [NSData sd_contentTypeForImageData:data];
NSLog(@"-----%@------%@", contentType, data);
我們看下打印結果:
-----image/webp------<52494646 72370000 57454250 56503858 0a000000 10000000 190200bd 0000414c 50484711 00000187 40906d33 83cd9ff7 778888a4 b3e53068 db4650c2 9f747a77 ff1822fa 9f927ff8 9224460e c3301c46 86216224 932431e6 1f5edfb6 6dd76d6c db56c195 20884bfb ffaf959d c214d01a 39e6f521 a2ffe95b b6edbaad 6ddb2a20 c03b40e4 ffffdade fbb0500b 2ddb2146 8c8788fe a7feff26 7ecae76a 9f2e77a3 fd9ce513 3ffc8fdd a7d286ec b65aa2d3 b3f6c735 44ef3ad6 ac879cce 21fafd58 929e6eac 53ccce25 e9c33bb8 d779b52f 438ccf59 9fdcc1bd ee7871ac f284be4f fad40eee 75c77b63 93b5d1ca 27de1de5 6c4376cf a28fece0 5e77bc36 3659b94a 64311e75 c8562dfa bc0eee75 c75b4393 efb32636 c3d165a7 167d5807 f7bae3a5 aecaf72b a1e28f21 1bb5e893 3ab8d71d 2f5da67c 9ba7472f 36d93892 3ea6a58e 30a6e39d 53e56b71
其中52494646 72370000 57454250
正好占用了12字節。那么也正好符合了我們上邊解釋的webp文件頭。
NSData (ImageContentTypeDeprecated)
這個分類有一個東西值得我們學習。
+ (NSString *)contentTypeForImageData:(NSData *)data __deprecated_msg("Use `sd_contentTypeForImageData:`");
這個__deprecated_msg
可以告訴開發者該方法不建議使用。這就有使用場景了。當我們在寫框架或者類的時候,如果功能相同,但是想使用心得方法名的時候,使用__deprecated_msg
給予其他開發者一個提示。這遠遠比我們直接刪除舊的更專業。
總結
這是SDWebImage源碼解讀的第一篇,我們解讀的目的是發現代碼中我們不知道的細節和知識。盡可能的想象出使用場景。在解讀完之后,需要做一個所有知識點的總結。我們應該盡量去揣摩作者駕馭代碼的思想。也許當我們抓住這些知識點並使用時,我們寫出的代碼跟別人就有了不同的感覺。