黑魔法__attribute__((cleanup))

編譯器屬性__attribute__用於向編譯器描述特殊的標識、檢查或優化,幾個常用的用法看《mattt大神的文章》就好。今天發現一個名爲cleanup的黑魔法屬性,簡單介紹下。


基本用法

__attribute__((cleanup(...))),用於修飾一個變量,在它的作用域結束時可以自動執行一個指定的方法,如:

// 指定一個cleanup方法,注意入參是所修飾變量的地址,類型要一樣
// 對於指向objc對象的指針(id *),如果不強制聲明__strong默認是__autoreleasing,造成類型不匹配
static void stringCleanUp(__strong NSString **string) {
    NSLog(@"%@", *string);
}
// 在某個方法中:
{
    __strong NSString *string __attribute__((cleanup(stringCleanUp))) = @"sunnyxx";
} // 當運行到這個作用域結束時,自動調用stringCleanUp

所謂作用域結束,包括大括號結束、return、goto、break、exception等各種情況。
當然,可以修飾的變量不止NSString,自定義Class基本類型都是可以的:

// 自定義的Class
static void sarkCleanUp(__strong Sark **sark) {
    NSLog(@"%@", *sark);
}
__strong Sark *sark __attribute__((cleanup(sarkCleanUp))) = [Sark new];
// 基本類型
static void intCleanUp(NSInteger *integer) {
    NSLog(@"%d", *integer);
}
NSInteger integer __attribute__((cleanup(intCleanUp))) = 1;

假如一個作用域內有若干個cleanup的變量,他們的調用順序是先入後出的棧式順序;
而且,cleanup是先於這個對象的dealloc調用的。

進階用法

既然__attribute__((cleanup(...)))可以用來修飾變量,block當然也是其中之一,寫一個block的cleanup函數非常有趣:

// void(^block)(void)的指針是void(^*block)(void)
static void blockCleanUp(__strong void(^*block)(void)) {
    (*block)();
}

於是在一個作用域裏聲明一個block:

{
   // 加了個`unused`的attribute用來消除`unused variable`的warning
    __strong void(^block)(void) __attribute__((cleanup(blockCleanUp), unused)) = ^{
        NSLog(@"I'm dying...");
    };
} // 這裏輸出"I'm dying..."

這裏不得不提萬能的Reactive Cocoa中神奇的@onExit方法,其實正是上面的寫法,簡單定義個宏:

#define onExit\
    __strong void(^block)(void) __attribute__((cleanup(blockCleanUp), unused)) = ^

用這個宏就能將一段寫在前面的代碼最後執行:

{
    onExit {
        NSLog(@"yo");
    };
} // Log "yo"

這樣的寫法可以將成對出現的代碼寫在一起,比如說一個lock:

NSRecursiveLock *aLock = [[NSRecursiveLock alloc] init];
[aLock lock];
// 這裏
//     有
//        100多萬行
[aLock unlock]; // 看到這兒的時候早忘了和哪個lock對應着了

用了onExit之後,代碼更集中了:

NSRecursiveLock *aLock = [[NSRecursiveLock alloc] init];
[aLock lock];
onExit {
    [aLock unlock]; // 媽媽再也不用擔心我忘寫後半段了
};
// 這裏
//    愛多少行
//           就多少行

還是那句老話:剩下的就全靠想象力了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章