原始碼閱讀:SDWebImage(二)——SDWebImageCompat

堯少羽發表於2018-05-25

該文章閱讀的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_IPHONETARGET_OS_IOSTARGET_OS_TVTARGET_OS_WATCH就是MAC平臺。否則就不是。


#if TARGET_OS_IOS || TARGET_OS_TV
    #define SD_UIKIT 1
#else
    #define SD_UIKIT 0
#endif
複製程式碼

這段程式碼定義了一個巨集SD_UIKIT,來判斷是否有UIKit,因為UIKit存在於iOStvOS兩個平臺。所以,只要是這兩個平臺中的任何一個就有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_ENUMNS_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(一)——從使用入手

原始碼閱讀: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

相關文章