Property中各修飾關鍵字源碼剖析

在Property中有很多關鍵詞來修飾屬性:
strong weak retain copy assign unsafe_unretained
那么光會有不行,還得知道為什么吧?

strong

話不多,上來就干一個試試,我們就拿strong 先開刀.
NSObject.mm 文件中(objc->Source),有這樣一段代碼

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

如果我所料不差,應該就是strong的底層實現(xiàn)了,那么這段代碼應該比較容易理解的了.
首先看方法結構分為兩個參數(shù), id *locationid obj,一個應該是當前對象所在地址,而另一個新指向的對象,隨后進行比較,判斷兩者是否是引用相同的對象.

 if (obj == prev) {
        return;
    }

如果不相同,那么利用 objc_retain()函數(shù)進行引用計數(shù)+1,并且*location = obj;將新引用的對象賦值給當前的屬性,最后利用objc_release()將原本引用的對象釋放掉.
簡單明了一氣呵成,而且我們一般在MRC情況下給成員變量創(chuàng)建set方法的時候貌似也是這樣干的.所以啊還是多看,多練呢.

copy

copy的實現(xiàn)過程在objc->Source->objc-accessors.mm
copy的實現(xiàn)過程并不是單一的函數(shù)就能解決的,需要多個函數(shù)進行操作,我們首先先看看其中牽涉到copy的:

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) {
        newValue = [newValue copyWithZone:nil];
    } else if (mutableCopy) {
        newValue = [newValue mutableCopyWithZone:nil];
    } else {
        if (*slot == newValue) return;
        newValue = objc_retain(newValue);
    }

    if (!atomic) {
        oldValue = *slot;
        *slot = newValue;
    } else {
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;        
        slotlock.unlock();
    }

    objc_release(oldValue);
}

咋一看,差點懷疑人生,仔細一看,不過如此嘛.我們來分析一下.
從函數(shù)名稱分析,嗯,很清晰,看來這是真正的Property Set函數(shù)底層了.
我們找到跟copy相關的

if (copy) {
        newValue = [newValue copyWithZone:nil];
    } else if (mutableCopy) {
        newValue = [newValue mutableCopyWithZone:nil];
    }

嗯,代碼簡單明了,就是我們寫copy和mutableCopy時的判斷嘛,利用對應的copyWithZonemutableCopyWithZone分別實現(xiàn)淺拷貝和深拷貝嘛(內心一陣激動,這么簡單,看來我們都離大神不遠了.)

我們點進去看一下copyWithZhonemutableCopyWithZone的方法實現(xiàn):

+ (id)copyWithZone:(struct _NSZone *)zone {
    return (id)self;
}
+ (id)mutableCopyWithZone:(struct _NSZone *)zone {
    return (id)self;
}

到這里copy基本就結束了,然而事情總不會一帆風順.
于是往下看的時候又發(fā)現(xiàn)了一段相關的代碼:

void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) 
{
    bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
    bool mutableCopy = (shouldCopy == MUTABLE_COPY);
    reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}

嗯,這個函數(shù)名字看來是Property的Set函數(shù)了,跟前面對比看來有點偽.
bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
這段代碼也很簡單,字面就能看懂,copy不能是MUTABLE_COPY,并且確定是個COPY.(想想開發(fā)這個的人員還蠻可愛的.),
bool mutableCopy = (shouldCopy == MUTABLE_COPY)mutableCopy也同理判斷它是個MUTABLE_COPY.

然而到了這里也差不多了,我們幻想的底層實現(xiàn)也差不多就這樣了,于是手一抖,往下又翻了一下,發(fā)現(xiàn)了另一段相關的代碼:

// This entry point was designed wrong.  When used as a getter, src needs to be locked so that
// if simultaneously used for a setter then there would be contention on src.
// So we need two locks - one of which will be contended.
void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong __unused) {
    spinlock_t *srcLock = nil;
    spinlock_t *dstLock = nil;
    if (atomic) {
        srcLock = &StructLocks[src];
        dstLock = &StructLocks[dest];
        spinlock_t::lockTwo(srcLock, dstLock);
    }

    memmove(dest, src, size);

    if (atomic) {
        spinlock_t::unlockTwo(srcLock, dstLock);
    }
}

void objc_copyCppObjectAtomic(void *dest, const void *src, void (*copyHelper) (void *dest, const void *source)) {
    spinlock_t *srcLock = &CppObjectLocks[src];
    spinlock_t *dstLock = &CppObjectLocks[dest];
    spinlock_t::lockTwo(srcLock, dstLock);

    // let C++ code perform the actual copy.
    copyHelper(dest, src);
    
    spinlock_t::unlockTwo(srcLock, dstLock);
}

嗯,事情果然沒有那么簡單.不過方法上面的注釋也解釋了,這個接口設計出現(xiàn)了問題,對此方法進行了安全性的考慮.

上圖方法中出現(xiàn)的函數(shù)解釋:

void *memmove(void *dest, const void *source, size_t count):
memmove用于從source拷貝count個字符到dest,如果目標區(qū)域和源區(qū)域有重疊的話,memmove能夠保證源串在被覆蓋之前將重疊區(qū)域的字節(jié)拷貝到目標區(qū)域中。

struct spinlock_t:自旋鎖
用來避免競爭條件的一種機制,例如:當一個臨界區(qū)的數(shù)據(jù)在多個函數(shù)之間被調用時,為了保護數(shù)據(jù)不被破壞,可以采用spinlock來保護臨界區(qū)的數(shù)據(jù)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,674評論 1 32
  • 《無人生還》 荒島上的離奇殺人案件,十個人一個接一個的死去,沒人知道兇手是誰,即便知道即將發(fā)生的,還是無法避免。無...
    6Day閱讀 313評論 0 0
  • 不是常常說,心靈和身體都必須有一個在路上么。洗滌心靈就去讀書,就去旅游,那怕是來一場說走就走的短途旅行也是極好的,...
    我想靜靜的閱讀 195評論 0 1
  • 【解題鏈接】:http://ctf4.shiyanbar.com/web/copy/index.php 使用工具:...
    是二千閱讀 762評論 0 1

友情鏈接更多精彩內容