iOS __attribute__ 的用法總結(jié)

_attribute_ 機制是GNU C 的一大特色, 是一個編譯器指令,它指定聲明的特征,允許更多的錯誤檢查和高級優(yōu)化。

格式:_attribute_(xxx) xxx:即參數(shù)

__ attribute__ 在iOS中的實際用法總結(jié)(部分常用關(guān)鍵詞):

1、format

官方例子:NSLog

FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2) NS_NO_TAIL_CALL;
#define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))

format屬性可以給被聲明的函數(shù)加上類似printf或者scanf的特征,它可以使編譯器檢查函數(shù)聲明和函數(shù)實際調(diào)用參數(shù)之間的格式化字符串是否匹配。

對于format參數(shù)的使用如下:

format (archetype, string-index, first-to-check)

archetype指定哪種風(fēng)格,這里是NSString
string-index指定傳入的第幾個參數(shù)是格式化字符串
first-to-check指定第一個可變參數(shù)所在的索引

自己寫的例子(加強理解):

#define QYFormatFunc(a,b) __attribute__((format(__NSString__, a, b)))

FOUNDATION_EXPORT void QYLog(NSString *des, NSString * _Nonnull format, ...) QYFormatFunc(2,3);

#define QYLog(des,format, ...) NSLog([NSString stringWithFormat:@"%@%@",des, format], __VA_ARGS__)

調(diào)用

- (void)test0 {
    QYLog(@"test0 = ",@"%@", @"0");
}
//輸出》》》2018-11-08 14:33:56.854454+0800 testDemo[59161:32792950] test0 = 0

2、availability

官方例子:

- (CGSize)sizeWithFont:(UIFont *)font NS_DEPRECATED_IOS(2_0, 7_0, "Use -sizeWithAttributes:") __TVOS_PROHIBITED;

來看一下 后邊的宏

#define NS_DEPRECATED_IOS(_iosIntro, _iosDep, ...) CF_DEPRECATED_IOS(_iosIntro, _iosDep, __VA_ARGS__)

define CF_DEPRECATED_IOS(_iosIntro, _iosDep, ...) __attribute__((availability(ios,introduced=_iosIntro,deprecated=_iosDep,message="" __VA_ARGS__)))

//宏展開以后如下
__attribute__((availability(ios,introduced=2_0,deprecated=7_0,message=""__VA_ARGS__)));

introduced:第一次出現(xiàn)的版本。
deprecated:聲明要廢棄的版本,意味著用戶要遷移為其他API
obsoleted: 聲明移除的版本,意味著完全移除,再也不能使用它
message:一些關(guān)于廢棄和移除的額外信息,clang發(fā)出警告的時候會提供這些信息,對用戶使用替代的API非常有用。

自己寫的例子(加強理解)

- (void)oldMethod __attribute__((availability(ios,introduced=2_0,deprecated=7_0,obsoleted=13_0,message="用 -newMethod 這個方法替代 ")));

- (void)newMethod;

調(diào)用 oldMethod 會警告:

image.png

3、unavailable

告訴編譯器該方法不可用,如果強行調(diào)用編譯器會提示錯誤。比如某個類在構(gòu)造的時候不想直接通過init來初始化,只能通過特定的初始化方法()比如單例,就可以將init方法標(biāo)記為unavailable;

官方的例子:

#define UNAVAILABLE_ATTRIBUTE __attribute__((unavailable))

自己寫的例子 (加強記憶)

@interface AttributeTest : NSObject

- (instancetype)init __attribute__((unavailable("你不要用 -init"))); 

@end

強行調(diào)用,編譯報錯

image.png

4、nonnull

編譯器對函數(shù)參數(shù)進行NULL的檢查,參數(shù)類型必須是指針類型(包括對象)

自己寫的例子(同上)

- (NSString *)getString1:(NSString *)str __attribute__((nonnull (1)));

- (NSString *)getString2:(NSString * _Nonnull)str;

調(diào)用傳參為空,會提示waring

image.png

兩種方式,效果相同。

5、constructor/ destructor

constructor修飾的函數(shù)會在main函數(shù)之前執(zhí)行。destructor修飾的函數(shù)會在main函數(shù)之后執(zhí)行,可以用exit模擬或者手動殺死程序。

__attribute__((constructor)) void  beforeMain(){
    NSLog(@">>>>>>>>>>>>>>before main");
}

__attribute__((destructor)) void afterMain() {
    NSLog(@">>>>>>>>>>>>>>after main");
}

輸出結(jié)果:

... testDemo[52322:3526219] >>>>>>>>>>>>>>before main
... testDemo[52322:3526219] >>>>>>>>>>>>>>main
... testDemo[52322:3526219] >>>>>>>>>>>>>>after main

假如需要多個被修飾的函數(shù),還可以控制優(yōu)先級,同一個文件有效。例如:

__attribute__((constructor(112))) void  beforeMain112(){
    NSLog(@">>>>>>>>>>>>>>before main112");
}

__attribute__((constructor(111))) void  beforeMain111(){
    NSLog(@">>>>>>>>>>>>>>before main111");
}

__attribute__((constructor(110))) void  beforeMain110(){
    NSLog(@">>>>>>>>>>>>>>before main110");
}



__attribute__((destructor(110))) void afterMain110() {
    NSLog(@">>>>>>>>>>>>>>after main110");
}

__attribute__((destructor(111))) void afterMain111() {
    NSLog(@">>>>>>>>>>>>>>after main111");
}

__attribute__((destructor(112))) void afterMain112() {
    NSLog(@">>>>>>>>>>>>>>after main112");
}

輸出結(jié)果:

testDemo[52665:3558499] >>>>>>>>>>>>>>before main110
testDemo[52665:3558499] >>>>>>>>>>>>>>before main111
testDemo[52665:3558499] >>>>>>>>>>>>>>before main112
testDemo[52665:3558499] >>>>>>>>>>>>>>main
testDemo[52665:3558499] >>>>>>>>>>>>>>after main112
testDemo[52665:3558499] >>>>>>>>>>>>>>after main111
testDemo[52665:3558499] >>>>>>>>>>>>>>after main110

括號內(nèi)的值表示優(yōu)先級,[0,100]這個返回時系統(tǒng)保留。
執(zhí)行順序constructor從小到大,destructor從大到小。

PS: +loadconstructor 調(diào)用的順序。

__attribute__((constructor)) void  beforeMain(){
    NSLog(@">>>>>>>>>>>before main");
}

+ (void)load {
    NSLog(@">>>>>>>>>load");
}

輸出結(jié)果:

testDemo[52892:3581563] >>>>>>>>>load
testDemo[52892:3581563] >>>>>>>>>>>before main

+loadconstructor先調(diào)用。

原因:dyld(動態(tài)鏈接器,程序的最初起點)在加載 image(可以理解成 Mach-O 文件)時會先通知 objc runtime 去加載其中所有的類,每加載一個類時,它的 +load 隨之調(diào)用,全部加載完成后,dyld 才會調(diào)用這個 image 中所有的 constructor 方法,然后才調(diào)用main函數(shù).

6、enable_if

用來檢查C方法參數(shù)是否合法,只能用來修飾函數(shù):

static void printValidAge(int age)
__attribute__((enable_if(age > 0 && age < 120, "仙人???"))) {
    printf("%d", age);
}

表示只能輸入的參數(shù)只能是 0 ~ 120,否則編譯報錯

test0@2x.png

7、cleanup

學(xué)習(xí)下:黑魔法attribute((cleanup))

8、objc_runtime_name

用于 @interface 或 @protocol,將類或協(xié)議的名字在編譯時指定成另一個:

__attribute__((objc_runtime_name("SarkGay")))
@interface Sark : NSObject
@end

NSLog(@"%@", NSStringFromClass([Sark class])); // "SarkGay"

所有直接使用這個類名的地方都會被替換(唯一要注意的是這時用反射就不對了),最簡單粗暴的用處就是去做個類名混淆:

__attribute__((objc_runtime_name("40ea43d7629d01e4b8d6289a132482d0dd5df4fa")))
@interface SecretClass : NSObject
@end

還能用數(shù)字開頭,怕不怕 - -,假如寫個腳本把每個類前加個隨機生成的 objc_runtime_name,豈不是最最精簡版的代碼混淆就完成了呢…

它是我所了解的唯一一個對 objc 運行時類結(jié)構(gòu)有影響的 attribute,通過編碼類名可以在編譯時注入一些信息,被帶到運行時之后,再反解出來,這就相當(dāng)于開設(shè)了一條秘密通道,打通了寫碼時和運行時。腦洞一下,假如把這個 attribute 定義成宏,以 annotation 的形式完成某些功能,比如:

// @singleton 包裹了 __attribute__((objc_runtime_name(...)))
// 將類名改名成 "SINGLETON_Sark_sharedInstance"
@singleton(Sark, sharedInstance)
@interface Sark : NSObject
+ (instancetype)sharedInstance;
@end

在運行時用 attribute((constructor)) 獲取入口時機,用 runtime 找到這個類,反解出 “sharedInstance” 這個 selector 信息,動態(tài)將 + alloc,- init 等方法替換,返回 + sharedInstance 單例。

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

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,030評論 0 9
  • iOS宏的經(jīng)典用法Apple的習(xí)慣attribute iOS宏的經(jīng)典用法1.常量宏、表達式宏 define kTa...
    reallychao閱讀 4,038評論 0 0
  • 前言 2000年,伊利諾伊大學(xué)厄巴納-香檳分校(University of Illinois at Urbana-...
    星光社的戴銘閱讀 16,263評論 8 180
  • GNU C的一大特色就是__attribute__機制。__attribute__可以設(shè)置函數(shù)屬性(Functio...
    閉家鎖閱讀 17,492評論 0 5
  • 大 考 文/中流擊水 沙沙筆紙響,考生上戰(zhàn)場。 張張試卷里,題題測智商。 十年坐寒窗,功夫在平常。 ...
    楚山漢水閱讀 395評論 7 8

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