性能优化程序员iOS

alloc、retain、release源码解析

2019-04-11  本文已影响22人  聪莞

在MRC时代,系统判定一个对象是否销毁是根据这个对象的引用计数器来判断的。我们要知道一个对象在内存中内存是如何被管理的,还是需要了解一下MRC。设置Build Setting中的Objective-C Automatic Reference Counting为NO,开启手动管理内存。

引用计数存放位置

开始本篇源码分析前,我们需要了解引用计数是如何存放的,在上一篇NONPOINTER_ISA和散列表中,我们有说到,NONPOINTER_ISA里面有一位extra_rc用来表示该对象的引用计数,并且当引用计数大于10时,则需要额外再用到sidetable_rc,即散列表里,sideTable里有一个RefcountMap来存放引用计数。

ReatinCount源码解析

从一个问题来开始:看如下代码:

NSObject * obj = [NSObject alloc];
NSLog(@"obj.retainCount = %ld",obj.retainCount);

obj的引用计数是多少?NSLog会输出几?想回答这个问题,我们可以来分析一下retainCount的源码:

- (NSUInteger)retainCount {
    return ((id)self)->rootRetainCount();
}
inline uintptr_t 
objc_object::rootRetainCount()
{
    if (isTaggedPointer()) return (uintptr_t)this;  //taggedPointer没有引用计数这个说法

    sidetable_lock();
    isa_t bits = LoadExclusive(&isa.bits);
    ClearExclusive(&isa.bits);
    if (bits.nonpointer) {    //判断是nonPointer类型的isa的话
        uintptr_t rc = 1 + bits.extra_rc;    //先从extra_rc中获取
        if (bits.has_sidetable_rc) {    //如果sideTable也有的话
            rc += sidetable_getExtraRC_nolock();    //加上从sideTable中获取的
        }
        sidetable_unlock();
        return rc;
    }

    sidetable_unlock();
    return sidetable_retainCount();
}

可以看到,在从extra_rc中获取引用计数时,做了个+1的操作,即retainCount返回的值是1 + 计数表总计。
运行来看一下Log信息:

2019-04-11 10:13:50.014200+0800 debug-objc[24348:3063630] obj.retainCount = 1

所以,可以得出结论,alloc的对象其真实的引用计数是0,而retainCount为1。

Alloc

走进alloc的源码:

+ (id)alloc {
    return _objc_rootAlloc(self);
}
id _objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
static ALWAYS_INLINE id callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
    if (slowpath(checkNil && !cls)) return nil;    //异常判断

    if (fastpath(!cls->ISA()->hasCustomAWZ())) {    //是否有自定义的AllocWithZone,有的话就跳过系统的alloc
        if (fastpath(cls->canAllocFast())) {    //是否可以AllocFast
        //** 来看一下canAllocFast
            bool canAllocFast() {
                assert(!isFuture());
                return bits.canAllocFast();
            }
            bool canAllocFast() {
                return false;  //居然直接返回false,what a f**k! 所以我们可以直接去看else里的代码
            }
        */
            bool dtor = cls->hasCxxDtor();
            id obj = (id)calloc(1, cls->bits.fastInstanceSize());
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            obj->initInstanceIsa(cls, dtor);
            return obj;
        }
        else {
            id obj = class_createInstance(cls, 0);    //调用创建对象
            if (slowpath(!obj)) return callBadAllocHandler(cls);  //一些错误处理
            return obj;
        }
    }
    if (allocWithZone) return [cls allocWithZone:nil];
    return [cls alloc];

    //跳入class_createInstance
    id 
    class_createInstance(Class cls, size_t extraBytes)
    {
        return _class_createInstanceFromZone(cls, extraBytes, nil);
    }

  static __attribute__((always_inline))  id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, 
                              bool cxxConstruct = true, 
                              size_t *outAllocatedSize = nil)
{
    if (!cls) return nil;

    assert(cls->isRealized());

    //判断有c++的构造和析构
    bool hasCxxCtor = cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    //判断是否可以创建Nonpointer类型
    bool fast = cls->canAllocNonpointer();

    //获取需要分配的size
    size_t size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (!zone  &&  fast) {    //zone传进来是nil 而fast一般也都是真 所以几乎都会进这里
        obj = (id)calloc(1, size);      //开辟内存
        if (!obj) return nil;
        obj->initInstanceIsa(cls, hasCxxDtor);    //初始化
    } 
    else {
        if (zone) {
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else {
            obj = (id)calloc(1, size);
        }
        if (!obj) return nil;

        obj->initIsa(cls);
    }

    if (cxxConstruct && hasCxxCtor) {
        obj = _objc_constructOrFree(obj, cls);
    }

    return obj;
}
 }

为了避免贴的代码太长,这里做一下分隔,接着跳入initInstanceIsa源码:

inline void  objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    assert(!cls->instancesRequireRawIsa());
    assert(hasCxxDtor == cls->hasCxxDtor());

    initIsa(cls, true, hasCxxDtor);
}
inline void  objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    assert(!isTaggedPointer());     //taggedPointer判断
    
    if (!nonpointer) {    //不是nonpointer的话,直接将cls赋给isa.cls,这里的isa就只是一个单纯的指针
        isa.cls = cls;
    } else {    //一般进入else
        assert(!DisableNonpointerIsa);
        assert(!cls->instancesRequireRawIsa());

        isa_t newisa(0);    //初始化一个所有位为 0 的指针 isa_t

/**
 __ARM_ARCH_7K__表示的应该是Apple Watch 相关的一些 所以iOS中SUPPORT_INDEXED_ISA会是0
#if __ARM_ARCH_7K__ >= 2  ||  (__arm64__ && !__LP64__)
#   define SUPPORT_INDEXED_ISA 1
#else
#   define SUPPORT_INDEXED_ISA 0
#endif
*/

#if SUPPORT_INDEXED_ISA  
        assert(cls->classArrayIndex() > 0);
        ISA_MAGIC_VALUE
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE

        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else      //iOS中会走到这里
        //isa_t的bits字段初始化为ISA_MAGIC_VALUE,ISA_MAGIC_VALUE是一个16进制的值,将其转换为二进制后,会发现ISA_MAGIC_VALUE是对nonpointer和magic做初始化。(见下一段代码)
        newisa.bits = ISA_MAGIC_VALUE;

        然后将是否有 C++ 析构函数标记
        newisa.has_cxx_dtor = hasCxxDtor;

        最后将位移(shift)后的 cls 存入 shiftcls(cls右移3位存到shiftcls中,从isa_t的结构体中也可以看到低3位都是用来存储其他信息的(nonpointer、has_assoc、has_cxx_dtor),既然可以右移三位,那就代表类地址的低三位全部都是0,否则就出错了,补0的作用应该是为了字节对齐。)。
        newisa.shiftcls = (uintptr_t)cls >> 3; 
#endif
        isa = newisa;
    }
}

我们来看一下ISA_MAGIC_VALUE对isa的初始化做了什么:

# if __arm64__
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
# elif __x86_64__
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL

将其转为2进制分别如下:


image.png image.png

按照NONPOINTER_ISA和散列表里的每一位的含义去对应上,可以看到其实是对magic 和 nonpointer 进行初始化。

Retain

- (id)retain {
    return ((id)self)->rootRetain();
}
id objc_object::rootRetain()
{
    return rootRetain(false, false);
}
id 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 (!tryRetain && sideTableLocked) sidetable_unlock();
            if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
            else return sidetable_retain();
        }

        if (slowpath(tryRetain && newisa.deallocating)) {
            ClearExclusive(&isa.bits);
            if (!tryRetain && sideTableLocked) sidetable_unlock();
            return nil;
        }
        uintptr_t carry;    //创建一个临时变量,表示是否有溢出

        /**
        #   define RC_ONE   (1ULL<<56) 1向左移56位 然后加进bits 其实就是操作bits位域里的extra_rc++
        */
        newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  

        if (slowpath(carry)) {    //如果发生了溢出,即 > 10,需要用到sideTable
            if (!handleOverflow) {
                ClearExclusive(&isa.bits);     //清理旧值
                return rootRetain_overflow(tryRetain);
            }

            if (!tryRetain && !sideTableLocked) sidetable_lock();
            sideTableLocked = true;      //锁住sideTable
            transcribeToSideTable = true;    //是否需要移动到sideTable
            newisa.extra_rc = RC_HALF;  //新的extra_rc设置为RC_HALF,另外的一半放进sideTable
            newisa.has_sidetable_rc = true;//将是否用到了sideTable置为true
        }
    }
//判断是否处理完成,不完成的话就一直进行bits的处理
 while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));


    if (slowpath(transcribeToSideTable)) {
        // 另外的一半放进sideTable
        sidetable_addExtraRC_nolock(RC_HALF);
    }

   

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

//添加到sideTable
bool 
objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
{
    SideTable& table = SideTables()[this];

     //从sideTable的refCountMap中获取旧值
    size_t& refcntStorage = table.refcnts[this];   
    size_t oldRefcnt = refcntStorage;
    if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true;

    uintptr_t carry;

    /** delta_rc就是传进来的RC_HALF,注意为什么要左移两位,因为refcountMap的前两位不是表示引用计数的,而是分别表示
        是否有弱引用SIDE_TABLE_WEAKLY_REFERENCED,
        是否正在析构SIDE_TABLE_DEALLOCATING
        */
    size_t newRefcnt = 
        addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry);
    if (carry) {    //如果也溢出了,那么就进行了一些类似于扩容似的操作,这里不做探究。
        refcntStorage =
            SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK);
        return true;
    }
    else {
        refcntStorage = newRefcnt;
        return false;
    }
}

总结一下,retain其实就是对引用计数进行了+1操作,如果extra_rc未溢出,那么就直接extra_rc++,如果extra_rc溢出了,就取出一半的值放到sideTable里。

release

- (oneway void)release {
    ((id)self)->rootRelease();
}
ALWAYS_INLINE bool 
objc_object::rootRelease()
{
    return rootRelease(true, false);
}
ALWAYS_INLINE bool 
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 (sideTableLocked) sidetable_unlock();
            return sidetable_release(performDealloc);
        }

        uintptr_t carry;
        //对extra_rc做减操作
        newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc--
        if (slowpath(carry)) {
          //如果减溢出了,说明extra_rc已经减到了0,就走到underflow
            goto underflow;
        }
    } 
//做while循环,直到release处理完成
while (slowpath(!StoreReleaseExclusive(&isa.bits, 
                                             oldisa.bits, newisa.bits)));

    if (slowpath(sideTableLocked)) sidetable_unlock();
    return false;

 underflow:
    newisa = oldisa;

    if (slowpath(newisa.has_sidetable_rc)) {    //如果散列表存在,就操作散列表,否则就开始dealloc
        if (!handleUnderflow) {
            ClearExclusive(&isa.bits);
            return rootRelease_underflow(performDealloc);
        }

        if (!sideTableLocked) {
            ClearExclusive(&isa.bits);
            sidetable_lock();
            sideTableLocked = true;
            // Need to start over to avoid a race against 
            // the nonpointer -> raw pointer transition.
            goto retry;
        } 
        size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);

        if (borrowed > 0) {
            newisa.extra_rc = borrowed - 1;  // redo the original decrement too
            bool stored = StoreReleaseExclusive(&isa.bits, 
                                                oldisa.bits, newisa.bits);
            if (!stored) {
                isa_t oldisa2 = LoadExclusive(&isa.bits);
                isa_t newisa2 = oldisa2;
                if (newisa2.nonpointer) {
                    uintptr_t overflow;
                    newisa2.bits = 
                        addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
                    if (!overflow) {
                        stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits, 
                                                       newisa2.bits);
                    }
                }
            }

            if (!stored) {
                // Inline update failed.
                // Put the retains back in the side table.
                sidetable_addExtraRC_nolock(borrowed);
                goto retry;
            }
            sidetable_unlock();
            return false;
        }
        else {
            // Side table is empty after all. Fall-through to the dealloc path.
        }
    }

    // 开始析构
    if (slowpath(newisa.deallocating)) {
        ClearExclusive(&isa.bits);
        if (sideTableLocked) sidetable_unlock();
        return overrelease_error();
    }
    newisa.deallocating = true;
    if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;

    if (slowpath(sideTableLocked)) sidetable_unlock();

    __sync_synchronize();
    if (performDealloc) {    //发送dealloc方法,所以引用计数到0的时候会走dealloc方法
        ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
    }
    return true;
}

总结一下,release的流程跟retain类似,不过一个是加,一个是减。并且release触发了引用计数为0,那么就直接开始触发dealloc消息。

为什么这里retain和release都是RC_HALF呢,我们知道,retain会先往extra_rc里加,如果溢出再往sideTable里添加,同样,release也是这样,先从extra_rc里减一,然后从sideTable里取值再减一,为了避免多次操作sideTable,因为sideTable有锁,所以取中间值。

strong

strong的源码非常简单,会调用objc_retain来处理新值,调用objc_release释放旧值。

void
objc_storeStrong(id *location, id obj)
{
    id prev = *location;
    if (obj == prev) {
        return;
    }
    objc_retain(obj);
    *location = obj;
    objc_release(prev);
}

property中的unsafe_unretained

property中设置属性时,会调用set方法,runtime中set方法的底层源码是这样的:

static ALWAYS_INLINE 
void _object_setIvar(id obj, Ivar ivar, id value, bool assumeStrong)
{
    if (!obj  ||  !ivar  ||  obj->isTaggedPointer()) return;

    ptrdiff_t offset;
    objc_ivar_memory_management_t memoryManagement;
    _class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement);

    if (memoryManagement == objc_ivar_memoryUnknown) {
        //如果是UnKnow,那么设置为strong
        if (assumeStrong) memoryManagement = objc_ivar_memoryStrong;
        else memoryManagement = objc_ivar_memoryUnretained;
    }

    id *location = (id *)((char *)obj + offset);

    switch (memoryManagement) {
    case objc_ivar_memoryWeak:       objc_storeWeak(location, value); break;
    case objc_ivar_memoryStrong:     objc_storeStrong(location, value); break;
    case objc_ivar_memoryUnretained: *location = value; break;
    case objc_ivar_memoryUnknown:    _objc_fatal("impossible");
    }
}

switch (memoryManagement) 判断中,可以看到,除了unretained,别的都是通过指针传递,而unretained是通过值传递,所以使用unretained会产生内存泄露。

@property (unsafe_unretained,nonatomic) NSObject * obj; //会产生野指针,发生内存泄漏。

上一篇 下一篇

猜你喜欢

热点阅读