UIImage+GIF 是UIImage 類的一個GIF 分類,在以前的版本里面這個分類是用了處理GIF 動態圖片的可是會有內存暴增的bug。在當前 '4.0.0-beta2' 的版本里GIF 動態圖片處理放在了UIImage+MultiFormat 這個分類裏面,而當前這個GIF 的分類的功能只是將動態圖片做爲靜態圖片來處理,若是是靜態圖片的NSData 數據則轉化爲靜態UIImage 直接返回,若是是動態圖片的NSData 數據,則把圖像的第1幀圖像轉換化爲靜態UIImage 返回。html
首先看UIImage+GIF.h 文件:git
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
定義了一個實例方法和一個類方法:github
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 文件:框架
主要研究上面類方法的實現。ide
學習研究以前先作一些拓展:函數
size_t 類型:學習
size_t 類型定義在cstddef 頭文件中,該文件是C標準庫的頭文件stddef.h 的C++ 版。它是一個與機器相關的unsigned類型,其大小足以保證存儲內存中對象的大小。
例如:bitset 的size 操做返回bitset對象中二進制位中的個數,返回值類型是size_t。
size_t 是標準C庫中定義的,應爲unsigned int,在64位系統中爲 long unsigned int。
使用:
實現方式:
在C++ 中,設計 size_t 就是爲了適應多個平臺的。size_t 的引入加強了程序在不一樣平臺上的可移植性。
size_t 是針對系統定製的一種數據類型,通常是整型,由於 C/C++ 標準只定義最低的位數,而不是必需的固定位數。並且在內存裏,對數的高位對齊存儲仍是低位對齊存儲各系統都不同。爲了提升代碼的可移植性,就有必要定義這樣的數據類型。通常這種類型都會定義到它具體佔幾位內存等。固然,有些是編譯器或系統已經給定義好的。
經測試發現,在32位系統中size_t是4字節的,而在64位系統中,size_t 是8字節的,這樣利用該類型能夠加強程序的可移植性。
詳細解釋:
在編譯的過程當中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://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