該文章閱讀的SDWebImage的版本為4.3.3。
這個類是一個配置類,類似於一個配置檔案,也可以說是一個相容類,用於相容不同平臺。
先看這個類匯入的標頭檔案#import <TargetConditionals.h>
。這個標頭檔案的內容是蘋果提供的配置條件,它會自動配置編譯器所要編譯的程式碼將要使用的微處理器指令集、執行系統以及執行時環境。
#ifdef __OBJC_GC__
#error SDWebImage does not support Objective-C Garbage Collection
#endif
複製程式碼
- 首先來看
__OBJC_GC__
這個巨集,這個巨集的意思是,是否支援Objective-C的垃圾回收機制 - 然後
#error
定義了一個錯誤,當Xcode在編譯時遇到這個預編譯指令就會停止編譯,錯誤的內容就是SDWebImage不支援Objective-C的垃圾回收機制
- 所以,這段程式碼的意思就很明顯了:
SDWebImage不支援Objective-C的垃圾回收機制
#if !TARGET_OS_IPHONE && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_WATCH
#define SD_MAC 1
#else
#define SD_MAC 0
#endif
複製程式碼
這段程式碼定義了一個巨集SD_MAC
,這樣做的目的根據SDWebImage
的作者的描述是,蘋果提供TARGET_OS_MAC
這個巨集有點兒迷、不靠譜,所以就自定義了一個:如果不是TARGET_OS_IPHONE
、TARGET_OS_IOS
、TARGET_OS_TV
和TARGET_OS_WATCH
就是MAC平臺。否則就不是。
#if TARGET_OS_IOS || TARGET_OS_TV
#define SD_UIKIT 1
#else
#define SD_UIKIT 0
#endif
複製程式碼
這段程式碼定義了一個巨集SD_UIKIT
,來判斷是否有UIKit
,因為UIKit
存在於iOS
和tvOS
兩個平臺。所以,只要是這兩個平臺中的任何一個就有UIKit
。
#if TARGET_OS_IOS
#define SD_IOS 1
#else
#define SD_IOS 0
#endif
複製程式碼
#if TARGET_OS_TV
#define SD_TV 1
#else
#define SD_TV 0
#endif
複製程式碼
#if TARGET_OS_WATCH
#define SD_WATCH 1
#else
#define SD_WATCH 0
#endif
複製程式碼
至於為什麼要定義這三個,我個人的考慮可能是以後蘋果的定義可能會變,這樣寫的話,就不在每個用到這個巨集的地方去做修改,這需要在這一個檔案的這一個地方修改就可以了。
#if SD_MAC
#import <AppKit/AppKit.h>
#ifndef UIImage
#define UIImage NSImage
#endif
#ifndef UIImageView
#define UIImageView NSImageView
#endif
#ifndef UIView
#define UIView NSView
#endif
#else
#if __IPHONE_OS_VERSION_MIN_REQUIRED != 20000 && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0
#error SDWebImage doesn't support Deployment Target version < 5.0
#endif
#if SD_UIKIT
#import <UIKit/UIKit.h>
#endif
#if SD_WATCH
#import <WatchKit/WatchKit.h>
#endif
#endif
複製程式碼
-
如果在MAC平臺上,會做一些轉換:把
NSImage
轉換成UIImage
、把NSImageView
轉換成UIImageView
、把NSView
轉換成UIView
,這樣做的好處是,不用因為平臺的不同再做判斷或者再多寫一份程式碼了。 -
如果不是MAC平臺,先定義了一個錯誤:
SDWebImage
不支援iOS5.0以下的版本,至於__IPHONE_OS_VERSION_MIN_REQUIRED != 20000
這個判斷我沒太看明白,查了好多資料感覺合理的解釋是:好像如果用模擬器跑的話__IPHONE_OS_VERSION_MIN_REQUIRED
的值會是20000
,但是我用模擬器跑了一下並不是這樣的,可能是Xcode更新了的緣故?接著,根據是否是watchOS
匯入不同的標頭檔案。
#ifndef NS_ENUM
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
#endif
#ifndef NS_OPTIONS
#define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type
#endif
複製程式碼
因為NS_ENUM
和NS_OPTIONS
都是在iOS 6 / OS X Mountain Lion
才開始有,但是根據上面那段可知,SDWebImage
相容到iOS5
,所以這算是重新定義相容iOS5
FOUNDATION_EXPORT UIImage *SDScaledImageForKey(NSString *key, UIImage *image);
複製程式碼
這是定義了一個公共函式用來縮放圖片。直接來看實現
inline UIImage *SDScaledImageForKey(NSString * _Nullable key, UIImage * _Nullable image) {
// 如果沒有傳入圖片就不繼續向下執行了
if (!image) {
return nil;
}
// 如果是MAC平臺也不向下執行了
#if SD_MAC
return image;
#elif SD_UIKIT || SD_WATCH
if ((image.images).count > 0) {
// 如果是動圖,就遍歷出所有的圖片後遞迴呼叫該函式進行縮放,然後返回
NSMutableArray<UIImage *> *scaledImages = [NSMutableArray array];
for (UIImage *tempImage in image.images) {
[scaledImages addObject:SDScaledImageForKey(key, tempImage)];
}
UIImage *animatedImage = [UIImage animatedImageWithImages:scaledImages duration:image.duration];
if (animatedImage) {
animatedImage.sd_imageLoopCount = image.sd_imageLoopCount;
}
return animatedImage;
} else {
#if SD_WATCH
if ([[WKInterfaceDevice currentDevice] respondsToSelector:@selector(screenScale)]) {
#elif SD_UIKIT
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
#endif
// 根據傳入的key中所包含的@2x和@3x來縮放圖片至對應的比例後返回
CGFloat scale = 1;
if (key.length >= 8) {
NSRange range = [key rangeOfString:@"@2x."];
if (range.location != NSNotFound) {
scale = 2.0;
}
range = [key rangeOfString:@"@3x."];
if (range.location != NSNotFound) {
scale = 3.0;
}
}
UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation];
image = scaledImage;
}
return image;
}
#endif
}
複製程式碼
對於這個函式,我的理解是根據圖片地址連結中是否包含@2x和@3x資訊,如果有,就把圖片縮放到指定比例。
typedef void(^SDWebImageNoParamsBlock)(void);
複製程式碼
這就是定義了一個名字為SDWebImageNoParamsBlock
,引數為空,返回值為空的程式碼塊,方便使用。
FOUNDATION_EXPORT NSString *const SDWebImageErrorDomain;
複製程式碼
NSString *const SDWebImageErrorDomain = @"SDWebImageErrorDomain";
複製程式碼
定義了一個靜態字串用於作為錯誤資訊的key
#ifndef dispatch_queue_async_safe
#define dispatch_queue_async_safe(queue, block)\
if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(queue)) == 0) {\
block();\
} else {\
dispatch_async(queue, block);\
}
#endif
#ifndef dispatch_main_async_safe
#define dispatch_main_async_safe(block) dispatch_queue_async_safe(dispatch_get_main_queue(), block)
#endif
複製程式碼
這個巨集就是使想要執行的程式碼塊block
,在主執行緒主佇列執行。
#if !__has_feature(objc_arc)
#error SDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
#endif
複製程式碼
#if !OS_OBJECT_USE_OBJC
#error SDWebImage need ARC for dispatch object
#endif
複製程式碼
這兩個錯誤的意思都是說:SDWebImage
只支援ARC
。
好了,到這兒,這個類我們就看完了。之所以先看這個類,因為基本每個類都會呼叫這個類,所以看完了這個類,以後再看其他類就會方便很多。
原始碼閱讀系列:SDWebImage
原始碼閱讀:SDWebImage(二)——SDWebImageCompat
原始碼閱讀:SDWebImage(三)——NSData+ImageContentType
原始碼閱讀:SDWebImage(四)——SDWebImageCoder
原始碼閱讀:SDWebImage(五)——SDWebImageFrame
原始碼閱讀:SDWebImage(六)——SDWebImageCoderHelper
原始碼閱讀:SDWebImage(七)——SDWebImageImageIOCoder
原始碼閱讀:SDWebImage(八)——SDWebImageGIFCoder
原始碼閱讀:SDWebImage(九)——SDWebImageCodersManager
原始碼閱讀:SDWebImage(十)——SDImageCacheConfig
原始碼閱讀:SDWebImage(十一)——SDImageCache
原始碼閱讀:SDWebImage(十二)——SDWebImageDownloaderOperation
原始碼閱讀:SDWebImage(十三)——SDWebImageDownloader
原始碼閱讀:SDWebImage(十四)——SDWebImageManager
原始碼閱讀:SDWebImage(十五)——SDWebImagePrefetcher
原始碼閱讀:SDWebImage(十六)——SDWebImageTransition
原始碼閱讀:SDWebImage(十七)——UIView+WebCacheOperation
原始碼閱讀:SDWebImage(十八)——UIView+WebCache
原始碼閱讀:SDWebImage(十九)——UIImage+ForceDecode/UIImage+GIF/UIImage+MultiFormat
原始碼閱讀:SDWebImage(二十)——UIButton+WebCache
原始碼閱讀:SDWebImage(二十一)——UIImageView+WebCache/UIImageView+HighlightedWebCache