这个问题的关键在于使其感觉非常容易实现。这里的规定应该类似于老式的 Objective-C @synchronized
块和 @property (atomic)
声明。
注意:和今天许多人一样,我对强制淘汰 @synchronized
/atomic
感兴趣——这是一个非常慢的奶奶级别的代码,在未来的飞船旁边没有任何位置。
您可以使用几乎相同的模式,唯一的真正例外是来自 libextobjc(它仅仅存在以防止与 block 相关的保留周期)的 @strongify(self)
和 @weakify(self)
等样板代码。
关键部分是你需要线程安全访问某个资源的代码的一部分。至少对于 GCDThreadsafe
的目的,存在两种类型的关键部分。
可能是一个好主意,在每个部分中只做您声称要做的。在读取部分中读取,在写入部分中写入。不要混淆你的事,老板。有人为那杯饮料付了钱。
是的,GCDThreadsafe 有几个 #define
宏,可以自动实现线程安全类的属性获取器和设置器。这就像能够指定 @property (atomic)
一样,只是它更快且不糟糕。
来自测试的示例
@interface ThreadsafeClass : NSObject <GCDThreadsafe>
@property (nonatomic, assign, readwrite) NSUInteger counter;
@end
@implementation ThreadsafeClass
@synthesize counter = _counter;
@gcd_threadsafe_implementSetter( NSUInteger, counter, setCounter: )
@gcd_threadsafe_implementGetter_assign( NSUInteger, counter )
// ...
@end
还有一个用于 Objective-C 对象的 @gcd_threadsafe_implementGetter_object()
和用于 GCD 对象的 @gcd_threadsafe_implementGetter_dispatch()
(它将自动确定 GCD 对象是否由运行时视为 Objective-C 对象或旧式 C 指针)。
要使用所有这些无聊的内容线程化您的类,您只需声明它遵守 GCDThreadsafe
协议即可。
就是这些。
该协议实际上是使用libextobjc的神奇“具体协议”机制定义的,这意味着它自动实现了它声明的所有方法(除非你定义了自己的实现,这通常是不应该做的)。什么都不用做。这很像一个mixin或类扩展,但稍微温和一些(以防你出于某种愚蠢的原因想要覆盖我那些免费、无需过问、从停车场中开来的默认实现)。
所以在你的Podfile中(你是不是使用CocoaPods,对吧?)
pod 'GCDThreadsafe'
在你的类的头文件中
#import <GCDThreadsafe/GCDThreadsafe.h>
@interface MyClass <GCDThreadsafe>
// ...
@end
并在你的代码中执行关键写操作
@weakify(self);
[self runCriticalMutableSection:^{
@strongify(self);
self.someProperty = @"a new value";
[self someMethodThatMutatesObjectState];
}];
...还有关键读操作
__block NSString *synchronizedValue = nil;
@weakify(self);
[self runCriticalReadSection:^{
@strongify(self);
synchronizedValue = [_someHiddenIvar copy];
}];
NSLog( @"synchronizedValue = %@", synchronizedValue );
实际上这就是全部内容。框架将(应该?……可能?)将一切安排得恰到好处。这是一个alpha版本,一定是,所以我非常欢迎任何愿意加入问题队列的反馈。
话又说回来,如果我有足够的勇气把alpha代码放入生产应用中,难道你不应该吗?
如果你是个傻瓜,并且想要用非标准特性(比如,给它一个自定义名称或使其并发)初始化你的队列,你可以在类的-init
方法中这样做,在其他事情之前
self = [super init];
if (self)
{
// the queueCritical property has to be named as such right now... i'll fix this eventually, maybe.
// also, you should definitely use a SERIAL queue, but if you're feeling ridiculous, you can always
// give CONCURRENT a shot as well.
@gcd_threadsafe_init( self.queueCritical, SERIAL, "com.pton.queueCritical" );
// ...
}
(libextobjc的具体协议真是酷毙了,对吧???)