Clang Attributes
iOS開發工作中,查看官方文檔時經常見到各種系統宏定義,而定義宏時經常一堆以__attribute__(xx)
的語法格式出現,這些究竟是何方神圣,有何作用?本文摘出比較常見的,做一番解釋。
以 __attribute__(xx)
的語法格式出現,是 Clang 提供的一些能夠讓開發者在編譯過程中參與一些源碼控制的方法。
__attribute__((format(__NSString__, F, A)))
格式化字符串
可以查看 NSLog 的用法:
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2) NS_NO_TAIL_CALL;
// Marks APIs which format strings by taking a format string and optional varargs as arguments
#if !defined(NS_FORMAT_FUNCTION)
#if (__GNUC__*10+__GNUC_MINOR__ >= 42) && (TARGET_OS_MAC || TARGET_OS_EMBEDDED)
#define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))
#else
#define NS_FORMAT_FUNCTION(F,A)
#endif
#endif
__attribute__((deprecated(s)))
版本棄用提示
在編譯過程中能夠提示開發者該方法或者屬性已經被棄用
/** Deprecated, use initWithTimeInterval: instead. */
- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970
__attribute__((deprecated("Use initWithTimeInterval:")));
__attribute__((availability(os,introduced=m,deprecated=n, obsoleted=o,message=“” __VA_ARGS__)))
指明使用版本范圍
os 指系統的版本,m 指明引入的版本,n 指明過時的版本,o 指完全不用的版本,message 可以寫入些描述信息。
- (void)method __attribute__((availability(ios,introduced=3_0,deprecated=6_0,obsoleted=7_0,message=“iOS3到iOS7版本可用,iOS7不能用”)));
__attribute__((unavailable(…)))
方法不可用提示
這個會在編譯過程中告知方法不可用,如果使用了還會讓編譯失敗。
__attribute__((unused))
沒有被使用也不報警告
__attribute__((__warn_unused_result__))
不使用方法的返回值就會警告,目前 swift3 已經支持該特性了。oc中也可以通過定義這個attribute來支持。
__attribute__((__availability__(swift, unavailable, message=_msg)))
OC 的方法不能在 Swift 中使用。
__attribute__((cleanup(…)))
作用域結束時自動執行一個指定方法
作用域結束包括大括號結束,return,goto,break,exception 等情況。這個動作是先于這個對象的 dealloc 調用的。
Reactive Cocoa 中有個比較好的使用范例,@onExit 這個宏,定義如下:
#define onExit \
rac_keywordify \
__strong rac_cleanupBlock_t metamacro_concat(rac_exitBlock_, __LINE__) __attribute__((cleanup(rac_executeCleanupBlock), unused)) = ^
static inline void rac_executeCleanupBlock (__strong rac_cleanupBlock_t *block) {
(*block)();
}
這樣可以在就可以很方便的把需要成對出現的代碼寫在一起了。同樣可以在 Reactive Cocoa 看到其使用
if (property != NULL) {
rac_propertyAttributes *attributes = rac_copyPropertyAttributes(property);
if (attributes != NULL) {
@onExit {
free(attributes);
};
BOOL isObject = attributes->objectClass != nil || strstr(attributes->type, @encode(id)) == attributes->type;
BOOL isProtocol = attributes->objectClass == NSClassFromString(@“Protocol”);
BOOL isBlock = strcmp(attributes->type, @encode(void(^)())) == 0;
BOOL isWeak = attributes->weak;
shouldAddDeallocObserver = isObject && isWeak && !isBlock && !isProtocol;
}
}
可以看出 attributes 的設置和釋放都在一起使得代碼的可讀性得到了提高。
__attribute__((overloadable))
方法重載
能夠在 c 的函數上實現方法重載。即同樣的函數名函數能夠對不同參數在編譯時能夠自動根據參數來選擇定義的函數
__attribute__((overloadable)) void printArgument(int number){
NSLog(@“Add Int %i”, number);
}
__attribute__((overloadable)) void printArgument(NSString *number){
NSLog(@“Add NSString %@“, number);
}
__attribute__((overloadable)) void printArgument(NSNumber *number){
NSLog(@“Add NSNumber %@“, number);
}
__attribute__((objc_designated_initializer))
指定內部實現的初始化方法
如果是 objc_designated_initializer
初始化的方法必須調用覆蓋實現 super 的 objc_designated_initializer
方法。
如果不是 objc_designated_initializer
的初始化方法,但是該類有 objc_designated_initializer
的初始化方法,那么必須調用該類的 objc_designated_initializer
方法或者非 objc_designated_initializer
方法,而不能夠調用 super 的任何初始化方法。
__attribute__((objc_subclassing_restricted))
指定不能有子類
相當于 Java 里的 final 關鍵字,如果有子類繼承就會出錯。
__attribute__((objc_requires_super))
子類繼承必須調用 super
聲明后子類在繼承這個方法時必須要調用 super,否則會出現編譯警告,這個可以定義一些必要執行的方法在 super 里提醒使用者這個方法的內容時必要的。
__attribute__((const))
重復調用相同數值參數優化返回
用于數值類型參數的函數,多次調用相同的數值型參數,返回是相同的,只在第一次是需要進行運算,后面只返回第一次的結果,這時編譯器的一種優化處理方式。
__attribute__((constructor(PRIORITY)))
和 __attribute__((destructor(PRIORITY)))
PRIORITY 是指執行的優先級,main 函數執行之前會執行 constructor,main 函數執行后會執行 destructor,+load 會比 constructor 執行的更早點,因為動態鏈接器加載 Mach-O 文件時會先加載每個類,需要 +load 調用,然后才會調用所有的 constructor 方法。
通過這個特性,可以做些比較好玩的事情,比如說類已經 load 完了,是不是可以在 constructor 中對想替換的類進行替換,而不用加在特定類的 +load 方法里。
Clang 警告處理
先看看這個
#pragma clang diagnostic push
#pragma clang diagnostic ignored “-Wdeprecated-declarations”
sizeLabel = [self sizeWithFont:font constrainedToSize:size lineBreakMode:NSLineBreakByWordWrapping];
#pragma clang diagnostic pop
如果沒有#pragma clang 這些定義,會報出 sizeWithFont 的方法會被廢棄的警告,這個加上這個方法當然是為了兼容老系統,加上 ignored “-Wdeprecated-declarations” 的作用是忽略這個警告。通過 clang diagnostic push/pop 可以靈活的控制代碼塊的編譯選項。