浅析关联引用

本文从单例引入,探讨关联引用在内存管理和对象存储中的工作原理。通过对 runtime 源码的分析,揭示了关联引用如何做到一对一的映射,以及对象释放时关联对象的自动释放机制。最后,作者提供了实验验证结果,强调正确理解和使用类变量的重要性。
摘要由CSDN通过智能技术生成

背景

今日吃饱了,确实撑得慌,找了我的邻居-阿杰一起散步,走了好大一圈,最后在小区下聊起了技术,从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
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值