iOS weak原理源码探究

作者: 水煮杰尼龟 | 来源:发表于2021-05-13 18:44 被阅读0次
    • 弱引用时干了什么,如下代码来看看
    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干了啥

    这里主要涉及到2个函数(weak_unregister_no_lock和weak_register_no_lock)以及SideTable数据结构

    我们具体来看看

    SideTable

    struct SideTable {
        spinlock_t slock;/// 自旋锁
        RefcountMap refcnts;/// 存储对象引用计数的map
        weak_table_t weak_table;/// 存储对象弱引用的map
    
        SideTable() {
            memset(&weak_table, 0, sizeof(weak_table));
        }
    
        ~SideTable() {
            _objc_fatal("Do not delete SideTable.");
        }
    };
    
    • SideTable存储着对象的引用计数以及weak引用,而一个个的SideTable又构成了一个集合,叫SideTablesSideTables在系统中是全局唯一的。
    • newTable = &SideTables()[newObj] 可以看到通过对象获取到其SideTable,而其内部是通过StripedMap对应的算法获取的,参数是对象地址。
    class StripedMap {
        static unsigned int indexForPointer(const void *p) {
            uintptr_t addr = reinterpret_cast<uintptr_t>(p);
            return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
        }
    }
    
    RefcountMap
    struct RefcountMapValuePurgeable {
        static inline bool isPurgeable(size_t x) {
            return x == 0;
        }
    };
    typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;
    
    • 可以简单理解为一个mapkeyDisguisedPtr<objc_object>类型,valuesize_t类型,还多了一个
      RefcountMapValuePurgeable:是否可清除数据,内部通过判断引用计数为0
    weak_table_t
    struct weak_table_t {
        weak_entry_t *weak_entries;/// 存储弱引用对象的相关信息
        size_t    num_entries;/// 元素个数
        uintptr_t mask; /// hash数组长度-1,用于和hash值做位与计算,来确定数组下标(hash数组的长度,而不是元素个数。比如,数组长度可能是64,而元素个数仅存了2个)
        uintptr_t max_hash_displacement;/// 可能会发生的hash冲突的最大次数
    };
    
    struct weak_entry_t {
        DisguisedPtr<objc_object> referent;// 被弱引用的对象
        
        // 引用该对象的对象列表,联合
        union {
            struct {/// 引用个数大于4
                weak_referrer_t *referrers;// 弱引用该对象的对象列表的动态数组
                uintptr_t        out_of_line_ness : 2;// 是否使用动态数组标记位
                uintptr_t        num_refs : PTR_MINUS_2;// 动态数组中元素的个数
                uintptr_t        mask;       // 用于hash确定动态数组index
                uintptr_t        max_hash_displacement;// 最大的hash冲突次数
            };
            struct {/// 引用个数小于4
                // out_of_line_ness field is low bits of inline_referrers[1]
                weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
            };
        };
    
        bool out_of_line() {
            return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
        }
    
        weak_entry_t& operator=(const weak_entry_t& other) {
            memcpy(this, &other, sizeof(other));
            return *this;
        }
    
        weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
            : referent(newReferent)
        {
            inline_referrers[0] = newReferrer;
            for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
                inline_referrers[i] = nil;
            }
        }
    };
    
    • weak_entries实质上是一个hash数组,数组中存储weak_entry_t类型的元素

    引用计数

    • 看一下OC是如何获取引用计数的
    inline uintptr_t 
    objc_object::rootRetainCount()
    {
            /// tagged pointer,直接返回this ,不需要引用计数的
        if (isTaggedPointer()) return (uintptr_t)this;
    
        sidetable_lock();
        isa_t bits = LoadExclusive(&isa.bits);
        ClearExclusive(&isa.bits);
        /// 采用了优化的isa指针
        if (bits.nonpointer) {
                /// 先读取isa.extra_rc
            uintptr_t rc = 1 + bits.extra_rc;
            if (bits.has_sidetable_rc) {///如果extra_rc不够大, 还需要读取sidetable中的数据
                rc += sidetable_getExtraRC_nolock();
            }
            sidetable_unlock();
            return rc;
        }
        sidetable_unlock();
        /// 未采用了优化的isa指针,直接返回sidetable中的数据
        return sidetable_retainCount();
    }
    

    weak_register_no_lock

    • weak ptr地址 注册到obj对应的weak_entry_t
    weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                      crashIfDeallocating);
    
    id 
    weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
                          id *referrer_id, bool crashIfDeallocating)
    {
        objc_object *referent = (objc_object *)referent_id;
        objc_object **referrer = (objc_object **)referrer_id;
        ///referent为nil 或 referent 采用了TaggedPointer计数方式,直接返回
        if (!referent  ||  referent->isTaggedPointer()) return referent_id;
    
        /// 确保被引用的对象可用
        bool deallocating;
        if (!referent->ISA()->hasCustomRR()) {
            deallocating = referent->rootIsDeallocating();
        }
        else {
            BOOL (*allowsWeakReference)(objc_object *, SEL) = 
                (BOOL(*)(objc_object *, SEL))
                object_getMethodImplementation((id)referent, 
                                               @selector(allowsWeakReference));
            if ((IMP)allowsWeakReference == _objc_msgForward) {
                return nil;
            }
            deallocating =
                ! (*allowsWeakReference)(referent, @selector(allowsWeakReference));
        }
        /// 正在析构的对象,不能够被弱引用
        if (deallocating) {
            if (crashIfDeallocating) {
                _objc_fatal("Cannot form weak reference to instance (%p) of "
                            "class %s. It is possible that this object was "
                            "over-released, or is in the process of deallocation.",
                            (void*)referent, object_getClassName((id)referent));
            } else {
                return nil;
            }
        }
    
        // now remember it and where it is being stored
        weak_entry_t *entry;
        ///在 weak_table中通过referent找对应的weak_entry
        if ((entry = weak_entry_for_referent(weak_table, referent))) {/// 找到了
            /// 将referrer插入到weak_entry_t的引用数组中
            append_referrer(entry, referrer);
        } 
        else {/// 没找到
            /// 新建一个
            weak_entry_t new_entry(referent, referrer);
            weak_grow_maybe(weak_table);///是否需要动态扩容
            weak_entry_insert(weak_table, &new_entry);///将weak_entry_t插入到weak_table中
        }
    
        // Do not set *referrer. objc_storeWeak() requires that the 
        // value not change.
    
        return referent_id;
    }
    
    weak_entry_for_referent
    static weak_entry_t *
    weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
    {
        ASSERT(referent);
    
        weak_entry_t *weak_entries = weak_table->weak_entries;
    
        if (!weak_entries) return nil;
            /// 通过referent进行hash算法  ,将hash值和mask进行位与运算,得到初始数组下标
        size_t begin = hash_pointer(referent) & weak_table->mask;
        size_t index = begin;
        size_t hash_displacement = 0;
        ///通过index拿到的referent与上面的referent不相同
        while (weak_table->weak_entries[index].referent != referent) {
            index = (index+1) & weak_table->mask; /// index+1 ,找下一个位置,基于二进制运算,当寻找到最后一个位置时,它又会自动让你从数组的第一个位置开始寻找
            /// 在数组中转了一圈还没找到目标元素  触发bad_weak_table crash
            if (index == begin) bad_weak_table(weak_table->weak_entries);
            hash_displacement++;
            if (hash_displacement > weak_table->max_hash_displacement) {
              /// hash冲突大于了最大可能的冲突次数,说明目标对象不存在于数组中,返回nil
                return nil;
            }
        }
        
        return &weak_table->weak_entries[index];
    }
    
    append_referrer
    static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
    {
        if (! entry->out_of_line()) {/// 未使用动态数组
            // Try to insert inline.
            for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                if (entry->inline_referrers[i] == nil) {
                    entry->inline_referrers[i] = new_referrer;
                    return;
                }
            }
    
            // Couldn't insert inline. Allocate out of line.
            ///inline_referrers存满了,转为动态数组
            weak_referrer_t *new_referrers = (weak_referrer_t *)
                calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
            // This constructed table is invalid, but grow_refs_and_insert
            // will fix it and rehash it.
            for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                new_referrers[i] = entry->inline_referrers[i];
            }
            entry->referrers = new_referrers;
            entry->num_refs = WEAK_INLINE_COUNT;
            entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;
            entry->mask = WEAK_INLINE_COUNT-1;
            entry->max_hash_displacement = 0;
        }
            /// 断言: 此时使用的动态数组   
        ASSERT(entry->out_of_line());
            /// 如果动态数组中元素个数大于或等于数组位置总空间的3/4
        if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
            ///扩展数组空间为当前长度的一倍 size_t new_size = old_size ? old_size * 2 : 8;
            return grow_refs_and_insert(entry, new_referrer);
        }
        // 不需要扩容,直接插入到weak_entry中 ,这里的hash算法与weak_entry_for_referent里的一样
        size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
        size_t index = begin;
        size_t hash_displacement = 0;
        while (entry->referrers[index] != nil) {
            hash_displacement++;
            index = (index+1) & entry->mask;
            if (index == begin) bad_weak_table(entry);
        }
        if (hash_displacement > entry->max_hash_displacement) {
            entry->max_hash_displacement = hash_displacement;
        }
        /// 将new_referrer存入hash数组,并更新元素个数num_refs
        weak_referrer_t &ref = entry->referrers[index];
        ref = new_referrer;
        entry->num_refs++;
    }
    
    weak_grow_maybe
    static void weak_grow_maybe(weak_table_t *weak_table)
    {
        size_t old_size = TABLE_SIZE(weak_table);
    
        // Grow if at least 3/4 full.
        /// 当大于现有长度的3/4时,进行扩容。
        if (weak_table->num_entries >= old_size * 3 / 4) {
            weak_resize(weak_table, old_size ? old_size*2 : 64);
        }
    }
    
    static void weak_resize(weak_table_t *weak_table, size_t new_size)
    {
        size_t old_size = TABLE_SIZE(weak_table);
    
        weak_entry_t *old_entries = weak_table->weak_entries; /// 老数据
        weak_entry_t *new_entries = (weak_entry_t *)
            calloc(new_size, sizeof(weak_entry_t));/// 新数据申请内存
    
        /// 重置weak_table数据
        weak_table->mask = new_size - 1;
        weak_table->weak_entries = new_entries;
        weak_table->max_hash_displacement = 0;
        weak_table->num_entries = 0;  // restored by weak_entry_insert below
        
        if (old_entries) {
            weak_entry_t *entry;
            weak_entry_t *end = old_entries + old_size;
            for (entry = old_entries; entry < end; entry++) {
                if (entry->referent) {// 依次将老的数据插入到新的内存空间
                    weak_entry_insert(weak_table, entry);
                }
            }
            free(old_entries);
        }
    }
    
    weak_entry_insert
    /// 插入entry进entries里
    static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
    {
        weak_entry_t *weak_entries = weak_table->weak_entries;
        ASSERT(weak_entries != nil);
    
        size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask);
        size_t index = begin;
        size_t hash_displacement = 0;
        while (weak_entries[index].referent != nil) {
            index = (index+1) & weak_table->mask;
            if (index == begin) bad_weak_table(weak_entries);
            hash_displacement++;
        }
    
        weak_entries[index] = *new_entry;
        weak_table->num_entries++;
    
        if (hash_displacement > weak_table->max_hash_displacement) {
            weak_table->max_hash_displacement = hash_displacement;
        }
    }
    

    weak_unregister_no_lock

    • 如果weak_ptr之前弱引用过别的对象oldObj,则调用weak_unregister_no_lock,在oldObjweak_entry_t中移除该weak_ptr地址
    void
    weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, 
                            id *referrer_id)
    {
        objc_object *referent = (objc_object *)referent_id;
        objc_object **referrer = (objc_object **)referrer_id;
    
        weak_entry_t *entry;
    
        if (!referent) return;
    
        if ((entry = weak_entry_for_referent(weak_table, referent))) {///找到referent所对应的weak_entry_t
            remove_referrer(entry, referrer);///移除referrer
            /// 检查weak_entry_t的hash数组是否已经空了
            bool empty = true;
            if (entry->out_of_line()  &&  entry->num_refs != 0) {
                empty = false;
            }
            else {
                for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                    if (entry->inline_referrers[i]) {
                        empty = false; 
                        break;
                    }
                }
            }
    
            if (empty) {///空了,将weak_entry_t从weak_table中移除
                weak_entry_remove(weak_table, entry);
            }
        }
    }
    
    static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
    {
        // remove entry
        if (entry->out_of_line()) free(entry->referrers);
        bzero(entry, sizeof(*entry));
    
        weak_table->num_entries--;
    
        weak_compact_maybe(weak_table);
    }
    
    static void weak_compact_maybe(weak_table_t *weak_table)
    {
        size_t old_size = TABLE_SIZE(weak_table);
    
        // Shrink if larger than 1024 buckets and at most 1/16 full.
        /// 当前数组长度大于1024,且实际使用空间最多只有1/16时
        if (old_size >= 1024  && old_size / 16 >= weak_table->num_entries) {
            weak_resize(weak_table, old_size / 8);/// 缩小8倍
            // leaves new table no more than 1/2 full
        }
    }
    

    Dealloc

    • 当对象释放的时候,weak引用的释放。
    inline void
    objc_object::rootDealloc()
    {
        if (isTaggedPointer()) return;  // fixme necessary?
    
        if (fastpath(isa.nonpointer  &&  ///是否isa优化
                     !isa.weakly_referenced  &&  ///是否弱引用
                     !isa.has_assoc  &&  /// 是否有关联对象
                     !isa.has_cxx_dtor  &&  ///是否自定义c++析构
                     !isa.has_sidetable_rc))/// 是否用到sidetable
        {
            assert(!sidetable_present());
            free(this);
        } 
        else {
            object_dispose((id)this);
        }
    }
    
    • 有弱引用的时候,走object_dispose
    id 
    object_dispose(id obj)
    {
        if (!obj) return nil;
    
        objc_destructInstance(obj);    
        free(obj);
    
        return nil;
    }
    
    void *objc_destructInstance(id obj) 
    {
        if (obj) {
            // Read all of the flags at once for performance.
            bool cxx = obj->hasCxxDtor();
            bool assoc = obj->hasAssociatedObjects();
    
            // This order is important.
            if (cxx) object_cxxDestruct(obj);/// 调用C++析构函数
            if (assoc) _object_remove_assocations(obj);// 关联对象remove相关
            obj->clearDeallocating();// 清理引用
        }
    
        return obj;
    }
    
    inline void 
    objc_object::clearDeallocating()
    {
        if (slowpath(!isa.nonpointer)) {
            // Slow path for raw pointer isa.
            sidetable_clearDeallocating();
        }
        else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
            // Slow path for non-pointer isa with weak refs and/or side table data.
            clearDeallocating_slow();
        }
    
        assert(!sidetable_present());
    }
    
    NEVER_INLINE void
    objc_object::clearDeallocating_slow()
    {
        ASSERT(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));
            ///以this指针为key,找到对应的SideTable
        SideTable& table = SideTables()[this];
        table.lock();
        if (isa.weakly_referenced) {/// 弱引用
            weak_clear_no_lock(&table.weak_table, (id)this);
        }
        if (isa.has_sidetable_rc) {/// 采用了SideTable
            table.refcnts.erase(this);/// SideTable移除this
        }
        table.unlock();
    }
    

    weak_clear_no_lock

    void 
    weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
    {
        objc_object *referent = (objc_object *)referent_id;
    
        weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
        if (entry == nil) {
            /// XXX shouldn't happen, but does with mismatched CF/objc
            //printf("XXX no entry for clear deallocating %p\n", referent);
            return;
        }
    
        // zero out references
        weak_referrer_t *referrers;
        size_t count;
        
        if (entry->out_of_line()) {
            referrers = entry->referrers;
            count = TABLE_SIZE(entry);
        } 
        else {
            referrers = entry->inline_referrers;
            count = WEAK_INLINE_COUNT;
        }
        
        for (size_t i = 0; i < count; ++i) {
            objc_object **referrer = referrers[i];
            if (referrer) {
                ///将weak ptr设置为nil,这也就是为什么weak 指针会自动设置为nil的原因
                if (*referrer == referent) {
                    *referrer = nil;
                }
                else if (*referrer) {
                    _objc_inform("__weak variable at %p holds %p instead of %p. "
                                 "This is probably incorrect use of "
                                 "objc_storeWeak() and objc_loadWeak(). "
                                 "Break on objc_weak_error to debug.\n", 
                                 referrer, (void*)*referrer, (void*)referent);
                    objc_weak_error();
                }
            }
        }
        ///将referent的weak_entry_t移除出weak_table
        weak_entry_remove(weak_table, entry);
    }
    

    实践看看引用计数和weak引用

    • 现在苹果基本都是采用了isa指针优化的,即isa指针不只是表示class类型,而是一个占用64位的结构体
    # if __arm64__
    #   define ISA_MASK        0x0000000ffffffff8ULL
    #   define ISA_MAGIC_MASK  0x000003f000000001ULL
    #   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    #   define ISA_BITFIELD                                                      \
                ///1 表示开启了isa优化,0 表示没有启用isa优化
          uintptr_t nonpointer        : 1;                                       \
          ///对象是否有关联对象
          uintptr_t has_assoc         : 1;                                       \
          ///对象是否有C++或ARC析构函数
          uintptr_t has_cxx_dtor      : 1;                                       \
          ///类指针的值
          uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
          ///固定为0x1a,用于在调试时区分对象是否已经初始化
          uintptr_t magic             : 6;                                       \
          ///对象是否被别的对象弱引用
          uintptr_t weakly_referenced : 1;                                       \
          ///对象是否正在被释放
          uintptr_t deallocating      : 1;                                       \
          ///是否引用计数过大,借用sidetable来存储
          uintptr_t has_sidetable_rc  : 1;                                       \
          ///对象的引用计数减1
          uintptr_t extra_rc          : 19
    #   define RC_ONE   (1ULL<<45)
    #   define RC_HALF  (1ULL<<18)
    
    # elif __x86_64__
    #   define ISA_MASK        0x00007ffffffffff8ULL
    #   define ISA_MAGIC_MASK  0x001f800000000001ULL
    #   define ISA_MAGIC_VALUE 0x001d800000000001ULL
    #   define ISA_BITFIELD                                                        \
          uintptr_t nonpointer        : 1;                                         \
          uintptr_t has_assoc         : 1;                                         \
          uintptr_t has_cxx_dtor      : 1;                                         \
          uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
          uintptr_t magic             : 6;                                         \
          uintptr_t weakly_referenced : 1;                                         \
          uintptr_t deallocating      : 1;                                         \
          uintptr_t has_sidetable_rc  : 1;                                         \
          uintptr_t extra_rc          : 8
    #   define RC_ONE   (1ULL<<56)
    #   define RC_HALF  (1ULL<<7)
    
    # else
    #   error unknown architecture for packed isa
    # endif
    
    • 我这边是在Mac跑的可编译源码,所以对应的是x86_64

    • 首先在NSObject *obj = [[NSObject alloc]init];代码后打上断点,先看看obj的引用计数,很明显引用计数是1,extra_rcisa的后8位,那我们就来看看是不是1

    • 断点后,x/4gx obj打印obj的4段内存,第一段即是isa

    x:4gx.png
    • 用编程计算机看二进制位
    • 可以看到extra_rc0000 0000,它的值是引用计数减1,所以引用计数为0+1=1

    再new一个obj持有看看

    NSObject *obj = [[NSObject alloc]init];
    NSObject *obj1 = obj;
    
    x:4gx1.png extra_rc1.png
    • 可以看到extra_rc变成0000 0001了,引用计数为1+1=2

    下面再来验证一下weak引用

    NSObject *obj = [[NSObject alloc]init];
    __weak NSObject *weakObj = obj;
    
    • weak不会增加引用计数,那么这里obj引用计数还是1,而且weakly_referenced位应该为1。
    x:4gx_weak.png weakly_referenced.png
    • 可以看出weakly_referenced确实变成1了,而引用计数还是0+1=1,符合预期。

    end

    相关文章

      网友评论

        本文标题:iOS weak原理源码探究

        本文链接:https://www.haomeiwen.com/subject/cdgraktx.html