起
黑魔法__attribute__((cleanup))有讲如何使用cleanup
来简化使用lock代码。__attribute__
这个修饰符很有用,前段时间集中写了一些东西收集这些有意思__attribute__
。戳这里
今天展示一种另外的方法达到这个目的。最终代码放在了我的github
实现
#define CONCAT(x, y) x##y
#define MACRO_CONCAT(x,y) CONCAT(x,y)
@interface AutoUnlockObject : NSObject {
id<NSLocking> _lock;
}
- (id) initWithLock:(id<NSLocking>)theLock;
@end
@implementation AutoUnlockObject
- (id) initWithLock:(id<NSLocking>)theLock{
self = [super init];
_lock = theLock;
[_lock lock];
return self;
}
- (void) dealloc{
[_lock unlock];
}
@end
#define AUTOLOCK(lock) \
__unused AutoUnlockObject* MACRO_CONCAT(tmpObject,__COUNTER__) = [[AutoUnlockObject alloc] initWithLock:lock];
解释
利用ARC的特性,在离开代码块时,tmpObject
会自动释放。这样-dealloc
中的unlock回触发,从而实现自动unlock。
- (void) dosth{
AUTOLOCK(self.lock);
//do actual work you want below
}
__Counter__
是一个gcc提供的宏,在此用于产生一个文件内唯一的数字,这样就可以在同一个函数里多次使用AUTOLOCK
lock不同的锁。CONCAT
和MACRO_CONCAT
是一种惯用的连接Macro的手法,不多说了。
这种方法依赖于代码块的长度,如果要分段使用多次lock同一把锁。那就方法如下:
- (void) dosth{
{
AUTOLOCK(self.lock);
//do actual work you want below
}
{
AUTOLOCK(self.lock);
//do actual work you want below
}
}
如果还嫌麻烦,那就只能用这个函数:
void lockAndDo(id<NSLocking>lock,dispatch_block_t block)
{
if (lock) {
AUTOLOCK(lock);
if (block) {
block();
}
}else{
if (block) {
block();
}
}
}
使用时:
lockAndDo(lock, ^{
//do actual work you want here
});
这样以来就跟@synchronized
很像了。
增强
@interface AutoUnlockObject2 : AutoUnlockObject
@property (nonatomic,copy) dispatch_block_t block;
@end
@implementation AutoUnlockObject2
- (void) dealloc{
if (self.block) self.block();
[_lock unlock];
}
@end
#define AUTOLOCK2(lock) \
__unused AutoUnlockObject2* tmpObject = [[AutoUnlockObject2 alloc] initWithLock:lock];\
tmpObject.block = ^
增强一下,使用起来跟@synchronized
和cleanup
的方案基本差不太多了:
AUTOLOCK2(lock){
NSLog(@"hi");
};
美中不足:没有使用__COUNTER__,如果不分代码块的话,一个作用域里只能用一个。
更新一下,改成这种就好了,
#define AUTOLOCK3(lock) \
[[AutoUnlockObject2 alloc] initWithLock:lock].block = ^
//usage
AUTOLOCK3(lock){
NSLog(@"hi1");
};
AUTOLOCK3(lock){
NSLog(@"hi2");
};
恩.
All is well that ends well
总结
不过话说回来,现在用lock的时候,已经不太多了。C++的代码基本也可以用类似实现。
原作写于segmentfault 链接
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。