在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
的底层实现了,那么这段代码应该比较容易理解的了.
首先看方法结构分为两个参数, id *location
和 id obj
,一个应该是当前对象所在地址,而另一个新指向的对象,随后进行比较,判断两者是否是引用相同的对象.
if (obj == prev) {
return;
}
如果不相同,那么利用 objc_retain()
函数进行引用计数+1,并且*location = obj;
将新引用的对象赋值给当前的属性,最后利用objc_release()
将原本引用的对象释放掉.
简单明了一气呵成,而且我们一般在MRC情况下给成员变量创建set方法的时候貌似也是这样干的.所以啊还是多看,多练呢.
copy
copy的实现过程在objc->Source->objc-accessors.mm
copy的实现过程并不是单一的函数就能解决的,需要多个函数进行操作,我们首先先看看其中牵涉到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);
}
咋一看,差点怀疑人生,仔细一看,不过如此嘛.我们来分析一下.
从函数名称分析,嗯,很清晰,看来这是真正的Property Set函数底层了.
我们找到跟copy相关的
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
}
嗯,代码简单明了,就是我们写copy和mutableCopy时的判断嘛,利用对应的copyWithZone
和mutableCopyWithZone
分别实现浅拷贝和深拷贝嘛(内心一阵激动,这么简单,看来我们都离大神不远了.)
到这里copy基本就结束了,然而事情总不会一帆风顺.
于是往下看的时候又发现了一段相关的代码:
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);
}
嗯,这个函数名字看来是Property的Set函数了,跟前面对比看来有点伪.
bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
这段代码也很简单,字面就能看懂,copy不能是MUTABLE_COPY,并且确定是个COPY.(想想开发这个的人员还蛮可爱的.),
bool mutableCopy = (shouldCopy == MUTABLE_COPY)
mutableCopy也同理判断它是个MUTABLE_COPY.
然而到了这里也差不多了,我们幻想的底层实现也差不多就这样了,于是手一抖,往下又翻了一下,发现了另一段相关的代码:
// 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);
}
嗯,事情果然没有那么简单.不过方法上面的注释也解释了,这个接口设计出现了问题,对此方法进行了安全性的考虑.
上图方法中出现的函数解释:
void *memmove(void *dest, const void *source, size_t count):
memmove用于从source拷贝count个字符到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。
struct spinlock_t:自旋锁
用来避免竞争条件的一种机制,例如:当一个临界区的数据在多个函数之间被调用时,为了保护数据不被破坏,可以采用spinlock来保护临界区的数据
网友评论