一种Auto Unlock的方法

黑魔法__attribute__((cleanup))有讲如何使用cleanup来简化使用lock代码。
__attribute__这个修饰符颇有用,前段时间集中写了一些东西收集这些有意思__attribute__。戳这里git

今天展现一种另外的方法达到这个目的。最终代码放在了个人githubgithub

实现

#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。segmentfault

- (void) dosth{
    AUTOLOCK(self.lock);
    //do actual work you want below
}

__Counter__是一个gcc提供的宏,在此用于产生一个文件内惟一的数字,这样就能够在同一个函数里屡次使用AUTOLOCKlock不一样的锁。 CONCATMACRO_CONCAT是一种惯用的链接Macro的手法,很少说了。函数

这种方法依赖于代码块的长度,若是要分段使用屡次lock同一把锁。那就方法以下:atom

- (void) dosth{
    {
        AUTOLOCK(self.lock);
        //do actual work you want below
    }
    
    {
        AUTOLOCK(self.lock);
        //do actual work you want below
    }
}

若是还嫌麻烦,那就只能用这个函数:spa

void lockAndDo(id<NSLocking>lock,dispatch_block_t block)
{
    if (lock) {
        AUTOLOCK(lock);
        if (block) {
            block();
        }
    }else{
        if (block) {
            block();
        }
    }
}

使用时:code

lockAndDo(lock, ^{
     //do actual work you want here   
});

这样以来就跟@synchronized很像了。blog

加强

@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 = ^

加强一下,使用起来跟@synchronizedcleanup的方案基本差不太多了:作用域

AUTOLOCK2(lock){
    NSLog(@"hi");
};

美中不足:没有使用__COUNTER__,若是不分代码块的话,一个做用域里只能用一个。get

更新一下,改为这种就行了,

#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 连接

相关文章
相关标签/搜索