在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 *location 和 id 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時的判斷嘛,利用對應的copyWithZone和mutableCopyWithZone分別實現(xiàn)淺拷貝和深拷貝嘛(內心一陣激動,這么簡單,看來我們都離大神不遠了.)
我們點進去看一下copyWithZhone和mutableCopyWithZone的方法實現(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ù)