編譯器屬性__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]; // 媽媽再也不用擔心我忘寫後半段了 }; // 這裏 // 愛多少行 // 就多少行 |
還是那句老話:剩下的就全靠想象力了。