在Objective-C中@synchronized如何锁定/解锁?

@synchronized不使用“lock”和“unlock”实现互斥吗?那么它是如何锁定/解锁的呢?

下面程序的输出仅仅是“Hello World”。

@interface MyLock: NSLock<NSLocking>
@end


@implementation MyLock


- (id)init {
return [super init];
}


- (void)lock {
NSLog(@"before lock");
[super lock];
NSLog(@"after lock");
}


- (void)unlock {
NSLog(@"before unlock");
[super unlock];
NSLog(@"after unlock");
}


@end




int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];


MyLock *lock = [[MyLock new] autorelease];
@synchronized(lock) {
NSLog(@"Hello World");
}


[pool drain];
}
193762 次浏览

它只是将一个信号量与每个对象关联起来,并使用它。

Objective-C语言级别的同步使用互斥量,就像NSLock一样。语义上存在一些小的技术差异,但将它们视为在一个公共(更原始)实体之上实现的两个独立接口基本上是正确的。

特别是在NSLock中,你有一个显式锁,而在@synchronized中,你有一个与你用来同步的对象相关联的隐式锁。语言级锁的好处是编译器理解它,所以它可以处理范围问题,但从机械上讲,它们的行为基本相同。

你可以认为@synchronized是编译器重写:

- (NSString *)myString {
@synchronized(self) {
return [[myString retain] autorelease];
}
}

转化为:

- (NSString *)myString {
NSString *retval = nil;
pthread_mutex_t *self_mutex = LOOK_UP_MUTEX(self);
pthread_mutex_lock(self_mutex);
retval = [[myString retain] autorelease];
pthread_mutex_unlock(self_mutex);
return retval;
}

这不是完全正确的,因为实际的转换更复杂,并且使用递归锁,但它应该能够理解这一点。

在Objective-C中,@synchronized块自动为你处理锁定和解锁(以及可能的异常)。运行时动态地生成一个NSRecursiveLock,它与你正在同步的对象相关联。这个苹果文档解释更详细。这就是为什么你看不到来自NSLock子类的日志消息——你同步的对象可以是任何东西,而不仅仅是一个NSLock。

基本上,@synchronized (...)是一个简化代码的方便构造。像大多数简化抽象一样,它也有相关的开销(可以将其视为隐藏成本),了解这一点是很好的,但是在使用这种结构时,原始性能可能不是最高目标。

实际上

{
@synchronized(self) {
return [[myString retain] autorelease];
}
}

直接转换为:

// needs #import <objc/objc-sync.h>
{
objc_sync_enter(self)
id retVal = [[myString retain] autorelease];
objc_sync_exit(self);
return retVal;
}

此API自iOS 2.0以来可用,并使用…

#import <objc/objc-sync.h>

Apple的@synchronized实现是开源的,可以在在这里中找到。Mike ash写了两篇关于这个主题的有趣文章:

简而言之,它有一个表,将对象指针(使用它们的内存地址作为键)映射到pthread_mutex_t锁,这些锁根据需要被锁定和解锁。