本文為投稿文章,作者:minggo
相比“凌波微步”的Swift,Object-C被譽為“如來神掌”。傳說Runtime就是支持這“如來神掌”說法的最好體現。聽起來總是這麼的神秘高級,於是總能在各個論壇看到碎片資料,時間一長總記不住哪裡是哪裡,每次都要打開好幾個網頁。這種記不住象現顯然是知識體系還不完整重要體現。還是自己從Runtime的思想到動手代碼呈現上做出總結尚為上策。
學習流程圖
一、基本概念
RunTime簡稱運行時,就是系統在運行的時候的一些機制,其中最主要的是消息機制。
對於C語言,函數的調用在編譯的時候會決定調用哪個函數,編譯完成之後直接順序執行,無任何二義性。
OC的函數調用成為消息發送。屬於動態調用過程。在編譯的時候並不能決定真正調用哪個函數(事實證明,在編 譯階段,OC可以調用任何函數,即使這個函數並未實現,只要申明過就不會報錯。而C語言在編譯階段就會報錯)。
只有在真正運行的時候才會根據函數的名稱找 到對應的函數來調用。
官網文檔還提供關於傳統和現代版本Runtime的說明
In the legacy runtime, if you change the layout of instance variables in a class, you must recompile classes that inherit from it.
In the modern runtime, if you change the layout of instance variables in a class, you do not have to recompile classes that inherit from it.In addition, the modern runtime supports instance variable synthesis for declared properties (see Declared Properties in The Objective-C Programming Language).
二、知曉OC的方法調用在Runtime中具體的實現
1.OC代碼調用一個方法
[self.loginBt login];
2.在編譯時RunTime會將上述代碼轉化成[發送消息]
objc_msgSend(self.loginB,@selector(login));
三、常見的作用
既然是“如來神掌”,簡直可以無法無天啦,當街攔下一個人問道“這是馬還是鹿啊?”,那人看是Runtime大人懼怕道“Runtime大人,您說是馬就是馬,是鹿就是鹿~”。Runtime大快“wow哈哈哈~,見你乖巧,我也不為難於你。你缺頭驢是吧?,本大人現在造一頭送於你,遷回家便是!喔~哈哈哈”。
呵呵,扯遠了,回到Runtime作用上。無所不能的事情就不一一介紹了,梳理下較為可能用的幾個地方:
1. 動態的添加對象的成員變量和方法
2. 動態交換兩個方法的實現
3. 實現分類也可以添加屬性
4. 實現NSCoding的自動歸檔和解檔
5. 實現字典轉模型的自動轉換
四、編寫代碼實現
1. 動態變量控制
1)Sense:
Teacher: What's your name?
XiaoMing: My name is XiaoMing.
Teacher: Pardon?
XiaoMing: My name is __
在程序當中,假設XiaoMing的name原來的值為XiaoMing,後來被Runtime偷換了一個名字叫Minggo。那麼,Runtime是如何做到的呢?
2)Step:
①動態獲取XiaoMing類中的所有屬性[當然包括私有]
Ivar *ivar = class_copyIvarList([self.xiaoMing class], &count);
②遍歷屬性找到對應name字段
const char *varName = ivar_getName(var);
③修改對應的字段值成Minggo
object_setIvar(self.xiaoMing, var, @"Minggo");
3)Show Code:
2. 動態交換方法
1)Sense:
Teacher: What's your name?
XiaoMing: My name is XiaoMing.
Teacher: Pardon?
XiaoMing: My name is __
在程序當中,假設XiaoMing的第一次回答為firstSay,後來被Runtime交換了一個名字叫secondSay的方法,最終再調用firstSay的時候,其實是調用了secondSay的實現。那麼,Runtime是如何做到的呢?
2)Step:
①動態找到firstSay和secondSay方法
Method m1 = class_getInstanceMethod([self.xiaoMing class], @selector(firstSay)); Method m2 = class_getInstanceMethod([self.xiaoMing class], @selector(secondSay));
②交換兩個方法
method_exchangeImplementations(m1, m2);
3)Show Code:
-(void)answer{ Method m1 = class_getInstanceMethod([self.xiaoMing class], @selector(firstSay)); Method m2 = class_getInstanceMethod([self.xiaoMing class], @selector(secondSay)); method_exchangeImplementations(m1, m2); NSString *secondName = [self.xiaoMing firstSay]; self.nameTf.text = secondName; NSLog(@"XiaoMing:My name is %@",secondName); }
3. 動態添加方法
1)Sense:
Teacher: Where is LiLei from?
XiaoMing: I don't know.
Teacher: Guess?.
LiHua: He is from __
在程序當中,假設XiaoMing的中沒有guess這個方法,後來被Runtime添加一個名字叫guess的方法,最終再調用guess方法做出相應。那麼,Runtime是如何做到的呢?
2)Step:
①動態給XiaoMing類中添加guess方法:
class_addMethod([self.xiaoMing class], @selector(guess), (IMP)guessAnswer, "v@:");
這裡參數地方說明一下:
(IMP)guessAnswer 意思是guessAnswer的地址指針;
"v@:" 意思是,v代表無返回值void,如果是i則代表int;@代表 id sel; : 代表 SEL _cmd;
“v@:@@” 意思是,兩個參數的沒有返回值。
②調用guess方法響應事件:
[self.xiaoMing performSelector:@selector(guess)];
③編寫guessAnswer的實現:
void guessAnswer(id self,SEL _cmd){ NSLog(@"He is from GuangTong"); }
這個有兩個地方留意一下:
1.void的前面沒有+、-號,因為只是C的代碼。
2.必須有兩個指定參數(id self,SEL _cmd)
3)Show Code:
-(void)answer{ class_addMethod([self.xiaoMing class], @selector(guess), (IMP)guessAnswer, "v@:"); if ([self.xiaoMing respondsToSelector:@selector(guess)]) { [self.xiaoMing performSelector:@selector(guess)]; } else{ NSLog(@"Sorry,I don't know"); } self.cityTf.text = @"GuangTong"; } void guessAnswer(id self,SEL _cmd){ NSLog(@"He is from GuangTong"); }
4. 動態為Category擴展加屬性
這一點上有兩點要表達一下:第一,XCode運行你在Category的.h文件申明@Property,編譯通過,但運行時如果沒有Runtime處理,進行賦值取值,就馬上報錯。第二,這一點是iOS面試當中經常面到的問題:如何給擴展添加屬性?
1)Sense:
Teacher: What's your Chinese name?
XiaoMing: I have no one.
LiHua: You should have one.
LiHua: Your Chinese name is __
在程序當中,假設XiaoMing的中沒有chineseName這個屬性,後來被Runtime添加一個名字叫chineseName的屬性。那麼,Runtime是如何做到的呢?
2)Step:
①申明chineseName屬性
#import "XiaoMing.h" @interface XiaoMing (MutipleName) @property(nonatomic,copy) NSString *chineseName; @end
②動態添加屬性和實現方法
③使用chineseName屬性
-(void)answer{ NSLog(@"My Chinese name is %@",self.xiaoMing.chineseName); self.chineseNameTf.text = self.xiaoMing.chineseName; }
3)Show Code:
上邊就是最要的Code了。以下更精彩。
五、效果圖更直觀
六、源碼下載地址更詳細
https://github.com/minggo620/iOSRuntimeLearn.git