背景
今日吃饱了,确实撑得慌,找了我的邻居-阿杰一起散步,走了好大一圈,最后在小区下聊起了技术,从YYKit,SDWebImage 等第三方库,扯到了关联引用,因为他们都用到了这个技术,然而我又想到了单例,单例和关联引用在实现上有一个相同点—都需要一个静态变量;那么疑问就来了:同样都需要一个静态变量,为什么结果不一样呢?或许你还没明白我的疑问是什么,请继续阅读吧!
一、回顾单例
我简单的写了个单例类 SingletonObject ,把静态变量 instance 放在 sharedInstance 类方法里看起来可能更好一些,放在外面也是可以的,二者的区别是作用域不同,生命周期也差不多,我个人比较喜欢放在方法内部,因为外部一般不需要直接获取,这里放在外部是为了和我们今天的角儿进行对比,提出我的疑问做铺垫。
@implementation SingletonObject
static id instance;
+ (instancetype)sharedInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc]init];
});
return instance;
}
@end
请问你有没有思考过,为什么这里可以实现单例,instance 变量不是每个对象都有一份吗?当然不是的!这个 instance 静态变量的地址是和类 SingletonObject 是对应的,也就说 instance 静态变量的地址是确定的,只要这个类的地址确定了,他也就是确定的了,因此可以简单的理解为 instance 静态变量是属于类的,姑且称之为 “类变量”,与实例变量相对应。
二、提出疑问
我们已经知道了静态变量是个“类变量”,我们也都用过 SDWebImage,其内部实现正是使用了关联引用,才使得我们使用起来是那么的方便,图片可以准确无误的下载显示出来,也就是说每个 imageView 对应图片都是自己想要的,一一对应的,我的问题来了,既然关联引用也使用了“类变量”,那么不应该是 imageView 最终都要显示为同一张图片才对嘛?!虽然你创建了 N 个 imageView ,但是 imageView 的 “类变量”(关联引用的 key) 是唯一的啊,这明显是个一对多的关系!可为什么使用 SDWebImage 的时候一切都那么正常,最终图片都一一对应上了,你怎么看呢?我觉得此事必有蹊跷!
三、大胆推测
我最后给阿杰做了一个这样的推测:问题肯定在关联引用的处理上,要不然我们就没必要在使用的时候传个 key 进去了,或许正是根据这个 key 做了一个地址偏移什么的,每个对象都能或许元类信息,然后给自身加个偏移存储下这个关联的对象,我们可以从打印静态变量地址,打印堆栈信息,runtime源码入手…
然后就各回各家,各找各妈了…
四、揭开神秘面纱
这么大的疑问放心里,肯定睡不着的,回家就开始找源码了,结果就找到了内部实现的代码,现在拿出来仔细分析下内部实现:
//这是我们调用的方法
void objc_setAssociatedObject(id object, const void *key, id value,
objc_AssociationPolicy policy)
{
objc_setAssociatedObject_non_gc(object, key, value, policy);
}
//这是内部的私有方法;
void objc_setAssociatedObject_non_gc(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}
//设置关联引用对象,最终将调用这个方法
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// 这段代码有点像重写 setter 方法的感觉;old就是赋值之前的老值,要被释放掉的;ObjcAssociation 下面会介绍;
ObjcAssociation old_association(0, nil);
// acquireValue:对传入的 value 做 reatain,copy 等策略
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;//这是重点,每个类都可以创建一个 manager 管理关联的对象,不过 manager 管理的 Hash