相比“凌波微步”的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程式碼呼叫一個方法
1 |
[self.loginBt login]; |
2.在編譯時RunTime會將上述程式碼轉化成[傳送訊息]
1 |
objc_msgSend(self.loginB,@selector(login)); |
三.常見的作用
既然是“如來神掌”,簡直可以無法無天啦,當街攔下一個人問道“這是馬還是鹿啊?”,那人看是Runtime大人懼怕道“Runtime大人,您說是馬就是馬,是鹿就是鹿~”。Runtime大快“wow哈哈哈~,見你乖巧,我也不為難於你。你缺頭驢是吧?,本大人現在造一頭送於你,遷回家便是!喔~哈哈哈”。
呵呵,扯遠了,回到Runtime作用上。無所不能的事情就不一一介紹了,梳理下較為可能用的幾個地方:
1. 動態的新增物件的成員變數和方法
2. 動態交換兩個方法的實現
3. 實現分類也可以新增屬性
4. 實現NSCoding的自動歸檔和解檔
5. 實現字典轉模型的自動轉換
四.編寫程式碼實現
1. 動態變數控制
1)Sense:
1 2 3 4 |
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類中的所有屬性[當然包括私有]
1 |
Ivar *ivar = class_copyIvarList([self.xiaoMing class], &count); |
②遍歷屬性找到對應name欄位
1 |
const char *varName = ivar_getName(var); |
③修改對應的欄位值成Minggo
1 |
object_setIvar(self.xiaoMing, var, @"Minggo"); |
3)Show Code:
1 2 3 4 |
-(void)answer{ unsigned int count = 0; Ivar *ivar = class_copyIvarList([self.xiaoMing class], &count); for (int i = 0; i |
2. 動態交換方法
1)Sense:
1 2 3 4 |
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方法
1 2 |
Method m1 = class_getInstanceMethod([self.xiaoMing class], @selector(firstSay)); Method m2 = class_getInstanceMethod([self.xiaoMing class], @selector(secondSay)); |
②交換兩個方法
1 |
method_exchangeImplementations(m1, m2); |
3)Show Code:
1 2 3 4 5 6 7 8 9 10 11 |
-(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:
1 2 3 4 |
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方法:
1 |
class_addMethod([self.xiaoMing class], @selector(guess), (IMP)guessAnswer, "v@:"); |
這裡引數地方說明一下:
(IMP)guessAnswer 意思是guessAnswer的地址指標;
“v@:” 意思是,v代表無返回值void,如果是i則代表int;@代表 id sel; : 代表 SEL _cmd;
“v@:@@” 意思是,兩個引數的沒有返回值。
②呼叫guess方法響應事件:
1 |
[self.xiaoMing performSelector:@selector(guess)]; |
③編寫guessAnswer的實現:
1 2 3 |
void guessAnswer(id self,SEL _cmd){ NSLog(@"He is from GuangTong"); } |
這個有兩個地方留意一下:
1.void的前面沒有+、-號,因為只是C的程式碼。
2.必須有兩個指定引數(id self,SEL _cmd)
3)Show Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
-(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:
1 2 3 4 |
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屬性
1 2 3 4 5 6 7 |
#import "XiaoMing.h" @interface XiaoMing (MutipleName) @property(nonatomic,copy) NSString *chineseName; @end |
②動態新增屬性和實現方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#import "XiaoMing+MutipleName.h" #import @implementation XiaoMing (MutipleName) char cName; -(void)setChineseName:(NSString *) chineseName{ objc_setAssociatedObject(self, &cName, chineseName, OBJC_ASSOCIATION_COPY_NONATOMIC); } -(NSString *)chineseName{ return objc_getAssociatedObject(self, &cName); } @end |
③使用chineseName屬性
1 2 3 4 |
-(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
【原創出品 未經授權 禁止轉載】
【歡迎微友分享轉發 禁止公號等未經授權的轉載】