一、clang分析@synchronized
int main(int argc, const char * argv[]) {
NSObject *obj = [[NSObject alloc] init];
@synchronized (obj) {
}
return 0;
}
通过clang -rewrite-objc main.m转为main.cpp后@synchronized (obj) { }内容如下
{
id _rethrow = 0;
id _sync_obj = (id)obj;
objc_sync_enter(_sync_obj);
try {
struct _SYNC_EXIT {
_SYNC_EXIT(id arg) : sync_exit(arg) {
} //构造函数,通过arg初始化sync_exit
~_SYNC_EXIT() {
objc_sync_exit(sync_exit);} //析构函数
id sync_exit;
} _sync_exit(_sync_obj); //把_sync_obj通过构造函数赋值给了sync_exit,它的生命周期是在try中,try执行完就会走析构调用objc_sync_exit
} catch (id e) {
_rethrow = e;
}
{
struct _FIN {
_FIN(id reth) : rethrow(reth) {
}
~_FIN() {
if (rethrow) objc_exception_throw(rethrow); }
id rethrow;
} _fin_force_rethow(_rethrow);
}
}
可以看到主要是内容包括一个objc_sync_enter和一个try-catch-finally结构,重点关注try中的内容,通过_sync_exit(_sync_obj)初始化了一个sync_exit = _sync_obj的struct _SYNC_EXIT结构体对象,然后在try作用域执行完后调用它的析构函数自运执行objc_sync_exit。那么可以看出@synchronized (obj) { }主要就是对obj进行objc_sync_enter和objc_sync_exit操作,以及抛出异常。
二、objc_sync_enter()源码解析
打上符号断点objc_sync_enter,找到objc_sync_enter属于libobjc.A.dylib,查看objc中objc_sync_enter源码。
//开始在obj上进行同步。如果有需要,会分配与obj关联的递归互斥锁。一旦获取锁成功返回OBJC_SYNC_SUCCESS。
int objc_sync_enter(id obj)
{
int result = OBJC_SYNC_SUCCESS; //OBJC_SYNC_SUCCESS=0
if (obj) {
SyncData* data = id2data(obj, ACQUIRE);
ASSERT(data);
//加锁
data->mutex.lock();
} else {
// @synchronized(nil) does nothing
if (DebugNilSync) {
_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
}
//啥也不干,asm("");
objc_sync_nil();
}
return result;
}
通过上面代码可以看到如果obj存在,通过id2data()函数拿到了data,调用data的mutex进行加锁。如果obj为nil,objc_sync_enter什么也不做。
2.1 id2data()函数分析
在看id2data()函数前,先来了解下其中用到的SyncData、SyncCacheItem、SyncCache、SyncList结构体。
struct SyncList {
SyncData *data;
spinlock_t lock;
constexpr SyncList