该文章阅读的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