談Runtime機制和使用的整體化梳理
來自: http://www.jianshu.com/p/8916ad5662a2
相比“凌波微步”的swift,Object-C被譽為“如來神掌”。傳說Runtime就是支持這“如來神掌”說法的最好體現。聽起來總是這么的神秘高級,于是總能在各個論壇看到碎片資料,時間一長總記不住哪里是哪里,每次都要打開好幾個網頁。這種記不住象現顯然是知識體系還不完整重要體現。還是自己從Runtime的思想到動手代碼呈現上做出總結尚為上策。
一.基本概念
- RunTime簡稱運行時,就是系統在運行的時候的一些機制,其中最主要的是消息機制。
- 對于C語言,函數的調用在編譯的時候會決定調用哪個函數( C語言的函數調用請看這里 )。編譯完成之后直接順序執行,無任何二義性。
- OC的函數調用成為消息發送。屬于動態調用過程。在編譯的時候并不能決定真正調用哪個函數(事實證明,在編 譯階段,OC可以調用任何函數,即使這個函數并未實現,只要申明過就不會報錯。而C語言在編譯階段就會報錯)。
- 只有在真正運行的時候才會根據函數的名稱找 到對應的函數來調用。
</ol> </blockquote>
官網文檔還提供關于傳統和現代版本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).</ol> </blockquote>
二.知曉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:
-(void)answer{ unsigned int count = 0; Ivar ivar = class_copyIvarList([self.xiaoMing class], &count); for (int i = 0; i<count; i++) { Ivar var = ivar[i]; const char varName = ivar_getName(var); NSString *name = [NSString stringWithUTF8String:varName];if ([name isEqualToString:@"_englishName"]) { object_setIvar(self.xiaoMing, var, @"Minggo"); break; } } NSLog(@"XiaoMing first answer is %@",self.xiaoMing.englishName); self.nameTf.text = self.xiaoMing.englishName;}</pre>
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);}</pre>
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");}</pre>
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</pre>
②動態添加屬性和實現方法
#import "XiaoMing+MutipleName.h"import <objc/runtime.h>
@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</pre>
③使用chineseName屬性
-(void)answer{ NSLog(@"My Chinese name is %@",self.xiaoMing.chineseName); self.chineseNameTf.text = self.xiaoMing.chineseName; }3)Show Code:
上邊就是最要的Code了。以下更精彩。
五.效果圖更直觀
本文由用戶 dahz6994 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!