iOS底層探索29、屬性修飾符 & retain/release

一、屬性修飾符

新建一個iOS工程,代碼如下:

@interface ViewController ()

@property (nonatomic, strong) UIImage *iamge_strong;

@property (nonatomic, strong) NSString *str_strong;
@property (nonatomic, copy) NSString *str_copy;

@property (nonatomic, assign) NSInteger num;

@end

1、clang 分析編譯后代碼

clang編譯 ViewController.m文件,如下:

// 聲明
extern "C" unsigned long OBJC_IVAR_$_ViewController$_iamge_strong;
extern "C" unsigned long OBJC_IVAR_$_ViewController$_str_strong;
extern "C" unsigned long OBJC_IVAR_$_ViewController$_str_copy;
extern "C" unsigned long OBJC_IVAR_$_ViewController$_num;
extern "C" unsigned long OBJC_IVAR_$_ViewController$_mulStr_strong;
extern "C" unsigned long OBJC_IVAR_$_ViewController$_mulStr_copy;
struct ViewController_IMPL {
    struct UIViewController_IMPL UIViewController_IVARS;
    UIImage *_iamge_strong;
    NSString *_str_strong;
    NSString *_str_copy;
    NSInteger _num;
    NSMutableString *_mulStr_strong;
    NSMutableString *_mulStr_copy;
};

/* @end */
// @interface ViewController ()

// @property (nonatomic, strong) UIImage *iamge_strong;
// @property (nonatomic, strong) NSString *str_strong;
// @property (nonatomic, copy) NSString *str_copy;
// @property (nonatomic, assign) NSInteger num;
// @property (nonatomic, strong) NSMutableString *mulStr_strong;
// @property (nonatomic, copy) NSMutableString *mulStr_copy;

/* @end */

// @implementation ViewController
// 實現(xiàn)

// iamge_strong 變量
// getter 
static UIImage * _I_ViewController_iamge_strong(ViewController * self, SEL _cmd) { return (*(UIImage **)((char *)self + OBJC_IVAR_$_ViewController$_iamge_strong)); }
// setter --> 通過內(nèi)存平移取賦值
static void _I_ViewController_setIamge_strong_(ViewController * self, SEL _cmd, UIImage *iamge_strong) { (*(UIImage **)((char *)self + OBJC_IVAR_$_ViewController$_iamge_strong)) = iamge_strong; }

// 不可變字符串 str_strong 
// getter
static NSString * _I_ViewController_str_strong(ViewController * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_ViewController$_str_strong)); }
// setter --> 通過內(nèi)存平移取賦值
static void _I_ViewController_setStr_strong_(ViewController * self, SEL _cmd, NSString *str_strong) { (*(NSString **)((char *)self + OBJC_IVAR_$_ViewController$_str_strong)) = str_strong; }

// 不可變字符串 str_copy 變量
// getter
static NSString * _I_ViewController_str_copy(ViewController * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_ViewController$_str_copy)); }
// extern
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
// setter --> objc_setProperty ()
static void _I_ViewController_setStr_copy_(ViewController * self, SEL _cmd, NSString *str_copy) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct ViewController, _str_copy), (id)str_copy, 0, 1); }

static NSInteger _I_ViewController_num(ViewController * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_ViewController$_num)); }
static void _I_ViewController_setNum_(ViewController * self, SEL _cmd, NSInteger num) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_ViewController$_num)) = num; }

// 可變字符串 strong
static NSMutableString * _I_ViewController_mulStr_strong(ViewController * self, SEL _cmd) { return (*(NSMutableString **)((char *)self + OBJC_IVAR_$_ViewController$_mulStr_strong)); }
// setter
static void _I_ViewController_setMulStr_strong_(ViewController * self, SEL _cmd, NSMutableString *mulStr_strong) { (*(NSMutableString **)((char *)self + OBJC_IVAR_$_ViewController$_mulStr_strong)) = mulStr_strong; }
// 可變字符串 copy
static NSMutableString * _I_ViewController_mulStr_copy(ViewController * self, SEL _cmd) { return (*(NSMutableString **)((char *)self + OBJC_IVAR_$_ViewController$_mulStr_copy)); }
//  setter  --> objc_setProperty()
static void _I_ViewController_setMulStr_copy_(ViewController * self, SEL _cmd, NSMutableString *mulStr_copy) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct ViewController, _mulStr_copy), (id)mulStr_copy, 0, 1); }
// @end

extern "C" unsigned long int OBJC_IVAR_$_ViewController$_iamge_strong __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct ViewController, _iamge_strong);
extern "C" unsigned long int OBJC_IVAR_$_ViewController$_str_strong __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct ViewController, _str_strong);
extern "C" unsigned long int OBJC_IVAR_$_ViewController$_str_copy __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct ViewController, _str_copy);
extern "C" unsigned long int OBJC_IVAR_$_ViewController$_num __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct ViewController, _num);
extern "C" unsigned long int OBJC_IVAR_$_ViewController$_mulStr_strong __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct ViewController, _mulStr_strong);
extern "C" unsigned long int OBJC_IVAR_$_ViewController$_mulStr_copy __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct ViewController, _mulStr_copy);

static struct /*_ivar_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count;
    struct _ivar_t ivar_list[6];
} _OBJC_$_INSTANCE_VARIABLES_ViewController __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_ivar_t),
    6,
    {{(unsigned long int *)&OBJC_IVAR_$_ViewController$_iamge_strong, "_iamge_strong", "@\"UIImage\"", 3, 8},
     {(unsigned long int *)&OBJC_IVAR_$_ViewController$_str_strong, "_str_strong", "@\"NSString\"", 3, 8},
     {(unsigned long int *)&OBJC_IVAR_$_ViewController$_str_copy, "_str_copy", "@\"NSString\"", 3, 8},
     {(unsigned long int *)&OBJC_IVAR_$_ViewController$_num, "_num", "q", 3, 8},
     {(unsigned long int *)&OBJC_IVAR_$_ViewController$_mulStr_strong, "_mulStr_strong", "@\"NSMutableString\"", 3, 8},
     {(unsigned long int *)&OBJC_IVAR_$_ViewController$_mulStr_copy, "_mulStr_copy", "@\"NSMutableString\"", 3, 8}}
};

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[13];
} _OBJC_$_INSTANCE_METHODS_ViewController __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    13,
    {{(struct objc_selector *)"viewDidLoad", "v16@0:8", (void *)_I_ViewController_viewDidLoad},
    {(struct objc_selector *)"iamge_strong", "@16@0:8", (void *)_I_ViewController_iamge_strong},
    {(struct objc_selector *)"setIamge_strong:", "v24@0:8@16", (void *)_I_ViewController_setIamge_strong_},
    {(struct objc_selector *)"str_strong", "@16@0:8", (void *)_I_ViewController_str_strong},
    {(struct objc_selector *)"setStr_strong:", "v24@0:8@16", (void *)_I_ViewController_setStr_strong_},
    {(struct objc_selector *)"str_copy", "@16@0:8", (void *)_I_ViewController_str_copy},
    {(struct objc_selector *)"setStr_copy:", "v24@0:8@16", (void *)_I_ViewController_setStr_copy_},
    {(struct objc_selector *)"num", "q16@0:8", (void *)_I_ViewController_num},
    {(struct objc_selector *)"setNum:", "v24@0:8q16", (void *)_I_ViewController_setNum_},
    {(struct objc_selector *)"mulStr_strong", "@16@0:8", (void *)_I_ViewController_mulStr_strong},
    {(struct objc_selector *)"setMulStr_strong:", "v24@0:8@16", (void *)_I_ViewController_setMulStr_strong_},
    {(struct objc_selector *)"mulStr_copy", "@16@0:8", (void *)_I_ViewController_mulStr_copy},
    {(struct objc_selector *)"setMulStr_copy:", "v24@0:8@16", (void *)_I_ViewController_setMulStr_copy_}}
};

由上代碼可知:

  1. copy 修飾的對象,其 setter是通過objc_setProperty()來執(zhí)行的;
  2. 而針對非copy修飾的對象,setter 是通過地址偏移來操作的.
  • objc_setProperty (self, _cmd, OFFSETOFIVAR(struct ViewController, _str_copy), (id)str_copy, 0, 1);
  • objc_setProperty (self, _cmd, OFFSETOFIVAR(struct ViewController, _mulStr_copy), (id)mulStr_copy, 0, 1);

2、objc_setProperty() 源碼分析

objc_setProperty() 源碼:

void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) 
{
    // MUTABLE_COPY 2
    
    // copy 修飾:shouldCopy = 1 --> copy = 1; mutableCopy = 0(1!=2)
    bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
    bool mutableCopy = (shouldCopy == MUTABLE_COPY);
    reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}

reallySetProperty()源碼:

static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
    if (offset == 0) {
        object_setClass(self, newValue);
        return;
    }

    id oldValue;
    id *slot = (id*) ((char*)self + offset);// 拿到舊值

    if (copy) {// copy
        // copyWithZone: 返回不可變
        // The copy returned is immutable if the consideration “immutable vs. mutable” applies to the receiving object;
        newValue = [newValue copyWithZone:nil];
    } else if (mutableCopy) {
        // mutableCopyWithZone: 無論原始是否可變 copy 返回的都是可變
        // The returned object is implicitly retained by the sender, which is responsible for releasing it. The copy returned is mutable whether the original is mutable or not.
        newValue = [newValue mutableCopyWithZone:nil];
    } else {
        if (*slot == newValue) return;
        newValue = objc_retain(newValue);// objc_retain() --> rootRetain()
    }

    if (!atomic) {
        // 非原子,簡單的賦值操作 --> 舊值換新值
        oldValue = *slot;
        *slot = newValue;
    } else {
        // 原子操作 --> 加 自旋鎖 lock --> 舊值換新值 --> 解鎖
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;        
        slotlock.unlock();
    }
    // 釋放舊值
    objc_release(oldValue);
}

二、對象的retainrelease

1、retain

rootRetain():

/// <#Description#>
/// @param tryRetain <#tryRetain description#>
/// @param handleOverflow <#handleOverflow description#>
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)) {// class
            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;
        // 引用計數(shù)值++: extra_rc++
        newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++

        // 引用計數(shù) extra_rc 溢出
        if (slowpath(carry)) {
            // newisa.extra_rc++ overflowed
            if (!handleOverflow) {
                // 處理溢出
                ClearExclusive(&isa.bits);
                // rootRetain_overflow(tryRetain) --> rootRetain(tryRetain, true)
                return rootRetain_overflow(tryRetain);
            }
            
            // 保留一半的計數(shù),并準備將另一半復制到 side table
            // 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;// 0x00000080 = 128 留一半
            newisa.has_sidetable_rc = true;// isa 的 has_sidetable_rc 標記為 true
        }
    } while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
    // StoreExclusive() 原子交換函數(shù): old 和 new

    if (slowpath(transcribeToSideTable)) {
        // Copy the other half of the retain counts to the side table.
        // 將另一半引用計數(shù)copy 到side table
        sidetable_addExtraRC_nolock(RC_HALF);
    }

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

將另一半引用計數(shù)copy 到side table
--> sidetable_addExtraRC_nolock(RC_HALF) :

// Move some retain counts to the side table from the isa field.
// Returns true if the object is now pinned.
bool 
objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
{
    ASSERT(isa.nonpointer);
    // 拿到 SideTable
    SideTable& table = SideTables()[this];

    // 舊的引用計數(shù)
    size_t& refcntStorage = table.refcnts[this];
    size_t oldRefcnt = refcntStorage;
    // isa-side bits should not be set here
    ASSERT((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
    ASSERT((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);

    // oldRefcnt & 1<<63
    if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true;

    uintptr_t carry;
    // 引用計數(shù) + 
    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;
    }
}

2、release

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)) {// class
            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;
        // 引用計數(shù)值--:extra_rc--
        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:
    // newisa.extra_rc-- underflowed: borrow from side table or deallocate

    // abandon newisa to undo the decrement
    newisa = oldisa;

    if (slowpath(newisa.has_sidetable_rc)) {// extra_rc溢出使用了 side table
        if (!handleUnderflow) {
            ClearExclusive(&isa.bits);
            // rootRelease(performDealloc, true)
            return rootRelease_underflow(performDealloc);
        }

        // Transfer retain count from side table to inline storage.

        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;// 之后開始向下走
        }

        // 嘗試從 side table 中刪除一些引用計數(shù)
        // Try to remove some retain counts from the side table.        
        size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);

        // To avoid races, has_sidetable_rc must remain set 
        // even if the side table count is now zero.

        if (borrowed > 0) {// Side table 中的引用計數(shù)減少
            // Side table retain count decreased.
            // Try to add them to the inline count.
            // 嘗試將它們添加到 extra_rc 計數(shù)中
            newisa.extra_rc = borrowed - 1;  // redo the original decrement too // 重做原來的遞減
            // 原子交換函數(shù) __c11_atomic_compare_exchange_weak
            bool stored = StoreReleaseExclusive(&isa.bits, 
                                                oldisa.bits, newisa.bits);
            if (!stored) {
                // 添加到 extra_rc 計數(shù)中失敗
                // 立馬再試一次,這可以防止在LL/SC架構(gòu)上的livelock,在那里 side table 訪問本身可能已經(jīng)刪除了保留
                // Inline update failed. 
                // Try it again right now. This prevents livelock on LL/SC 
                // architectures where the side table access itself may have 
                // dropped the reservation.
                isa_t oldisa2 = LoadExclusive(&isa.bits);
                isa_t newisa2 = oldisa2;
                if (newisa2.nonpointer) {
                    uintptr_t overflow;
                    // extra_rc + 操作
                    newisa2.bits = 
                        addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
                    if (!overflow) {
                        // 可以添加到 extra_rc 計數(shù)
                        stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits, 
                                                       newisa2.bits);
                    }
                }
            }

            if (!stored) {
                // 添加到 extra_rc 失敗
                // Inline update failed.
                // Put the retains back in the side table.
                sidetable_addExtraRC_nolock(borrowed);
                goto retry;
            }

            // Decrement successful after borrowing from side table.
            // This decrement cannot be the deallocating decrement - the side 
            // table lock and has_sidetable_rc bit ensure that if everyone 
            // else tried to -release while we worked, the last one would block.
            sidetable_unlock();
            return false;
        }
        else {
            // Side table is empty after all. Fall-through to the dealloc path.
        }
    }

    // 釋放對象 dealloc
    // Really deallocate.

    if (slowpath(newisa.deallocating)) {
        ClearExclusive(&isa.bits);
        if (sideTableLocked) sidetable_unlock();
        return overrelease_error();
        // does not actually return
    }
    newisa.deallocating = true;
    if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;

    if (slowpath(sideTableLocked)) sidetable_unlock();

    __c11_atomic_thread_fence(__ATOMIC_ACQUIRE);

    if (performDealloc) {
        ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
    }
    return true;
}

sidetable_subExtraRC_nolock()

// Move some retain counts from the side table to the isa field.
// Returns the actual count subtracted, which may be less than the request.
size_t 
objc_object::sidetable_subExtraRC_nolock(size_t delta_rc)
{
    ASSERT(isa.nonpointer);
    // 獲取散列表
    SideTable& table = SideTables()[this];

    // 從 RefcountMap 找到當前對象
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it == table.refcnts.end()  ||  it->second == 0) {
        // Side table retain count is zero. Can't borrow.
        return 0;
    }
    size_t oldRefcnt = it->second;// 舊的引用計數(shù)

    // isa-side bits should not be set here
    ASSERT((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
    ASSERT((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);

    // 原引用計數(shù) -  操作,和加操作對應
    size_t newRefcnt = oldRefcnt - (delta_rc << SIDE_TABLE_RC_SHIFT);
    ASSERT(oldRefcnt > newRefcnt);  // shouldn't underflow
    it->second = newRefcnt;
    return delta_rc;
}

引用計數(shù)的總結(jié):

  • retain - 引用計數(shù)加
  1. 對象的isa bitsFieldextra_rc++
  2. 若,extra_rc要溢出了,則拿出一半存到side table中,isahas_sidetable_rc標記為 true;
  • release - 引用計數(shù)減
  1. 對象的isa bitsFieldextra_rc--;
  2. 如果使用了 side table,嘗試從 side table 中刪除一些引用計數(shù);
    2.1. Side table 中的引用計數(shù)減少,嘗試將它們添加到 extra_rc 計數(shù)中;
    不成功則放回 side Tbale.
    *2.1. 為了避免沖突,has_sidetable_rc必須保持設置,即使現(xiàn)在side tablecount是零。
  3. 如果對象引用計數(shù)要釋放完了,則調(diào) dealloc釋放對象.

以上。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容