libextobjc 拾遗之 onExit 的实现

libextobjc 是一个非常牛逼的、充满了黑魔法的 OC 库,拓展了 OC 的能力,里面有非常多值得学习的东西。

本文主要聊聊 ExtScope 这个类中 onExit 宏的使用以及原理。

这个宏可以在代码块结束时做一些清理工作,无论是因为异常,还是 goto/break/continue/return 等语句导致代码块结束,都不影响清理工作的进行。

- (void)showUsage {
    @onExit {
        NSLog(@"Exit");
    };
    NSLog(@"End of method: showUsage");
}

整个宏的定义如下:

#define onExit \

    ext_keywordify \

    __strong ext_cleanupBlock_t metamacro_concat(ext_exitBlock_, __LINE__) __attribute__((cleanup(ext_executeCleanupBlock), unused)) = ^

作用类似于 Swift 中的 defer,在结束代码块时做一些事。

很多人可能都知道 ext_keywordify 用于构造一个空的 autoreleasepool,这样可以要求使用者在前面强制加上 @

#define ext_keywordify autoreleasepool {}

实际上事情并非这么简单,autoreleasepool 的写法缺少编译器优化,多多少少会影响运行时性能。另一种可行的做法是利用 @try/@catch/@finally 这一语法。但它会导致编译器无法正确工作,漏掉一些原本可以检查出的错,比如:

- (void)tryCatchMissReturnTypeWarning {
    // This is block is used to test if foo and bar are the same, but this block has no return type
    // If `@autoreleasepool {}` is used, this code will not compile but now it does
    // Ths the usage of `@try {} @catch (...) {}` is dangaroud
    BOOL (^matchesFooOrBar)(id) = ^ BOOL (id obj){
        id foo = [[NSObject alloc] init];
        id bar = [[NSObject alloc] init];

        @try {} @catch (...) {}
//        @autoreleasepool {}
        NSLog(@"%@,%@", foo, bar);
    };
}

所以正确的做法是:

#if defined(DEBUG) && !defined(NDEBUG)
#define ext_keywordify autoreleasepool {}
#else
#define ext_keywordify try {} @catch (...) {}
#endif

这样 DEBUG 时不会漏掉错误,RELEASE 时又不影响性能

top Created with Sketch.