概要
atomic只是一个锁,具体一点地说就是一个spinlock自旋锁,而线程安全是利用锁来构建的一种代码模式,两者在逻辑上不对等
例子1
针对某一个属性,若线程A在调getter的同时,线程B和C都调了该属性的setter,那线程A能get到3种值:原始值、线程B设置的值、线程C设置的值
但因为线程A在一定概率下能获取正确的值,所以不能100%证明atomic不是线程安全的
例子2
@property(atomic, strong) NSString* str;
/// thread A
for (int i = 0; i < 100000; i++) {
if (i % 2 == 0) {
self.str = @"0123456789";
} else {
self.str = @"01234";
}
NSLog(@"Thread A: %@", self.str);
}
/// thread B
for (int i = 0; i < 100000; i++) {
if (self.str.length >= 10) {
NSString *subStr = [self.str substringWithRange:NSMakeRange(0, 10)];
}
NSLog(@"Thread B: %@", self.str);
}
线程B在生成subStr时肯定会因为str长度不够而Crash,能100%证明atomic不是线程安全的
atomic源码解析
本文基于runtime源码818.2版本做分析
在objc-accessors.mm中可以看到atomic的实现,代码如下
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);
}
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);
}
atomic对属性的setter和getter方法添加spinlock自旋锁,但其实spinlock由于存在优先级反转问题已经被弃用,在其内部实现里已经被os_unfair_lock替代