atomic与nonatomic是iOS开发中最常用的属性修饰符。从名字上看一个是原子性操作,一个是非原子性操作。非原子性操作容易理解其实就是不保证线程安全。而原子性操作是要求保证线程安全的。今天从objc源码来分析atomic和nonatomic是如何实现的。
示例代码:
@interface XXObject : NSObject
@property (atomic, strong) NSString *name;
@property (nonatomic, strong) NSString *name;
@end
操作属性的方法被定义在objc-accessors.mm文件中。
setter
设置atomic属性时调用的是objc_setProperty_atomic方法,然后将方法转发到reallySetProperty中。方法源码如下:
void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
reallySetProperty(self, _cmd, newValue, offset, true, false, false);
}
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
// 偏移量为0,更改isa指针
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);
}
从源码看逻辑非常容易理解,在进行值的设置时,根据对象地址从全局的哈希表中取出spinlock_t类型的锁。spinlock_t是一个C++类,内部使用的是os_unfair_lock锁来保证线程访问安全。os_unfair_lock是用来替代OSSpinLock的锁,它是一个自旋锁,解决了优先级反转问题。
nonatomic走的是不加锁逻辑。
getter
源码如下:
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
// 偏移量是0,返回当前对象的isa指针
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();
// 为了优化性能,在自旋锁的外部将对象放入自动释放池
return objc_autoreleaseReturnValue(value);
}
对atomic属性进行访问时,调用的是objc_getProperty方法,内部也是通过自旋锁来解决多线程问题,出于性能优化考虑,苹果在自旋锁之外将对象放到自动释放池中。
nonatomic走的是不加锁逻辑。
总结
通过对源码的分析可以知道目前atomic的实现方式并不是通过@synchronized方式实现的,而是采用的更高性能的os_unfair_lock自旋锁来实现。