Clang Attributes
iOS開發(fā)工作中愤估,查看官方文檔時(shí)經(jīng)常見到各種系統(tǒng)宏定義,而定義宏時(shí)經(jīng)常一堆以__attribute__(xx)
的語(yǔ)法格式出現(xiàn)沽瘦,這些究竟是何方神圣胧砰,有何作用?本文摘出比較常見的孕讳,做一番解釋。
以 __attribute__(xx)
的語(yǔ)法格式出現(xiàn)巍膘,是 Clang 提供的一些能夠讓開發(fā)者在編譯過(guò)程中參與一些源碼控制的方法厂财。
__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)))
版本棄用提示
在編譯過(guò)程中能夠提示開發(fā)者該方法或者屬性已經(jīng)被棄用
/** 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 指系統(tǒng)的版本,m 指明引入的版本峡懈,n 指明過(guò)時(shí)的版本璃饱,o 指完全不用的版本,message 可以寫入些描述信息逮诲。
- (void)method __attribute__((availability(ios,introduced=3_0,deprecated=6_0,obsoleted=7_0,message=“iOS3到iOS7版本可用帜平,iOS7不能用”)));
__attribute__((unavailable(…)))
方法不可用提示
這個(gè)會(huì)在編譯過(guò)程中告知方法不可用,如果使用了還會(huì)讓編譯失敗梅鹦。
__attribute__((unused))
沒有被使用也不報(bào)警告
__attribute__((__warn_unused_result__))
不使用方法的返回值就會(huì)警告,目前 swift3 已經(jīng)支持該特性了冗锁。oc中也可以通過(guò)定義這個(gè)attribute來(lái)支持齐唆。
__attribute__((__availability__(swift, unavailable, message=_msg)))
OC 的方法不能在 Swift 中使用。
__attribute__((cleanup(…)))
作用域結(jié)束時(shí)自動(dòng)執(zhí)行一個(gè)指定方法
作用域結(jié)束包括大括號(hào)結(jié)束冻河,return箍邮,goto,break叨叙,exception 等情況锭弊。這個(gè)動(dòng)作是先于這個(gè)對(duì)象的 dealloc 調(diào)用的。
Reactive Cocoa 中有個(gè)比較好的使用范例擂错,@onExit 這個(gè)宏味滞,定義如下:
#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)();
}
這樣可以在就可以很方便的把需要成對(duì)出現(xiàn)的代碼寫在一起了。同樣可以在 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 的設(shè)置和釋放都在一起使得代碼的可讀性得到了提高钮呀。
__attribute__((overloadable))
方法重載
能夠在 c 的函數(shù)上實(shí)現(xiàn)方法重載剑鞍。即同樣的函數(shù)名函數(shù)能夠?qū)Σ煌瑓?shù)在編譯時(shí)能夠自動(dòng)根據(jù)參數(shù)來(lái)選擇定義的函數(shù)
__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))
指定內(nèi)部實(shí)現(xiàn)的初始化方法
如果是 objc_designated_initializer
初始化的方法必須調(diào)用覆蓋實(shí)現(xiàn) super 的 objc_designated_initializer
方法。
如果不是 objc_designated_initializer
的初始化方法爽醋,但是該類有 objc_designated_initializer
的初始化方法蚁署,那么必須調(diào)用該類的 objc_designated_initializer
方法或者非 objc_designated_initializer
方法,而不能夠調(diào)用 super 的任何初始化方法蚂四。
__attribute__((objc_subclassing_restricted))
指定不能有子類
相當(dāng)于 Java 里的 final 關(guān)鍵字光戈,如果有子類繼承就會(huì)出錯(cuò)哪痰。
__attribute__((objc_requires_super))
子類繼承必須調(diào)用 super
聲明后子類在繼承這個(gè)方法時(shí)必須要調(diào)用 super,否則會(huì)出現(xiàn)編譯警告久妆,這個(gè)可以定義一些必要執(zhí)行的方法在 super 里提醒使用者這個(gè)方法的內(nèi)容時(shí)必要的晌杰。
__attribute__((const))
重復(fù)調(diào)用相同數(shù)值參數(shù)優(yōu)化返回
用于數(shù)值類型參數(shù)的函數(shù),多次調(diào)用相同的數(shù)值型參數(shù)镇饺,返回是相同的乎莉,只在第一次是需要進(jìn)行運(yùn)算,后面只返回第一次的結(jié)果奸笤,這時(shí)編譯器的一種優(yōu)化處理方式惋啃。
__attribute__((constructor(PRIORITY)))
和 __attribute__((destructor(PRIORITY)))
PRIORITY 是指執(zhí)行的優(yōu)先級(jí),main 函數(shù)執(zhí)行之前會(huì)執(zhí)行 constructor监右,main 函數(shù)執(zhí)行后會(huì)執(zhí)行 destructor边灭,+load 會(huì)比 constructor 執(zhí)行的更早點(diǎn),因?yàn)閯?dòng)態(tài)鏈接器加載 Mach-O 文件時(shí)會(huì)先加載每個(gè)類健盒,需要 +load 調(diào)用绒瘦,然后才會(huì)調(diào)用所有的 constructor 方法。
通過(guò)這個(gè)特性扣癣,可以做些比較好玩的事情惰帽,比如說(shuō)類已經(jīng) load 完了,是不是可以在 constructor 中對(duì)想替換的類進(jìn)行替換父虑,而不用加在特定類的 +load 方法里该酗。
Clang 警告處理
先看看這個(gè)
#pragma clang diagnostic push
#pragma clang diagnostic ignored “-Wdeprecated-declarations”
sizeLabel = [self sizeWithFont:font constrainedToSize:size lineBreakMode:NSLineBreakByWordWrapping];
#pragma clang diagnostic pop
如果沒有#pragma clang 這些定義,會(huì)報(bào)出 sizeWithFont 的方法會(huì)被廢棄的警告士嚎,這個(gè)加上這個(gè)方法當(dāng)然是為了兼容老系統(tǒng)呜魄,加上 ignored “-Wdeprecated-declarations” 的作用是忽略這個(gè)警告。通過(guò) clang diagnostic push/pop 可以靈活的控制代碼塊的編譯選項(xiàng)莱衩。