SDWebImage 作为一个将服务器远程的图片获取到UIImageView上显示的一个第三方类库,于我而言,最为常用的API莫过于 -sd_setImageWithURL: placeholderImage:
方法。不妨以此入手,拨开它源代码的神秘面纱。
点入.m文件,发现上图中从上至下的一列方法,都依次指向相邻的下一个方法,不过在上层调用时个别参数置为 nil
或 0
。直到 -sd_setImageWithURL: placeholderImage:options:progress:completed:
,方见端倪。
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock { [self sd_cancelCurrentImageLoad]; objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); // [1] if (!(options & SDWebImageDelayPlaceholder)) { // [2] dispatch_main_async_safe(^{ self.image = placeholder; }); } ... }
根据苹果的 官方文档 :
objc_setAssociatedObject
是Objective-C运行时的一个函数,它可以将两个对象关联起来。这个函数需要四个参数:原对象,一个键,一个值,和一个关联策略的常量。其中,键是一个void指针。
abbood 将它的使用场景归结为如下几点:
(1)给类别(category)添加实例变量。
假设你想给你不能修改的对象(比如说苹果官方提供的对象,UIImage、UILabel神马的。注意:我们这里讨论的是修改对象本身,不包括将其子类化。)的类别中添加个自定义的属性( 这几乎是Objective-C最大的缺点 ),比如说,我们想给 UIImage
添加一个title的属性。
// UIImage-Title.h: @interface UIImage(Title) @property(nonatomic, copy) NSString *title; @end // UIImage-Title.m: #import <Foundation/Foundation.h> #import <objc/runtime.h> static char titleKey; @implementation UIImage(Title) - (NSString *)title { return objc_getAssociatedObject(self, &titleKey); } - (void)setTitle:(NSString *)title { objc_setAssociatedObject(self, &titleKey, title, OBJC_ASSOCIATION_COPY); } @end
(2)动态地给对象添加状态信息,而这个对象的实例变量即便结合KVO也不能达到目的。(有点拗口,慢慢理解。。)
意思是说,你的对象只有在runtime期间(也就是说,动态地)才能获取状态信息。所以,尽管你可以把状态信息存在实例变量中,但实际上你是要在runtime期间把这个信息和对象绑定起来、并且动态地把它和另一个对象关联,要强调的是 这是一个对象的动态状态 这个事实。
如下是一个不错的 例子 中的一个片段,在这个例子中,关联对象使用了KVO通知,但KVO并不是关联对象的必要条件。
static char BOOLRevealing; - (BOOL)isRevealing { return [(NSNumber*)objc_getAssociatedObject(self, &BOOLRevealing) boolValue]; } - (void)_setRevealing:(BOOL)revealing { [self willChangeValueForKey:@"isRevealing"]; objc_setAssociatedObject(self, &BOOLRevealing, [NSNumber numberWithBool:revealing], OBJC_ASSOCIATION_RETAIN_NONATOMIC); [self didChangeValueForKey:@"isRevealing"]; }
引一段 SDWebImage 中的代码,纯正的、不含KVO的长这样子:
- (void)setShowActivityIndicatorView:(BOOL)show{ objc_setAssociatedObject(self, &TAG_ACTIVITY_SHOW, [NSNumber numberWithBool:show], OBJC_ASSOCIATION_RETAIN); } - (BOOL)showActivityIndicatorView{ return [objc_getAssociatedObject(self, &TAG_ACTIVITY_SHOW) boolValue]; }
其中, TAG_ACTIVITY_SHOW
的定义如下:
static char TAG_ACTIVITY_SHOW;
所以,这里相当于给UIImageView的category添加了一个 imageURLKey
的属性,我猜想,是用来表示图片资源的唯一性的key。
&
而不是 &&
呢? 趁机捡一捡基础知识:
&
是按位运算的双目运算符,功能是参与将运算的两个数各自对应的二进位相与。
例如:9&5可以写算式:00001001 & 00000101 = 00000001,即 9 & 5 = 1。
&&
检查第一个操作数的值,如果为false,就不再处理第二个操作数,直接返回false。
所以,这里的 (options & SDWebImageDelayPlaceholder)
,是两个枚举值进行按位运算,而非我们平常写的true/false的布尔判断。
随手点入枚举值的定义,发现这一堆:
typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) { /** * By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying. * This flag disable this blacklisting. */ SDWebImageRetryFailed = 1 << 0, // [3] /** * By default, image downloads are started during UI interactions, this flags disable this feature, * leading to delayed download on UIScrollView deceleration for instance. */ SDWebImageLowPriority = 1 << 1, /** * This flag disables on-disk caching */ SDWebImageCacheMemoryOnly = 1 << 2, ... };
而其实 SDWebImageDelayPlaceholder
正是这一群枚举值中的一个,所以猜测,这句代码的意思是说,传入的 option
为 SDWebImageDelayPlaceholder
时该如何如何……(可为嘛曲曲折折地不直说呢?=_=|||)
<<
为按位左移运算符。具体它 是什么以及怎样运算 ,这里略去不提了。简单举例如下:
1 << 2 即 1 * 4 1 << 3 即 1 * 8
http://stackoverflow.com/questions/5909412/what-is-objc-setassociatedobject-and-in-what-cases-should-it-be-used/16313377#16313377
Mattt的真迹: http://nshipster.com/associated-objects/