Objective-C runtime机制(6)——weak引用的底层实现原理

前言

提起弱引用,大家都知道它的作用:
(1)不会添加引用计数 (2)当所引用的对象释放后,引用者的指针自动置为nil
那么,围绕它背后的实现,是怎么样的呢?在许多公司面试时,都会问到这个问题。那么,今天就带大家一起分析一下weak引用是怎么实现的,希望能够搞清楚每一个细节。

Store as weak

当我们要weak引用一个对象,我们可以这么做:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSObject *obj = [[NSObject alloc] init];
        __weak NSObject *weakObj = obj;
    }
}

创建了一个NSObject对象obj,然后用weakObj对obj做弱引用。
当我们对一个对象做weak引用的时候,其背后是通过runtime来支持的。当把一个对象做weak引用时,会调用runtime方法objc_initWeak

objc_initWeak

id objc_initWeak(id *location, id newObj)
{
    if (!newObj) {
        *location = nil;
        return nil;
    }

    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object*)newObj);
}

该方法接受两个参数:

  • id *location :__weak指针的地址,即例子中的weak指针取地址: &weakObj它是一个指针的地址。之所以要存储指针的地址,是因为最后我们要讲__weak指针指向的内容置为nil,如果仅存储指针的话,是不能够完成这个功能的。
  • id newObj :所引用的对象。即例子中的obj

有一个返回值 id : 会返回obj自身,但其值已经做了更改(isa_t中的weak_ref位置1),参见Objective-C runtime机制(5)——iOS 内存管理

objc_initWeak实质是调用了storeWeak方法。看这个方法的名字,就可以猜到是将weak引用存到某个地方,没错,实际上苹果就是这么做的。

storeWeak

storeWeak方法有点长,这也是weak引用的核心实现部分。其实核心也就实现了两个功能:

  • 将weak指针的地址location存入到obj对应的weak_entry_t的数组(链表)中,用于在obj析构时,通过该数组(链表)找到所有其weak指针引用,并将指针指向的地址(location)置为nil。关于weak_entry_t,在上一篇中已有介绍。

  • 如果启用了isa优化,则将obj的isa_tweakly_referenced位置1。置位1的作用主要是为了标记obj被weak引用了,当dealloc时,runtime会根据weakly_referenced标志位来判断是否需要查找obj对应的weak_entry_t,并将引用置为nil

// Template parameters.
enum HaveOld { DontHaveOld = false, DoHaveOld = true };
enum HaveNew { DontHaveNew = false, DoHaveNew = true };
enum CrashIfDeallocating {
    DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};

template <HaveOld haveOld, HaveNew haveNew,
          CrashIfDeallocating crashIfDeallocating>
static id 
storeWeak(id *location, objc_object *newObj)
{
    assert(haveOld  ||  haveNew);
    if (!haveNew) assert(newObj == nil);

    Class previouslyInitializedClass = nil;
    id oldObj;
    SideTable *oldTable;
    SideTable *newTable;

    // Acquire locks for old and new values.
    // Order by lock address to prevent lock ordering problems. 
    // Retry if the old value changes underneath us.
 retry:
    if (haveOld) { // 如果weak ptr之前弱引用过一个obj,则将这个obj所对应的SideTable取出,赋值给oldTable
        oldObj = *location;
        oldTable = &SideTables()[oldObj];
    } else {
        oldTable = nil; // 如果weak ptr之前没有弱引用过一个obj,则oldTable = nil
    }
    if (haveNew) { // 如果weak ptr要weak引用一个新的obj,则将该obj对应的SideTable取出,赋值给newTable
        newTable = &SideTables()[newObj];
    } else {
        newTable = nil; // 如果weak ptr不需要引用一个新obj,则newTable = nil
    }
    
    // 加锁操作,防止多线程中竞争冲突
    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);

    // location 应该与 oldObj 保持一致,如果不同,说明当前的 location 已经处理过 oldObj 可是又被其他线程所修改
    if (haveOld  &&  *location != oldObj) {
        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
        goto retry;
    }

    // Prevent a deadlock between the weak reference machinery
    // and the +initialize machinery by ensuring that no 
    // weakly-referenced object has an un-+initialized isa.
    if (haveNew  &&  newObj) {
        Class cls = newObj->getIsa();
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized())  // 如果cls还没有初始化,先初始化,再尝试设置weak
        {
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            _class_initialize(_class_getNonMetaClass(cls, (id)newObj));

            // If this class is finished with +initialize then we're good.
            // If this class is still running +initialize on this thread 
            // (i.e. +initialize called storeWeak on an instance of itself)
            // then we may proceed but it will appear initializing and 
       
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值