Clang Attributes 和 Clang 警告處理

Clang Attributes

iOS開(kāi)發(fā)工作中,查看官方文檔時(shí)經(jīng)常見(jiàn)到各種系統(tǒng)宏定義,而定義宏時(shí)經(jīng)常一堆以__attribute__(xx) 的語(yǔ)法格式出現(xiàn),這些究竟是何方神圣,有何作用?本文摘出比較常見(jiàn)的,做一番解釋。

__attribute__(xx)的語(yǔ)法格式出現(xiàn),是 Clang 提供的一些能夠讓開(kāi)發(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ò)程中能夠提示開(kāi)發(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 可以寫(xiě)入些描述信息。

- (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))

沒(méi)有被使用也不報(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)的代碼寫(xiě)在一起了。同樣可以在 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 的初始化方法,但是該類(lèi)有 objc_designated_initializer 的初始化方法,那么必須調(diào)用該類(lèi)的 objc_designated_initializer 方法或者非 objc_designated_initializer 方法,而不能夠調(diào)用 super 的任何初始化方法。
__attribute__((objc_subclassing_restricted)) 指定不能有子類(lèi)

相當(dāng)于 Java 里的 final 關(guān)鍵字,如果有子類(lèi)繼承就會(huì)出錯(cuò)。

__attribute__((objc_requires_super))子類(lèi)繼承必須調(diào)用 super

聲明后子類(lèi)在繼承這個(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ù)值類(lèi)型參數(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è)類(lèi),需要 +load 調(diào)用,然后才會(huì)調(diào)用所有的 constructor 方法。

通過(guò)這個(gè)特性,可以做些比較好玩的事情,比如說(shuō)類(lèi)已經(jīng) load 完了,是不是可以在 constructor 中對(duì)想替換的類(lèi)進(jìn)行替換,而不用加在特定類(lèi)的 +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

如果沒(méi)有#pragma clang 這些定義,會(huì)報(bào)出 sizeWithFont 的方法會(huì)被廢棄的警告,這個(gè)加上這個(gè)方法當(dāng)然是為了兼容老系統(tǒng),加上 ignored “-Wdeprecated-declarations” 的作用是忽略這個(gè)警告。通過(guò) clang diagnostic push/pop 可以靈活的控制代碼塊的編譯選項(xiàng)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容