一直在使用property修饰符,从未深入研究过,今天抽时间去看看这些属性究竟在是什么意思,
我们先来声明NSString对象,分别用strong、copy、retain 修饰。
@property (nonatomic, strong) NSString *testa
@property (nonatomic, retain) NSString *testb;
@property (nonatomic, copy) NSString *testc;
@property (atomic, copy) NSString *testd;
@property (atomic, strong) NSString *teste;
使用clang -rewrite-objc 命令,看到如下代码
// testa getter
static NSString * _I_ViewController_testa(ViewController * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_ViewController$_testa)); }
// testa setter
static void _I_ViewController_setTesta_(ViewController * self, SEL _cmd, NSString *testa) { (*(NSString **)((char *)self + OBJC_IVAR_$_ViewController$_testa)) = testa; }
// testb getter
static NSString * _I_ViewController_testb(ViewController * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_ViewController$_testb)); }
// testb setter
static void _I_ViewController_setTestb_(ViewController * self, SEL _cmd, NSString *testb) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct ViewController, _testb), (id)testb, 0, 0); }
// testc getter
static NSString * _I_ViewController_testc(ViewController * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_ViewController$_testc)); }
// testc setter
static void _I_ViewController_setTestc_(ViewController * self, SEL _cmd, NSString *testc) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct ViewController, _testc), (id)testc, 0, 1); }
// testd getter
static NSString * _I_ViewController_testd(ViewController * self, SEL _cmd) { typedef NSString * _TYPE;
return (_TYPE)objc_getProperty(self, _cmd, __OFFSETOFIVAR__(struct ViewController, _testd), 1); }
// testd setter
static void _I_ViewController_setTestd_(ViewController * self, SEL _cmd, NSString *testd) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct ViewController, _testd), (id)testd, 1, 1); }
// teste getter
static NSString * _I_ViewController_teste(ViewController * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_ViewController$_teste)); }
// teste setter
static void _I_ViewController_setTeste_(ViewController * self, SEL _cmd, NSString *teste) { (*(NSString **)((char *)self + OBJC_IVAR_$_ViewController$_teste)) = teste; }
getter
在使用copy或者mutableCopy的时候将会调用objc_getProperty,其他的都一样
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}
// Retain release world
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;
// Atomic retain release world
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
id value = objc_retain(*slot);
slotlock.unlock();
// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
return objc_autoreleaseReturnValue(value);
}
setter
strong和retain修饰的setter却不一样。
1. strong是直接赋值的,这里的是交由ARC去维护retaincount,
2. retain, copy是调用了objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct ViewController, _testb), (id)testb, 0, 0)
3. strong 修饰下的nonatomic和atomic是一样的,
我们再看看objc_setProperty如何实现的
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
{
objc_setProperty_non_gc(self, _cmd, offset, newValue, atomic, shouldCopy);
}
objc_setProperty调用了objc_setProperty_non_gc,后面的两个参数看着比较面熟,一个是atomic对应的是nonatomic和atomic,shouldCopy对应的是copy或者是mutableCopy,2为mutableCopy、1为copy。这里有调用了reallySetProperty方法。
void objc_setProperty_non_gc(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);
}
这里的更为熟悉了,atomic、copy、mutableCopy.
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];
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
atomic这里,我们看到了spinlock_t,这里就是实现了变量的原子属性。所以我们在使用atomic的时候,并不是线程安全的。