一種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 連接

相關文章
相關標籤/搜索