美文网首页iOSiOS 开发 iOS开发记录
谈Runtime机制和使用的整体化梳理

谈Runtime机制和使用的整体化梳理

作者: minggo | 来源:发表于2016-01-20 17:52 被阅读7364次

    相比“凌波微步”的swift,Object-C被誉为“如来神掌”。传说Runtime就是支持这“如来神掌”说法的最好体现。听起来总是这么的神秘高级,于是总能在各个论坛看到碎片资料,时间一长总记不住哪里是哪里,每次都要打开好几个网页。这种记不住象现显然是知识体系还不完整重要体现。还是自己从Runtime的思想到动手代码呈现上做出总结尚为上策。

    学习流程图

    一.基本概念

    1. RunTime简称运行时,就是系统在运行的时候的一些机制,其中最主要的是消息机制。
    1. 对于C语言,函数的调用在编译的时候会决定调用哪个函数,编译完成之后直接顺序执行,无任何二义性。
    2. OC的函数调用成为消息发送。属于动态调用过程。在编译的时候并不能决定真正调用哪个函数(事实证明,在编 译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错。而C语言在编译阶段就会报错)。
    3. 只有在真正运行的时候才会根据函数的名称找 到对应的函数来调用。

    官网文档还提供关于传统和现代版本Runtime的说明

    1. In the legacy runtime, if you change the layout of instance variables in a class, you must recompile classes that inherit from it.
    1. 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:
    -(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;
    }
    

    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
    

    ②动态添加属性和实现方法

    #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  
    

    ③使用chineseName属性

    -(void)answer{
        NSLog(@"My Chinese name is %@",self.xiaoMing.chineseName);
        self.chineseNameTf.text = self.xiaoMing.chineseName;
    }  
    
    3)Show Code:

    上边就是最要的Code了。以下更精彩。

    五.效果图更直观

    微信公众号:minggo_dev

    相关文章

      网友评论

      • lfb_CD:写得真的很清晰,顺便问一下,我能转载到微信公众号么?会注明本文章的链接以及作者的信息的
        lfb_CD:@minggo :+1:
        minggo:@lfb_CD 可以
      • 小小云x:以前只知其一,越来越了解
      • 大刀阔斧007:受益匪浅,希望楼主多多分享啊
      • 小木___Boy:请问gif 是用什么工具做的
        小木___Boy:@minggo :+1:
        minggo: @小木___Boy GIFBrewery
      • 处处闻笛鸟花落知多少谁知:很想知道什么情况下会需要用runtime给类添加方法?:ghost:
        minggo:@处处闻笛鸟花落知多少谁知 想体验,写个动态添加一个方修改系统某个类中几私有变量啦。至于第三方,以后留意到艾特你!
        处处闻笛鸟花落知多少谁知:@minggo 有没有哪个第三方轮子有用到这种方式的?我想学习体验下。:bow:
        minggo:@处处闻笛鸟花落知多少谁知 方法无非处理操作数或整合其它方法,比如要多次使用一类的多个私有成员变量,每用runtime方式找到显得累赘冗余,还不如动态添加方法组合这多个成员变量,最后暴露出来。
      • WhisperKarl:简直太棒了
        minggo: @Whisper悟空 谢谢支持!
      • 小木___Boy:楼主遗留了两处,如果完善则是极好的

        实现NSCoding的自动归档和解档
        实现字典转模型的自动转换
        minggo:@小木___Boy 我有空补上放到GitHub上,到时候艾特你!
      • TryEnough:谢谢
      • 方克己:我给你满分。
      • 刘栋:66666666
      • 我是C:捞干的来,👍
      • DevinWu:学习了,楼主讲解的通俗易懂
        minggo: @DevinWu 谢谢!
      • 天命肥:对runtime的理解又加深了,通俗易懂。:+1:
        minggo: @天命肥 谢谢支持
      • 00715f4b0537:挺好的 讲明白不少了,学习了 :smile:
        minggo:@无泪有桑 谢谢支持!
      • 未来的自己在哪里:简直通俗易懂
        minggo: @未来的自己在哪里 谢谢支持!
      • e73559e118ad:很详细,写得也挺幽默。 :+1:
        minggo:@_LiYong 谢谢支持
      • 夏堇菌: :smile: 支持小明一个
      • c37e3f987f84:小明无处不在啊,哈哈 :+1: :+1:
        minggo:@图涨的空杯 小明比较经典,呵呵
      • d696133f00e5:有github代码学习很赞。
        minggo:@SnowChen 呵呵,谢谢支持
      • e624803c25de: :smile: :smile: 描写Runtime无所不能那段脑洞很有意思!
        minggo:@赛码 呵呵,谢谢支持
      • 溪枫狼::+1:🏻。学习思路值得参考
        minggo:@溪枫狼 :smile:
      • b9de1534baf7:很全,收下!

      本文标题:谈Runtime机制和使用的整体化梳理

      本文链接:https://www.haomeiwen.com/subject/qqjlkttx.html