1. 程式人生 > >SDWebImage源碼閱讀(三)UIImage+GIF

SDWebImage源碼閱讀(三)UIImage+GIF

imp 邏輯 use read see ren ase extern class

  UIImage+GIF 是UIImage 類的一個GIF 分類,在之前的版本裏面這個分類是用了處理GIF 動態圖片的但是會有內存暴增的bug。在當前 ‘4.0.0-beta2‘ 的版本裏GIF 動態圖片處理放在了UIImage+MultiFormat 這個分類裏面,而當前這個GIF 的分類的功能只是將動態圖片作為靜態圖片來處理,如果是靜態圖片的NSData 數據則轉化為靜態UIImage 直接返回,如果是動態圖片的NSData 數據,則把圖像的第1幀圖像轉換化為靜態UIImage 返回。

  首先看UIImage+GIF.h 文件:

 1 @interface UIImage (GIF)
2 3 /** 4 * Compatibility method - creates an animated UIImage from an NSData, it will only contain the 1st frame image 5 */ 6 + (UIImage *)sd_animatedGIFWithData:(NSData *)data; 7 8 /** 9 * Checks if an UIImage instance is a GIF. Will use the `images` array 10 */ 11 - (BOOL)isGIF; 12 13
@end

  定義了一個實例方法和一個類方法:

1 + (UIImage *)sd_animatedGIFWithData:(NSData *)data;

  兼容的方法創建一個動畫UIImage 從一個NSData,它僅僅只包含第一幀圖像。

  判斷一個UIImage 實例是否是GIF 圖片:

1 - (BOOL)isGIF;
1 - (BOOL)isGIF {
2     return (self.images != nil);
3 }

  檢查一個UIImage 的實例是否是GIF,將使用“images” 數組判斷。

  UIImage+GIF.m 文件:

  主要研究上面類方法的實現。

  學習研究之前先做一些拓展:

  size_t 類型:

  size_t 類型定義在cstddef 頭文件中,該文件是C標準庫的頭文件stddef.h 的C++ 版。它是一個與機器相關的unsigned類型,其大小足以保證存儲內存中對象的大小。

  例如:bitset 的size 操作返回bitset對象中二進制位中的個數,返回值類型是size_t。

  例如:在用下標訪問元素時,vector使用vector:size_type 作為下標類型,而數組下標的正確類型則是size_t。vector 使用的下標實際也是size_t,源碼是: typedef size_t size_type。   來源:

  size_t 是標準C庫中定義的,應為unsigned int,在64位系統中為 long unsigned int。

  使用:

  一個基本的無符號整數的 C/C ++ 類型,它是sizeof 操作符返回的結果類型,該類型的大小可選擇。因此,它可以存儲在理論上是可能的任何類型的數組的最大大小。換句話說,一個指針可以被安全地放進size_t 類型(一個例外是類的函數指針,但是這是一個特殊的情況)。   size_t 類型通常用於循環、數組索引、大小的存儲和地址運算。 雖然size_t 可以存儲一個指針,它的目的是更好地使用另一個unsinged 整數類型uintptr_t 形式。在某些情況下,使用size_t 類型是更為有效,比習慣性使用無符號類型的程序會更安全。   size_t是在基於無符號整數memsize 類型的 C/C++ 的標準庫中定義的。C語言中,此類型位於頭文件stddef.h 中,而在C++ 中,則位於cstddef.h 中。

  實現方式:

  在C++ 中,設計 size_t 就是為了適應多個平臺的。size_t 的引入增強了程序在不同平臺上的可移植性。

  size_t 是針對系統定制的一種數據類型,一般是整型,因為 C/C++ 標準只定義最低的位數,而不是必需的固定位數。而且在內存裏,對數的高位對齊存儲還是低位對齊存儲各系統都不一樣。為了提高代碼的可移植性,就有必要定義這樣的數據類型。一般這種類型都會定義到它具體占幾位內存等。當然,有些是編譯器或系統已經給定義好的。

  經測試發現,在32位系統中size_t是4字節的,而在64位系統中,size_t 是8字節的,這樣利用該類型可以增強程序的可移植性。

  詳細解釋:

  size_t 在C語言中就有了。   它是一種“整型”類型,裏面保存的是一個整數,就像int, long那樣。這種整數用來記錄一個大小(size),size_t 的全稱應該是size type,就是說“一種用來記錄大小的數據類型”。   用sizeof(xxx) 操作可獲取 xxx 占用的內存空間字節數,sizeof 返回的必定是無符號整形,在標準c中通過 typedef 將返回值類型定義為size_t。strlen 獲取字符數組實際使用的字節數,不包含數組結尾符’\0’,返回類型size_t。   若用printf 輸出size_t 類型時,C99 中定義格式符 %zd,若編譯器不支持可以嘗試%u 或%lu 。   因為size_t類型的數據其實是保存了一個整數,所以它也可以做加減乘除,也可以轉化為int並賦值給int類型的變量。   類似的還有wchar_t, ptrdiff_t。wchar_t就是wide char type,“一種用來記錄一個寬字符的數據類型”。ptrdiff_t就是pointer difference type,“一種用來記錄兩個指針之間的距離的數據類型”。   通常,size_t 和ptrdiff_t 都是用typedef 來實現的。你可能在某個頭文件裏面找到類似的語句: typedef unsigned int size_t; 而wchar_t則稍有不同。在一些舊的編譯器中,wchar_t也可能是用typedef來實現,但是新的標準中wchar_t已經是 C/C++ 語言的關鍵字,wchar_t 類型的地位已經和char, int的地位等同了。   在標準C/C++的語法中,只有int float char bool等基本的數據類型,至於size_t 或size_type 都是以後的編程人員為了方便記憶所定義的一些便於理解的由基本數據類型的變體類型。   例如:typedef int size_t; 定義了size_t為整型。

  在編譯的過程中size_t類型的a 值會被編譯他的補碼。所以在使用size_t 類型數據的過程中尤其要註意,特別是在邏輯表達式中使用到該類型,稍不註意可能帶來很嚴重的後果。
  註:正數的補碼:與原碼相同;負數的補碼:符號位為1,其余位為該數絕對值的原碼按位取反,然後整個數加1。

參考鏈接:http://blog.csdn.net/JIEJINQUANIL/article/details/50981834

http://jeremybai.github.io/blog/2014/09/10/size-t

http://baike.baidu.com/link?url=WhKafqypjk2GpAfTxbeWLNEQIqW3bjODmV5faNTtZUcPPCn1Y7MDOa05UR4DOstvaVcbGeOXqUcKCRJ6eIz4ba

http://blog.csdn.net/zhanghaotian2011/article/details/7974891

http://www.cnblogs.com/kaituorensheng/p/3239446.html

http://blog.csdn.net/bigloomy/article/details/6563870

  下面看 + (UIImage *)sd_animatedGIFWithData:(NSData *)data; 方法實現:

 1 + (UIImage *)sd_animatedGIFWithData:(NSData *)data {
 2     if (!data) {
 3         return nil;
 4     }
 5 
 6     CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
 7 
 8     size_t count = CGImageSourceGetCount(source);
 9 
10     UIImage *staticImage;
11 
12     if (count <= 1) {
13         staticImage = [[UIImage alloc] initWithData:data];
14     } else {
15         // we will only retrieve the 1st frame. the full GIF support is available via the FLAnimatedImageView category.
16         // this here is only code to allow drawing animated images as static ones
17 #if SD_WATCH
18         CGFloat scale = 1;
19         scale = [WKInterfaceDevice currentDevice].screenScale;
20 #elif SD_UIKIT
21         CGFloat scale = 1;
22         scale = [UIScreen mainScreen].scale;
23 #endif
24         
25         CGImageRef CGImage = CGImageSourceCreateImageAtIndex(source, 0, NULL);
26 #if SD_UIKIT || SD_WATCH
27         UIImage *frameImage = [UIImage imageWithCGImage:CGImage scale:scale orientation:UIImageOrientationUp];
28         staticImage = [UIImage animatedImageWithImages:@[frameImage] duration:0.0f];
29 #elif SD_MAC
30         staticImage = [[UIImage alloc] initWithCGImage:CGImage size:NSZeroSize];
31 #endif
32         CGImageRelease(CGImage);
33     }
34 
35     CFRelease(source);
36 
37     return staticImage;
38 }

  這裏主要使用了 ImageIO 框架下的 <ImageIO/CGImageSource.h>,並在 UIImage+GIF.m 文件開始通過 #import <ImageIO/ImageIO.h>,引入ImageIO 框架。

  1.判斷傳入的data 如果是nil 則直接返回nil。

  2.使用CGImageSource.h 的:CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL),創建一個CGImageSourceRef 對象source。

1 /* Create an image source reading from `data‘.  The `options‘ dictionary
2  * may be used to request additional creation options; see the list of keys
3  * above for more information. */
4 
5 IMAGEIO_EXTERN CGImageSourceRef __nullable CGImageSourceCreateWithData(CFDataRef __nonnull data, CFDictionaryRef __nullable options) IMAGEIO_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_4_0);

  3.使用CGImageSource.h 的:CGImageSourceGetCount(source),獲得source 裏面的圖片數量count。

1 /* Return the number of images (not including thumbnails) in the image
2  * source `isrc‘. */
3 
4 IMAGEIO_EXTERN size_t CGImageSourceGetCount(CGImageSourceRef __nonnull isrc)  IMAGEIO_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_4_0);

  4.創建一個UIImage 實例staticImage。(從名字裏面能大概猜出創建該實例的用意)

  5.如果count 小於等於1,即表示該data 數據是一個靜態圖片的NSData 數據,則把data 轉化為UIImage 並賦值給staticImage。

  6.如果count 大於1,即表示該data 數據是一個動態圖片的NSData 數據。如果當前是WATCH 平臺開發通過:

1         CGFloat scale = 1;
2         scale = [WKInterfaceDevice currentDevice].screenScale;

獲取當前設備的screenScale,如果是iOS/TV 平臺開發(包含UIKit 框架)通過:

1         CGFloat scale = 1;
2         scale = [UIScreen mainScreen].scale;

獲得當前屏幕的scale,並賦值給float 類型的scale 變量,用於控制返回的動態圖片的第一幀圖像的大小。

  7.使用CGImageSource.h 的:CGImageSourceCreateImageAtIndex(source, 0, NULL),獲得source 裏面index 為0 時的CGImageRef 實例CGImage。

1 /* Return the image at `index‘ in the image source `isrc‘.  The index is
2  * zero-based. The `options‘ dictionary may be used to request additional
3  * creation options; see the list of keys above for more information. */
4 
5 IMAGEIO_EXTERN CGImageRef __nullable CGImageSourceCreateImageAtIndex(CGImageSourceRef __nonnull isrc, size_t index, CFDictionaryRef __nullable options)  IMAGEIO_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_4_0);

  8.如果是iOS/TV/WATCH 平臺開發,根據上面獲取的CGImage 和scale 做參數,使用:

1 + (UIImage *)imageWithCGImage:(CGImageRef)cgImage scale:(CGFloat)scale orientation:(UIImageOrientation)orientation NS_AVAILABLE_IOS(4_0);

創建一個UIImage 實例frameImage。

  9.把frameImage 放在一個NSArray 裏面作參數,使用:

1 + (nullable UIImage *)animatedImageWithImages:(NSArray<UIImage *> *)images duration:(NSTimeInterval)duration NS_AVAILABLE_IOS(5_0);

創建一個images 為一張圖片,duration 為0.0f的動態圖片並賦值給staticImage。如果是MAC 平臺開發,依然CGImage 作參數,使用:

1 [[UIImage alloc] initWithCGImage:CGImage size:NSZeroSize];

創建一個UIImage 實例賦值給staticImage。

  10.使用CGImage.h 的CGImageRelease(CGImage) 釋放CGImage。

1 /* Equivalent to `CFRelease(image)‘. */
2 
3 CG_EXTERN void CGImageRelease(CGImageRef cg_nullable image)
4     CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

  11.使用CFBase.h 的CFRelease(source) 釋放source。

1 CF_EXPORT
2 void CFRelease(CFTypeRef cf);

  12.返回staticImage。

END

參考鏈接:http://www.jianshu.com/p/d3e9e3d0a778

SDWebImage源碼閱讀(三)UIImage+GIF