美文网首页
iOS中对象内存管理的探讨

iOS中对象内存管理的探讨

作者: Bel李玉 | 来源:发表于2020-12-05 18:31 被阅读0次

OC对象的alloc过程一文中,我们一起探讨了alloc的过程,接下来我们就对对象的retain、release、dealloc过程进行探讨。

retain

首先我们看下 rootRetain的源码实现

objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
    if (isTaggedPointer()) return (id)this;

    bool sideTableLocked = false;
    bool transcribeToSideTable = false;

    isa_t oldisa;
    isa_t newisa;

    do {
        transcribeToSideTable = false;
        oldisa = LoadExclusive(&isa.bits);
        newisa = oldisa;
        if (slowpath(!newisa.nonpointer)) {
            ClearExclusive(&isa.bits);
            if (rawISA()->isMetaClass()) return (id)this;
            if (!tryRetain && sideTableLocked) sidetable_unlock();
            if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
            else return sidetable_retain();
        }
        // don't check newisa.fast_rr; we already called any RR overrides
        if (slowpath(tryRetain && newisa.deallocating)) {
            ClearExclusive(&isa.bits);
            if (!tryRetain && sideTableLocked) sidetable_unlock();
            return nil;
        }
        uintptr_t carry;
        newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++

        if (slowpath(carry)) {
            // newisa.extra_rc++ overflowed
            if (!handleOverflow) {
                ClearExclusive(&isa.bits);
                return rootRetain_overflow(tryRetain);
            }
            // Leave half of the retain counts inline and 
            // prepare to copy the other half to the side table.
            if (!tryRetain && !sideTableLocked) sidetable_lock();
            sideTableLocked = true;
            transcribeToSideTable = true;
            newisa.extra_rc = RC_HALF;
            newisa.has_sidetable_rc = true;
        }
    } while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));

    if (slowpath(transcribeToSideTable)) {
        // Copy the other half of the retain counts to the side table.
        sidetable_addExtraRC_nolock(RC_HALF);
    }

    if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
    return (id)this;
}

首先,会判断当前 是否为 taggedPointer,如果是就直接返回。然后会操作散列表(SideTable),在 sidetable_unlock方法中

void 
objc_object::sidetable_unlock()
{
    SideTable& table = SideTables()[this];
    table.unlock();
}

SideTablesMap中以thiskey得到该散列表,在内存中有多个散列表,是通过Map进行存放的,一共有 64张散列表。为什么会有多张散列表呢?因为我们对引用计数进行管理时,都需要解锁上锁,性能不好。
SideTable结构体中

struct SideTable {
    spinlock_t slock;
    RefcountMap refcnts;
    weak_table_t weak_table;

    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }

    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }

    void lock() { slock.lock(); }
    void unlock() { slock.unlock(); }
    void forceReset() { slock.forceReset(); }

    // Address-ordered lock discipline for a pair of side tables.

    template<HaveOld, HaveNew>
    static void lockTwo(SideTable *lock1, SideTable *lock2);
    template<HaveOld, HaveNew>
    static void unlockTwo(SideTable *lock1, SideTable *lock2);
};

有一把锁 spinlock_t,存放引用对象的引用计数表RefcountMap,和一张弱引用表weak_table_t

第二步判断当前对象是否在deallocing状态,如果是正在dealloc,流程就进入到了 dealloc阶段,dealloc的过程在文章的下个阶段有介绍。
第三步,对isa的bits进行处理,对 extra_rc进行 +1,如果 extra_rc满了,就操作散列表。将满状态extra_rc的一半移到散列表中,将 has_sidetable_rc状态位设置为 true,表示现在依赖散列表了。

release

release 的过程和retain的过程类似,retain是对extra_rc进行相加,release是对extra_rc相减。

objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
    if (isTaggedPointer()) return false;

    bool sideTableLocked = false;

    isa_t oldisa;
    isa_t newisa;

 retry:
    do {
        oldisa = LoadExclusive(&isa.bits);
        newisa = oldisa;
        if (slowpath(!newisa.nonpointer)) {
            ClearExclusive(&isa.bits);
            if (rawISA()->isMetaClass()) return false;
            if (sideTableLocked) sidetable_unlock();
            return sidetable_release(performDealloc);
        }
        // don't check newisa.fast_rr; we already called any RR overrides
        uintptr_t carry;
        newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc--
        if (slowpath(carry)) {
            // don't ClearExclusive()
            goto underflow;
        }
    } while (slowpath(!StoreReleaseExclusive(&isa.bits, 
                                             oldisa.bits, newisa.bits)));

    if (slowpath(sideTableLocked)) sidetable_unlock();
    return false;
underflow:
   ......
}

extra_rc进行减法运算使用的是 subc函数,如果extra_rc为0(empty)了,就会在散列表里面进行查找减1。当extra_rc为空且散列表里面的也为空,则将 deallocing 设置为 true,对该对象进行析构(dealloc)

root_dealloc

其源码实现如下

inline void
objc_object::rootDealloc()
{
    if (isTaggedPointer()) return;  // fixme necessary?

    if (fastpath(isa.nonpointer  &&  
                 !isa.weakly_referenced  &&  
                 !isa.has_assoc  &&  
                 !isa.has_cxx_dtor  &&  
                 !isa.has_sidetable_rc))
    {
        assert(!sidetable_present());
        free(this);
    } 
    else {
        object_dispose((id)this);
    }
}

对象在释放的时候,要 清除对象的关联对象,调用其cxx析构函数,清空弱引用表,清空散列表里面的引用计数表,最后进行free
objc_destructInstance方法中,如果有弱引用(weak_reference)会将其弱引用表进行清空(erase)。如果有has_sidetable_rc,将其存放引用计数的sidetable清空。

相关文章

  • iOS中对象内存管理的探讨

    在OC对象的alloc过程[https://www.jianshu.com/p/83bd92067e79]一文中,...

  • iOS之从MRC到ARC内存管理详解

    概述 在iOS中开发中,我们或多或少都听说过内存管理。iOS的内存管理一般指的是OC对象的内存管理,因为OC对象分...

  • 内存管理:不看白不看,看了就是赚

    一、iOS的内存管理方式 1、小对象的内存管理 -- Tagged Pointer 2、普通对象的内存管理 -- ...

  • iOS的内存管理

    iOS OC对象的内存管理 在iOS中,使用引用计数来管理OC对象内存 一个新创建的OC对象引用计数默认是1,当引...

  • iOS 中堆和栈的区别以及冒泡排序

    在引入堆和栈之前,先要知道,iOS中的内存管理范围: OC对象需要进行内存管理,非oc对象不需要进行内存管理,比如...

  • iOS中的内存管理

    iOS中的内存管理 内存管理的思考方式 自己生成的对象,自己持有 非自己生成的对象,自己也能持有 不再需要自己持有...

  • 内存管理 之 MRC、ARC(待完善)

    1、概述 iOS的内存管理是指OC对象的内存管理。在程序运行中,创建对象、定义变量、调用函数这些操作都会增加程序的...

  • iOS内存管理

    iOS内存管理机制 基本数据类型无需我们管理,内存管理的范围是对象,iOS内存管理采用了引用计数器的方式来管理内存...

  • iOS 内存管理

    内存管理的原理 iOS 内存管理,是基于引用计数来管理内存;当对象引用计数为0时,对象将被销毁,回收内存空间;内存...

  • 内存管理面试题

    讲一下 iOS 内存管理的理解 在iOS中,使用引用计数来管理OC对象的内存 一个新创建的OC对象引用计数默认是1...

网友评论

      本文标题:iOS中对象内存管理的探讨

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