美文网首页
Objective-C 对象的内存管理-变量修饰符

Objective-C 对象的内存管理-变量修饰符

作者: _涼城 | 来源:发表于2022-04-05 18:54 被阅读0次

    __strong 修饰符

         __strong 修饰符是 id 类型和对象类型默认的所有权修饰符。__strong 修饰符表示对对象的强引用。当前对象被其他对象引用时,会执行 retain 操作,引用计数器+1。当 retainCount = 0 时,该对象才会被销毁。由于所有权默认类型为 __strong 修饰符,所以不需要写上 __strong

     id __strong obj = [NSObject alloc]init];
    

    循环引用

         如果仅通过 __strong 修饰符不能解决引用计数式内存管理必然会发生的循环引用问题。循环引用容易发生内存泄露。所谓内存泄露就是应当废弃的对象在超出其生命周期后继续存在。

    循环引用

    __weak 修饰符

        怎么样才能避免循环引用呢?使用 __weak 修饰符可以避免循环引用。__weak 修饰符不持有对象,在超出其变量作用域时,对象即被释放。并且在对象被废弃时,弱引用自动失效且处于 nil 被赋值的状态。

    __weak 修饰符的实现

    /*编译器模拟代码*/
    id obj1;
    objc_initWeak(&obj1,obj);
    objc_destoryWeak(&obj1);
    

    objc_initWeak

        在 objc 源码中,通过 objc_initWeak 函数初始化附有 __weak 修饰符的变量,源码如下:

     * __weak id weakPtr;
     * (The non-nil case) 
     * 
     * 
     * This function IS NOT thread-safe with respect to concurrent 
     * modifications to the weak variable. (Concurrent weak clear is safe.)
     *
     * @param location Address of __weak ptr. 
     * @param newObj Object ptr. 
     */
    id
    objc_initWeak(id *location, id newObj)
    {
        if (!newObj) {
            *location = nil;
            return nil;
        }
    
        return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
            (location, (objc_object*)newObj);
    }
    

    storeWeak

    可以看到 objc_initWeak 函数的注释表明传入参数实际上是 ptr 指针 和 o 对象 调用 storeWeak 函数,源码如下:

    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) {
            oldObj = *location;
            oldTable = &SideTables()[oldObj];
        } else {
            oldTable = nil;
        }
        if (haveNew) {
            newTable = &SideTables()[newObj];
        } else {
            newTable = nil;
        }
        //先上锁
        SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
    
        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()) 
            {
                SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
                class_initialize(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 
                // not yet initialized to the check above.
                // Instead set previouslyInitializedClass to recognize it on retry.
                previouslyInitializedClass = cls;
    
                goto retry;
            }
        }
    
        // Clean up old value, if any.
        if (haveOld) {
            // 如果weak指针之前指向了一个弱引用,则会调用weak_unregister_no_lock方法将旧的weak指针地址移除
            weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
        }
    
        // Assign new value, if any.
        if (haveNew) {
            newObj = (objc_object *)
                weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                      crashIfDeallocating);
            // weak_register_no_lock returns nil if weak store should be rejected
    
            // Set is-weakly-referenced bit in refcount table.
            if (newObj  &&  !newObj->isTaggedPointer()) {
                newObj->setWeaklyReferenced_nolock();
            }
    
            // Do not set *location anywhere else. That would introduce a race.
            *location = (id)newObj;
        }
        else {
            // No new value. The storage is not changed.
        }
        
        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
    
        return (id)newObj;
    }
    
    

    storeWeak 函数中的流程如下:

    1. 首先获取新旧引用散列表 oldTablenewTable,并加锁
    2. 如果指针当前有指向旧对象,则调用 weak_unregister_no_lock函数
    3. 如果指针要指向的新对象存在,则调用 weak_register_no_lock函数,并对新对象设置相关弱引用标志位 setWeaklyReferenced_nolock

    weak_unregister_no_lock

    查看 weak_unregister_no_lock 函数源码如下:

        /** 
     * Unregister an already-registered weak reference.
     * This is used when referrer's storage is about to go away, but referent
     * isn't dead yet. (Otherwise, zeroing referrer later would be a
     * bad memory access.)
     * Does nothing if referent/referrer is not a currently active weak reference.
     * Does not zero referrer.
     * 
     * FIXME currently requires old referent value to be passed in (lame)
     * FIXME unregistration should be automatic if referrer is collected
     * 
     * @param weak_table The global weak table.
     * @param referent The object.
     * @param referrer The weak reference.
     */
    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))) {
            remove_referrer(entry, referrer);
            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_remove(weak_table, entry);
            }
        }
    
        // Do not set *referrer = nil. objc_storeWeak() requires that the 
        // value not change.
    }
    
    

    通过上面源码得知 weak_unregister_no_lock 的流程如下:

    1. 根据传入对象 referent_id 作为 key,通过 weak_entry_for_referent 在传入的弱引用表(weak_table)中寻找对应 weak_entry_t 类型的 value,赋值给 entryentry中保存的是一个对象的弱引用信息;

    2. 找到 entry 后,在 entry 中的 inline_referrersreferrers 遍历查找 referrer 并置 nil,referrers 中存放的都是弱引用变量的地址;

    3. 遍历 entryinline_referrers 中是否还有引用,如果无引用则移除 entry

    因此,weak_unregister_no_lock 其实就是删除对应的弱引用地址

    weak_register_no_lock

    查看 weak_register_no_lock 函数源码如下:

    /** 
     * Registers a new (object, weak pointer) pair. Creates a new weak
     * object entry if it does not exist.
     * 
     * @param weak_table The global weak table.
     * @param referent The object pointed to by the weak reference.
     * @param referrer The weak pointer address.
     */
    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;
    
        if (!referent  ||  referent->isTaggedPointer()) return referent_id;
    
        // ensure that the referenced object is viable
        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;
        if ((entry = weak_entry_for_referent(weak_table, referent))) {
            append_referrer(entry, referrer);
        } 
        else {
            weak_entry_t new_entry(referent, referrer);
            weak_grow_maybe(weak_table);
            weak_entry_insert(weak_table, &new_entry);
        }
    
        // Do not set *referrer. objc_storeWeak() requires that the 
        // value not change.
    
        return referent_id;
    }
    
    

    通过上面源码得知 weak_register_no_lock 的流程如下:

    1. 判断对象是否存在或者是一个 Tagged Pointer ,直接返回对象
    2. 根据 deallocating(对象是否正在释放的标志和对象是否支持弱引用)和入参 crashIfDeallocating 判断是否中止程序运行。
    3. weak_table_t 中去找 referent 对应的 weak_entry_t,如果能找到 entry,则调用 append_referrer 函数把对象弱引用的指针 referrer 插入 weak_entry_t 的哈希数组中(或者是定长为 4 的内部数组中)。
    4. 如果没有找到对应的 weak_entry_t,则首先创建一个 new_entry,然后先执行 weak_grow_maybe 扩容,然后调用 weak_entry_insertnew_entry 插入 weak_table_t 的哈希数组中。

    因此,weak_register_no_lock 其实就是保存对应的弱引用地址

    objc_destoryWeak

    在离开作用域后,__weak 修饰的对象会调用 objc_destoryWeak 函数,源码如下:

    void
    objc_destroyWeak(id *location)
    {
        (void)storeWeak<DoHaveOld, DontHaveNew, DontCrashIfDeallocating>
            (location, nil);
    }
    
    

    可以看到 objc_destroyWeak 函数依旧调用 storeWeak 函数,传递的 newObj 参数为 nil,用于清除相应的弱引用指针地址。

    __uunsafe_unretained 修饰符

    __unsafe_unretained 修饰符正如其名 unsafe 所示,是不安全的修饰符。附有 __unsafe_unretained 修饰符的变量不属于编译器的内存管理对象。

    _autoreleasing 修饰符

    __autoreleasing 修饰符 等价于 autorelease 方法。

    相关文章

      网友评论

          本文标题:Objective-C 对象的内存管理-变量修饰符

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