从源码看 iOS property strong

从源码看 iOS property strong

深入理解之前,先熟悉一个概念,Tagged Pointer
http://www.infoq.com/cn/articles/deep-understanding-of-tagged-pointer

strong引用计数加1,我们深入看看,详细的过程

写个Demo

@interface pandaTest : NSObject

@property (nonatomic, strong) pandaTest *testProp;

@end

这里忽略循环引用,只是看看strong的过程。

单步调试

objc-debug`-[pandaTest setTestProp:]:
    0x100001420 <+0>:  pushq  %rbp
    0x100001421 <+1>:  movq   %rsp, %rbp
    0x100001424 <+4>:  subq   $0x20, %rsp
    0x100001428 <+8>:  movq   %rdi, -0x8(%rbp)
    0x10000142c <+12>: movq   %rsi, -0x10(%rbp)
    0x100001430 <+16>: movq   %rdx, -0x18(%rbp)
    0x100001434 <+20>: movq   -0x18(%rbp), %rdx
    0x100001438 <+24>: movq   -0x8(%rbp), %rsi
    0x10000143c <+28>: movq   0x5225(%rip), %rdi        ; pandaTest._testProp
    0x100001443 <+35>: addq   %rdi, %rsi
    0x100001446 <+38>: movq   %rsi, %rdi
    0x100001449 <+41>: movq   %rdx, %rsi
    0x10000144c <+44>: callq  0x100004bc2               ; symbol stub for: objc_storeStrong
    0x100001451 <+49>: addq   $0x20, %rsp
    0x100001455 <+53>: popq   %rbp
    0x100001456 <+54>: retq   

看看内部会走什么流程。symbol stub for: objc_storeStrong我们发现了这个。这里跳到runtime中调用 objc_storeStrong 方法。一步步来。

看看 objc_storeStrong方法的具体实现

void
objc_storeStrong(id *location, id obj)
{
    //取出之前指向的地址
    id prev = *location;
    if (obj == prev) {
        return;
    }
    ///引用计数加1
    objc_retain(obj);
    ///strong修饰的指针指向obj
    *location = obj;
    ///释放前一个对象
    objc_release(prev);
}


这里参数有两个,一个 location 一个obj
此处,location 是我们使用 strong 修饰的指针,obj 是 通过alloc init 产生的对象。

///对齐方式
__attribute__((aligned(16)))
void 
objc_release(id obj)
{
    if (!obj) return;
    if (obj->isTaggedPointer()) return;
    return obj->release();
}

释放函数比较简单,

所以重点就在 objc_retain(obj);方法里面

id 
objc_retain(id obj)
{
    if (!obj) return obj;
    if (obj->isTaggedPointer()) return obj;
    return obj->retain();
}

这里直接调用 retain。

ALWAYS_INLINE 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();
        }
        // 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;
        ///retaincount 加1
        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.
        ///在全局的表中改变retainCount的值
        sidetable_addExtraRC_nolock(RC_HALF);
    }

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

此处,参数有两个,tryRetain,handleOverflow,会有溢出情况?

来看看 isa 指针的定义,

union isa_t 
{
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;

#if SUPPORT_PACKED_ISA

    // extra_rc must be the MSB-most field (so it matches carry/overflow flags)
    // nonpointer must be the LSB (fixme or get rid of it)
    // shiftcls must occupy the same bits that a real class pointer would
    // bits + RC_ONE is equivalent to extra_rc + 1
    // RC_HALF is the high bit of extra_rc (i.e. half of its range)

    // future expansion:
    // uintptr_t fast_rr : 1;     // no r/r overrides
    // uintptr_t lock : 2;        // lock for atomic property, @synch
    // uintptr_t extraBytes : 1;  // allocated with extra bytes

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    struct {
        uintptr_t nonpointer        : 1;
        uintptr_t has_assoc         : 1;
        uintptr_t has_cxx_dtor      : 1;
        uintptr_t shiftcls          : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
        uintptr_t magic             : 6;
        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 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
    struct {
        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)
    };

比如在 x86_64 情况下, extra_rc 就是引用计数的个数标示,只占8位,能表示的大小在 0 - 256 之间,所以,超过 256 自然就溢出了。溢出怎么办呢,runtime 维护了一个表

alignas(StripedMap) static uint8_t 
    SideTableBuf[sizeof(StripedMap)];

static void SideTableInit() {
    new (SideTableBuf) StripedMap();
}

static StripedMap& SideTables() {
    return *reinterpret_cast*>(SideTableBuf);
}

这个表初始化的比较早,程序刚启动就初始化了。调用过程大概如下:

_objc_init
    map_images
        map_images_nolock
            arr_init
                SideTableInit

我们重新回到 rootRetain函数

未完待续

你可能感兴趣的:(从源码看 iOS property strong)