iOS之cleanup编译器属性的妙用

个人博客的云服务器不打算续费了,所以会陆续将上面的一些文章迁移到这里来,格式可能不兼容,见谅。

attribute

编译器属性__attribute__用于向编译器描述特殊的标识、检查或优化,几个常用的用法看这篇文章就好。

cleanup

理解代码先从命名开始,cleanup中文意为"清理",那么可以知道这个关键字执行的应该和清理操作相关。

实际上,cleanup是一个系统内置函数,函数签名类似: void cleanup(void (*)(id *instance))。可以看到,它的形参是一个函数指针,这个函数指针指向一个没有返回值、形参为id *的函数。

cleanup函数的作用是在修饰的对象的作用域结束时,调用传递给cleanup函数的形参函数。而且,cleanup是先于这个对象的dealloc调用的。假如一个作用域内有若干个被cleanup修饰的变量,形参函数的调用顺序是后入先出的栈式顺序。

例如:

/// code
inline static void personClean(__strong Person **aPerson) {
    NSLog(@"Person clean now, is %p", *aPerson);
}

{
    Person *person __attribute((cleanup(personClean))) = Person.new;
} /// mark - 此处person对象作用域结束,生命周期结束

/// output
Person clean now, is 0x......

上例中,定义了一个c函数为personClean,该函数返回值为空、形参为指向Person对象的二重指针,并在该函数中打印了入参对象。

下面的代码,在{}作用域中定义了person对象,并使用__attribute((cleanup(personClean)))修饰了该对象,表示在该对象生命周期结束时,应将person对象作为入参调用personClean函数。

当代码执行到mark - 处时,person对象的生命周期即将结束,此时会cleanup函数会调用personClean函数,执行相关操作。

cleanup实战技巧

1. 保留代码,最后执行

上面提到了,一个作用域内存在多个cleanup操作时,在作用域结束时会按照后进先出的顺序执行入参函数。

利用这个特性,就可以在作用域开始时就将作用域结束时执行的逻辑设定好。例如,在一个函数中执行加锁操作,如果函数行数特别多,以防在函数结束时忘记解锁,可以通过cleanup函数提前设定函数结束时的解锁操作。

伪代码如下:

inline static void lockClean(__strong Lock **lock) {
    [*lock unlock];
}

void myFunction() {
    
    Lock *lock __attribute((cleanup(lockClean)), unused) = Lock.new;
    [lock lock];
} /// mark - 执行到这里时,将自动调用lockClean函数执行解锁

2.实现保留清理操作的块

上面提到了cleanup函数的签名类似: void cleanup(void (*)(id *instance))id在objc中,可以修饰任意类型。也就是说,cleanup函数同样适用于block变量。

我们可以定义一个宏,如下:

#define OnExit \
__strong dispatch_block_t block __attribute((cleanup(blockClean), unused)) = ^

同样的,需要定义一个传递给cleanup函数的清理函数:

inline static void blockClean(__strong dispatch_block_t *block) {
    !(*block) ? : (*block)();
}

blockClean函数中,只是执行了传递的入参block,这样就可以将清理逻辑在定义入参的地方处理了,提高了代码的内聚。

完成上两步后,再来重写一次在作用域结束时,自动进行解锁的伪代码:

void myFunction() {
    
    Lock *lock __attribute((cleanup(lockClean)), unused) = Lock.new;
    OnExit {
        [lock unlock];
    };
} /// mark - 执行到这里时,将自动调用blockClean函数,而后回调到OnExit定义的块中执行解锁

可以看到,和第一点那种方式不同的是:使用cleanup函数修饰block类型的变量,将对象作用域结束时的清理逻辑和对象的定义集中到了一起,这样在清理逻辑中也能使用同样的上下文。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值