1

黑魔法__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提供的宏,在此用于产生一个文件内唯一的数字,这样就可以在同一个函数里多次使用AUTOLOCKlock不同的锁。 CONCATMACRO_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 = ^

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

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 链接


canopus4u
618 声望50 粉丝