「这是我参加2022初次更文应战的第28天,活动详情查看:2022初次更文应战」。

  • 弱引证时干了什么,如下代码来看看
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *obj = [[NSObject alloc]init];
        __weak NSObject *weakObj = obj;
    }
    return 0;
}
  • 经过clang来看看cpp代码
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_5c_vc7szrdj0xj63p71bn1fz8n80000gn_T_main_32a486_mi_0);
        NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
        __attribute__((objc_ownership(weak))) NSObject *weakObj = obj;
    }
    return 0;
}
  • 可以看出是经过objc_ownership来完成,但是这样也没法追寻下去了
    那么转换成.ll中间文件来看看
define i32 @main(i32 %0, i8** %1) #1 {
  %3 = alloca i32, align 4
  %4 = alloca i32, align 4
  %5 = alloca i8**, align 8
  %6 = alloca %0*, align 8
  %7 = alloca %0*, align 8
  store i32 0, i32* %3, align 4
  store i32 %0, i32* %4, align 4
  store i8** %1, i8*** %5, align 8
  %8 = call i8* @llvm.objc.autoreleasePoolPush() #2
  notail call void (i8*, ...) @NSLog(i8* bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_ to i8*))
  %9 = load %struct._class_t*, %struct._class_t** @"OBJC_CLASSLIST_REFERENCES_$_", align 8
  %10 = bitcast %struct._class_t* %9 to i8*
  %11 = call i8* @objc_alloc_init(i8* %10)
  %12 = bitcast i8* %11 to %0*
  store %0* %12, %0** %6, align 8
  %13 = load %0*, %0** %6, align 8
  %14 = bitcast %0** %7 to i8**
  %15 = bitcast %0* %13 to i8*
  %16 = call i8* @llvm.objc.initWeak(i8** %14, i8* %15) #2
  %17 = bitcast %0** %7 to i8**
  call void @llvm.objc.destroyWeak(i8** %17) #2
  %18 = bitcast %0** %6 to i8**
  call void @llvm.objc.storeStrong(i8** %18, i8* null) #2
  call void @llvm.objc.autoreleasePoolPop(i8* %8)
  ret i32 0
}
  • 可以看到%16 = call i8* @llvm.objc.initWeak(i8** %14, i8* %15) #2,是调用了objcinitWeak办法,那么就去objc源码里捋这个办法就行了,下面根本都是在源码里注释探究。

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指针地址,如文章最初比如中的weakObj的地址
  • id newObj:引证的目标,即文章最初比如中的obj
  • 而后returnstoreWeak办法,这个便是核心完成了

storeWeak

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;
 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;
    }
    if (haveNew  &&  newObj) {
        Class cls = newObj->getIsa();
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized()) 
        {/// 假如cls还没有初始化,先初始化,再尝试设置weak
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            class_initialize(cls, (id)newObj);
            previouslyInitializedClass = cls;
            goto retry;/// 从头获取一遍newObj,这时的newObj应该现已初始化过了
        }
    }
    // Clean up old value, if any.
    if (haveOld) {
        // 假如weak_ptr之前弱引证过别的目标oldObj,则调用weak_unregister_no_lock,在oldObj的weak_entry_t中移除该weak_ptr地址
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }
    // Assign new value, if any.
    if (haveNew) {// 假如weak_ptr需求弱引证新的目标newObj
        // 1. 调用weak_register_no_lock办法,将weak ptr的地址记录到newObj对应的weak_entry_t中
        newObj = (objc_object *)
            weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                  crashIfDeallocating);
        // 2. 更新newObj的isa的weakly_referenced bit标志位
        if (newObj  &&  !newObj->isTaggedPointer()) {
            newObj->setWeaklyReferenced_nolock();
        }
        // 3. *location 赋值,也便是将weak ptr直接指向了newObj。可以看到,这里并没有将newObj的引证计数+1
        *location = (id)newObj;// 将weak ptr指向object
    }
    else {
        // No new value. The storage is not changed.
    }
    // 解锁
    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
    return (id)newObj;// 返回newObj
}
  • storeWeak办法接受5个参数,其中HaveOld haveOld, HaveNew haveNew, CrashIfDeallocating crashIfDeallocating 3个枚举别离传入的是false,true,true,别离表明:weak ptr之前是否现已指向了一个弱引证,weak ptr是否需求指向一个新引证, 假如被弱引证的目标正在析构,此时再弱引证该目标,是否应该crash
  • 结合上述代码注释应该大致能捋清楚storeWeak干了啥