前言
作为一个iOS
开发人员,我们在日常的开发中经常会使用到nonatomic
这个关键字,大家都知道nonatomic
的意思是非原子性,与其相对的atomic
原子性。但是非原子性和原子性到底是什么意思呢?他们到底做了什么、有什么不同呢?
原理
对属性设置相关的修饰词,在底层会调用如下方法:
void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
reallySetProperty(self, _cmd, newValue, offset, true, false, false);
}
void objc_setProperty_nonatomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
reallySetProperty(self, _cmd, newValue, offset, false, false, false);
}
void objc_setProperty_atomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
reallySetProperty(self, _cmd, newValue, offset, true, true, false);
}
void objc_setProperty_nonatomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
reallySetProperty(self, _cmd, newValue, offset, false, true, false);
}
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);
}
上述代码中,*slot
是所修饰对象的地址。我们可以看出,nonatomic
在底层的赋值是操作是直接将新值赋给了所修饰的对象:
oldValue = *slot;
*slot = newValue;
而atomic
在底层的赋值操作是:
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
首先,生成了一个spinlock_t
的锁,加锁,赋值,解锁。现在的已经不是原来的自旋锁,而是os_unfair_lock
。
using spinlock_t = mutex_tt<LOCKDEBUG>;
class mutex_tt : nocopy_t {
os_unfair_lock mLock;
void lock() {
lockdebug_mutex_lock(this);
os_unfair_lock_lock_with_options_inline
(&mLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
}
void unlock() {
lockdebug_mutex_unlock(this);
os_unfair_lock_unlock_inline(&mLock);
}
}
而getter
的底层实现:
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
id value = objc_retain(*slot);
slotlock.unlock();
return objc_autoreleaseReturnValue(value);
}
我们可以看出,nonatomic
是直接返回,而atomic
也会在取值的过程中也是加了一把锁。
所以,我们可以得出结论:nonatomic
和atomic
的区别是setter/getter
方法会不会加锁。atomic
会对setter/getter
方法进行加锁操作,而nonatomic
不会。
也就是说,被atomic
修饰的属性,当前有两个线程,线程1和线程2,线程1执行getter
的时候,线程2如果想要执行setter
,必须要线程1执行完getter
才行。
既然atomic
的对属性的setter/getter
加了锁,那么它修饰的属性是绝对安全的么?下面,我们看一个例子:
@property (atomic, strong) NSArray *atomicArray;
- (void)atomicTest {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 100000; i ++) {
if (i % 2 == 0) {
self.atomicArray = @[@"AAA", @"BBB", @"CCC"];
} else {
self.atomicArray = @[@"DDD"];
}
NSLog(@"Thread A: %@\n", self.atomicArray);
}
});
//Thread B
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 100000; i ++) {
if (self.atomicArray.count >= 2) {
NSString* str = [self.atomicArray objectAtIndex:1];
NSLog(@"==str==%@==", str);
}
NSLog(@"Thread B: %@\n",self.atomicArray);
}
});
}
运行程序,我们发现程序会崩溃,直接抛出如下异常:
libsystem_kernel.dylib`__pthread_kill:
这是为什么呢?atomic
只是保证了setter/getter
的安全性,但是并不能其属性的线程安全。因为我们对属性进行了[self.atomicArray objectAtIndex:1]
取值操作,虽然self.atomicArray
的getter
是安全的,但是其objectAtIndex
却不是安全的。
结论
atomic
会对setter/getter
方法进行加锁操作,而nonatomic
不会。所以在性能上nonatomic
要优于atomic
。而atomic
只是对属性的setter/getter
方法进行了加锁操作,这种安全仅仅是set/get
的读写安全,并非真正意义上的线程安全,因为属性的线程安全还有读写之外的其他操作。因此,在日常的开发中,使用atomic
还是需要慎重。
网友评论