Runtime那些事兒(消息機制)

一、關(guān)于runtime

之前在項目中有遇到過用runtime解決改變?nèi)肿煮w的問題,所以再一次感受到了runtime黑魔法的強大,趁現(xiàn)在有機會分享一下對runtime的一些理解。在對象調(diào)用方法是Objective-C中經(jīng)常使用的功能绍绘,也就是消息的傳遞,而Objective-C是C的超集迟赃,所以和C不同的是陪拘,Objective-C使用的是動態(tài)綁定,也就是runtime纤壁。Objective-C的消息傳遞和消息機制也就不多說了左刽,今天主要說的是動態(tài)方法,也就是函數(shù)的調(diào)用酌媒。

二欠痴、相關(guān)的幾個函數(shù)

下面一張圖詳細的概括了每個函數(shù)調(diào)用的先后以及執(zhí)行的前提

消息傳遞函數(shù)的調(diào)用

1.對象在收到無法解讀的消息后,首先會調(diào)用所屬類的

1

+?(BOOL)resolveInstanceMethod:(SEL)sel

這個方法在運行時秒咨,沒有找到SEL的IML時就會執(zhí)行喇辽。這個函數(shù)是給類利用class_addMethod添加函數(shù)的機會。根據(jù)文檔雨席,如果實現(xiàn)了添加函數(shù)代碼則返回YES菩咨,未實現(xiàn)返回NO。舉個例子陡厘,新建了一個工程抽米,首先我在ViewController這個類中執(zhí)行doSomething1這個方法,代碼如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27//

//??ViewController.m

//??RuntimeTest1

//

//??Created?by?HenryCheng?on?15/12/24.

//??Copyright??(版權(quán)符號)?2015年?www.igancao.com??All?rights?reserved.

//

#import?"ViewController.h"

@interface?ViewController?()

@end

@implementation?ViewController

-?(void)viewDidLoad?{

[superviewDidLoad];

[self?performSelector:@selector(doSomething)];

}

-?(void)didReceiveMemoryWarning?{

[superdidReceiveMemoryWarning];

//?Dispose?of?any?resources?that?can?be?recreated.

}

@end

運行結(jié)果

1

2

3**2015-12-24?10:35:37.726?RuntimeTest1[1877:337842]?-[ViewController?doSomething]:?unrecognized?selector?sent?to?instance?0x7fe9f3736680**

**2015-12-24?10:35:37.729?RuntimeTest1[1877:337842]?***?Terminating?app?due?to?uncaught?exception'NSInvalidArgumentException',?reason:'-[ViewController?doSomething]:?unrecognized?selector?sent?to?instance?0x7fe9f3736680'**

*****?Firstthrowcall?stack:**

不出意外糙置,程序崩潰云茸,因為沒有找到doSomething這個方法,下面我們在里面實現(xiàn) + (BOOL)resolveInstanceMethod:(SEL)sel這個方法谤饭,并且判斷如果SEL 是doSomething那就輸出add method here

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35//

//??ViewController.m

//??RuntimeTest1

//

//??Created?by?HenryCheng?on?15/12/24.

//??Copyright??(版權(quán)符號)?2015年?www.igancao.com?All?rights?reserved.

//

#import?"ViewController.h"

@interface?ViewController?()

@end

@implementation?ViewController

-?(void)viewDidLoad?{

[superviewDidLoad];

[self?performSelector:@selector(doSomething)];

}

+?(BOOL)resolveInstanceMethod:(SEL)sel?{

if(sel?==?@selector(doSomething))?{

NSLog(@"add?method?here");

returnYES;

}

return[superresolveInstanceMethod:sel];

}

-?(void)didReceiveMemoryWarning?{

[superdidReceiveMemoryWarning];

//?Dispose?of?any?resources?that?can?be?recreated.

}

@end

繼續(xù)運行标捺,然后看到log

1

2

3

4**2015-12-24?10:47:24.687?RuntimeTest1[2007:382077]?add?method?here**

**2015-12-24?10:47:24.687?RuntimeTest1[2007:382077]?-[ViewController?doSomething]:?unrecognized?selector?sent?to?instance?0x7f9568c331f0**

**2015-12-24?10:47:24.690?RuntimeTest1[2007:382077]?***?Terminating?app?due?to?uncaught?exception'NSInvalidArgumentException',?reason:'-[ViewController?doSomething]:?unrecognized?selector?sent?to?instance?0x7f9568c331f0'**

*****?Firstthrowcall?stack:**

可以看到程序依然是崩潰了懊纳,但是我們可以看到輸出了add method here,這說明我們 + (BOOL)resolveInstanceMethod:(SEL)sel這個方法執(zhí)行了亡容,并進入了判斷嗤疯,所以,在這兒萍倡,我們可以做一下操作身弊,使這個方法得到相應(yīng)辟汰,不至于走到最后- (void)doesNotRecognizeSelector:(SEL)aSelector這個方法中而崩掉了列敲,接下來,我么繼續(xù)操作帖汞,如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41//

//??ViewController.m

//??RuntimeTest1

//

//??Created?by?HenryCheng?on?15/12/24.

//??Copyright??(版權(quán)符號)?2015年?www.igancao.com?All?rights?reserved.

//

#import?"ViewController.h"

#import?[objc/runtime.h](因識別問題戴而,此處將尖括號改為方括號)

@interface?ViewController?()

@end

@implementation?ViewController

-?(void)viewDidLoad?{

[superviewDidLoad];

[self?performSelector:@selector(doSomething)];

}

+?(BOOL)resolveInstanceMethod:(SEL)sel?{

if(sel?==?@selector(doSomething))?{

NSLog(@"add?method?here");

class_addMethod([self?class],?sel,?(IMP)dynamicMethodIMP,"v@:");

returnYES;

}

return[superresolveInstanceMethod:sel];

}

void?dynamicMethodIMP?(id?self,?SEL?_cmd)?{

NSLog(@"doSomething?SEL");

}

-?(void)didReceiveMemoryWarning?{

[superdidReceiveMemoryWarning];

//?Dispose?of?any?resources?that?can?be?recreated.

}

@end

導(dǎo)入了并且在+ (BOOL)resolveInstanceMethod:(SEL)sel中執(zhí)行了class_addMethod這個方法,然后定義了一個void dynamicMethodIMP (id self, SEL _cmd)這個函數(shù)翩蘸,運行工程所意,看log

1

2**2015-12-24?11:45:11.934?RuntimeTest1[2284:478571]?add?method?here**

**2015-12-24?11:45:11.934?RuntimeTest1[2284:478571]?doSomething?SEL**

這時候我們發(fā)現(xiàn),程序并沒有崩潰催首,而且還輸出了doSomething SEL扶踊,這時候就說明我們已經(jīng)通過runtime成功的向我們這個類中添加了一個方法。關(guān)于class_addMethod這個方法郎任,是這樣定義的

1

OBJC_EXPORT?BOOL?class_addMethod(Class?cls,?SEL?name,?IMP?imp,??const?char?*types)

cls ? 在這個類中添加方法秧耗,也就是方法所添加的類

name ?方法名,這個可以隨便起的

imp ? 實現(xiàn)這個方法的函數(shù)

types 定義該數(shù)返回值類型和參數(shù)類型的字符串舶治,這里比如"v@:"分井,其中v就是void,帶表返回類型就是空霉猛,@代表參數(shù)尺锚,這里指的是id(self),這里:指的是方法SEL(_cmd)惜浅,比如我再定義一個函數(shù)

1

2

3

4int?newMethod?(id?self,?SEL?_cmd,?NSString?*str)?{

return100;

}

那么添加這個函數(shù)的方法就應(yīng)該是ass_addMethod([self class], @selector(newMethod), (IMP)newMethod, "i@:@");

2.如果在+ (BOOL)resolveInstanceMethod:(SEL)sel中沒有找到或者添加方法

消息繼續(xù)往下傳遞到- (id)forwardingTargetForSelector:(SEL)aSelector看看是不是有對象可以執(zhí)行這個方法瘫辩,我們來重新建個工程,然后新建一個叫SecondViewController的類坛悉,里面有一個- (void)secondVCMethod方法杭朱,如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41//

//??SecondViewController.m

//??RuntimeTest2

//

//??Created?by?HenryCheng?on?15/12/24.

//??Copyright??(版權(quán)符號)?2015年??www.igancao.com?All?rights?reserved.

//

#import?"SecondViewController.h"

@interface?SecondViewController?()

@end

@implementation?SecondViewController

-?(void)viewDidLoad?{

[superviewDidLoad];

//?Do?any?additional?setup?after?loading?the?view.

}

-?(void)secondVCMethod?{

NSLog(@"This?is?secondVC?method?!");

}

-?(void)didReceiveMemoryWarning?{

[superdidReceiveMemoryWarning];

//?Dispose?of?any?resources?that?can?be?recreated.

}

/*

#pragma?mark?-?Navigation

//?In?a?storyboard-based?application,?you?will?often?want?to?do?a?little?preparation?before?navigation

-?(void)prepareForSegue:(UIStoryboardSegue?*)segue?sender:(id)sender?{

//?Get?the?new?view?controller?using?[segue?destinationViewController].

//?Pass?the?selected?object?to?the?new?view?controller.

}

*/

@end

工程結(jié)構(gòu)應(yīng)該是這樣的

工程目錄圖

現(xiàn)在我想在ViewController中調(diào)用- (void)secondVCMethod這個方法,我們知道ViewController和SecondViewController并無繼承關(guān)系吹散,按照正常的步驟去做程序肯定會因為在ViewController找不到- (void)secondVCMethod這個方法而直接崩潰的

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26//

//??ViewController.m

//??RuntimeTest2

//

//??Created?by?HenryCheng?on?15/12/24.

//??Copyright??(版權(quán)符號)?2015年?www.igancao.com??All?rights?reserved.

//

#import?"ViewController.h"

#import?@interface?ViewController?()

@end

@implementation?ViewController

-?(void)viewDidLoad?{

[superviewDidLoad];

[self?performSelector:@selector(secondVCMethod)];

}

-?(void)didReceiveMemoryWarning?{

[superdidReceiveMemoryWarning];

//?Dispose?of?any?resources?that?can?be?recreated.

}

@end

運行結(jié)果

1

2

3**2015-12-24?13:54:44.314?RuntimeTest2[3164:835814]?-[ViewController?secondVCMethod]:?unrecognized?selector?sent?to?instance?0x7fc3a8535c10**

**2015-12-24?13:54:44.317?RuntimeTest2[3164:835814]?***?Terminating?app?due?to?uncaught?exception'NSInvalidArgumentException',?reason:'-[ViewController?secondVCMethod]:?unrecognized?selector?sent?to?instance?0x7fc3a8535c10'**

*****?Firstthrowcall?stack:**

現(xiàn)在我們來處理一下這個消息弧械,如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41//

//??ViewController.m

//??RuntimeTest2

//

//??Created?by?HenryCheng?on?15/12/24.

//??Copyright??(版權(quán)符號)?2015年?www.igancao.com?All?rights?reserved.

//

#import?"ViewController.h"

#import?@interface?ViewController?()

@end

@implementation?ViewController

-?(void)viewDidLoad?{

[superviewDidLoad];

[self?performSelector:@selector(secondVCMethod)];

}

-?(id)forwardingTargetForSelector:(SEL)aSelector?{

Class?class?=?NSClassFromString(@"SecondViewController");

UIViewController?*vc?=?class.new;

if(aSelector?==?NSSelectorFromString(@"secondVCMethod"))?{

NSLog(@"secondVC?do?this?!");

returnvc;

}

returnnil;

}

+?(BOOL)resolveInstanceMethod:(SEL)sel?{

return[superresolveInstanceMethod:sel];

}

-?(void)didReceiveMemoryWarning?{

[superdidReceiveMemoryWarning];

//?Dispose?of?any?resources?that?can?be?recreated.

}

@end

運行結(jié)果

1

2**2015-12-24?14:00:34.168?RuntimeTest2[3284:870957]?secondVCdothis!**

**2015-12-24?14:00:34.169?RuntimeTest2[3284:870957]?This?is?secondVC?method?!**

我們會發(fā)現(xiàn)- (void)secondVCMethod這個方法執(zhí)行了,程序也并沒有崩潰空民,原因就是在這一步

1

2

3

4

5

6

7

8

9

10-?(id)forwardingTargetForSelector:(SEL)aSelector?{

Class?class?=?NSClassFromString(@"SecondViewController");

UIViewController?*vc?=?class.new;

if(aSelector?==?NSSelectorFromString(@"secondVCMethod"))?{

NSLog(@"secondVC?do?this?!");

returnvc;

}

returnnil;

}

在沒有找到- (void)secondVCMethod這個方法的時候刃唐,消息繼續(xù)傳遞羞迷,直到- (id)forwardingTargetForSelector:(SEL)aSelector,然后我在里面創(chuàng)建了一個SecondViewController的對象画饥,并且判斷如過有這個方法衔瓮,就返回SecondViewController的對象。這個函數(shù)就是消息的轉(zhuǎn)發(fā)抖甘,在這兒我們成功的把消息傳給了SecondViewController热鞍,然后讓它來執(zhí)行,所以就執(zhí)行了那個方法衔彻。同時薇宠,也相當(dāng)于完成了一個多繼承!

三艰额、最后一點

當(dāng)然澄港,還有好幾個函數(shù),在上面那張圖里面已經(jīng)清晰的表達了柄沮,有興趣的可以自己試試回梧,看看消息的傳遞順序到底是怎么樣的。上面提到的這些知識runtime的冰山一角祖搓,runtime黑魔法的強大遠不止于此狱意,比如方法的調(diào)配(Method Swizzling)等,在項目實戰(zhàn)中還是很有用的拯欧,后面有時間會再介紹.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末详囤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子哈扮,更是在濱河造成了極大的恐慌纬纪,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滑肉,死亡現(xiàn)場離奇詭異包各,居然都是意外死亡,警方通過查閱死者的電腦和手機靶庙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門问畅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人六荒,你說我怎么就攤上這事护姆。” “怎么了掏击?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵卵皂,是天一觀的道長。 經(jīng)常有香客問我砚亭,道長灯变,這世上最難降的妖魔是什么殴玛? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮添祸,結(jié)果婚禮上滚粟,老公的妹妹穿的比我還像新娘。我一直安慰自己刃泌,他們只是感情好凡壤,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著耙替,像睡著了一般亚侠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上林艘,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天盖奈,我揣著相機與錄音混坞,去河邊找鬼狐援。 笑死,一個胖子當(dāng)著我的面吹牛究孕,可吹牛的內(nèi)容都是我干的啥酱。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼厨诸,長吁一口氣:“原來是場噩夢啊……” “哼镶殷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起微酬,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤绘趋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后颗管,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陷遮,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年垦江,在試婚紗的時候發(fā)現(xiàn)自己被綠了帽馋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡比吭,死狀恐怖绽族,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情衩藤,我是刑警寧澤吧慢,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站赏表,受9級特大地震影響检诗,放射性物質(zhì)發(fā)生泄漏怖喻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一岁诉、第九天 我趴在偏房一處隱蔽的房頂上張望锚沸。 院中可真熱鬧,春花似錦涕癣、人聲如沸哗蜈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽距潘。三九已至,卻和暖如春只搁,著一層夾襖步出監(jiān)牢的瞬間音比,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工氢惋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留洞翩,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓焰望,卻偏偏與公主長得像骚亿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子熊赖,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容