美文网首页移动端开发
Runtime 运行时(未完待续)

Runtime 运行时(未完待续)

作者: 码路芽子 | 来源:发表于2016-09-23 15:56 被阅读9次

    Runtime简介

    • Runtime简称运行时. OC就是运行时机制, 也就是在运行的时候的一些机制, 其中最主要的是消息机制
    • 对于C语言, 函数的调用在编译的时候会决定调用哪个函数
    • 对于OC的函数, 属于动态调用过程, 在编译的时候并不能决定真正调用哪个函数, 只有在真正运行的时候才会根据函数的名字来调用
    • 事实证明
      • 在编译阶段, OC可以调用任何函数, 即使这个函数并未实现, 只要声明过就不会报错
      • 在编译阶段, C语言调用未实现的函数就会报错

    Runtime各种方法使用

    Runtime消息机制

    • 介绍
    • 方法调用本质: 就是发送一个消息, 用Runtime发送消息, OC底层是通过Runtime实现
    
        导入头文件 `#import <objc/message.h>`
    
        id objc = [NSObject alloc];
        objc = [objc init];
        /**
         * 最终生成消息机制, 编辑器做的事情
         * 最终代码, 需要把当前代码重新编译, 用xcode编译器, clang
         * clang -rewrite-objc main.m 查看最终生成代码
         * Runtime都有个前缀, 谁的事情就使用谁
         */
         /**
         * id: 谁发送消息
         * SEL: 发送什么消息
         */
    
        
        /**
         * xcode6之前, 苹果可以使用objc_msgSend, 而且有参数提示
         * xcode6之后不推荐使用Runtime
         * 解决方法: 找到build setting -> 搜索msg, 改成NO, 不用严格检查
                这样Runtime就正常了
         */
        
        /**
         * id objc = [NSObject alloc];
         * objc = [objc init];.
         
         * 用Runtime写这两句
           *太麻烦 id objc = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)([NSObject class], @selector(alloc));
         */
         id objc = objc_msgSend([NSObject class], @selector(alloc));
        objc = objc_msgSend(objc, @selector(init));
        
        
        ******************
        //TODO: 什么时候调用Runtime, 方法调用流程
        /** 最底层写法 */
        Person *per = [Person alloc];
        objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"));
        per = [per init];
        objc_msgSend(per, sel_registerName("init"));
    
        /** 调用对象方法 */
        objc_msgSend(per, @selector(eat));
    

    什么时候使用runtime

    不得不用runtime消息机制, 可以调用私有方法, 因为正常的没有声明的方法, oc不能使用

    //TODO: 什么时候调用Runtime, 方法调用流程
        /** 最底层写法 */
        Person *per = [Person alloc];
        objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"));
        per = [per init];
        objc_msgSend(per, sel_registerName("init"));
      
        /** 调用对象方法 */
        objc_msgSend(per, @selector(eat));
        objc_msgSend(per, @selector(run:), 20);
    

    开发中使用场景

    • Runtime的方法都是有前缀的, 谁的事情谁开头

    • 方法调用流程

    • 怎么去调用eat方法, 对象方法: 类对象的方法列表中 类方法: 元类中方法列表

    • 1.通过isa去对应的类中查找方法


      QQ20160923-0.png

      2.注册方法编号
      3.根据方法编号去查找对应方法
      4.找到的只是最终函数实现地址, 根据地址去方法区调用对应函数

    • 内存的5大区

      1. 栈区 -> 不需要手动管理内存, 自动管理
      2. 堆 -> 需要手动管理内存, 根据地址去方法去调用
      3. 静态区
      4. 常量区
      5. 方法区

    二 交换方法

    案例

    /** 把类记载进内存的时候只调用一次 */
    + (void)load
    {
        /** 获取方法
           * imageNamed
           * wdy_imageNamed
         */
        /** 获取那个类方法, 获取那个方法 */
        Method imageNamedMethod = class_getInstanceMethod(self, @selector(imageNamed:));
        Method wdy_imagenameMethod = class_getInstanceMethod(self, @selector(wdy_imageNamed:));
        
        /** Runtime交换方法 */
        method_exchangeImplementations(imageNamedMethod, wdy_imagenameMethod);
        
    }
    
    /** 调用多次  */
    + (void)initialize
    {
        
    }
    
    /** 加载图片 */
    /** 判断是否加载成功 */
    + (UIImage *)wdy_imageNamed:(NSString *)name
    {
        UIImage *image = [self wdy_imageNamed:name];
        if (image) {
            NSLog(@"加载成功");
        } else {
            NSLog(@"加载失败");
        }
        return image;
    }
    
    /**
     * Runtime(交换方法):
     * 需求:让UIImage加载图片, 告诉我是否成功
        解决方法: 
        1.自定义UIimage
            弊端:
            *.每次使用, 都需要导入
            *.项目大了, 没办法实现
        2.用UIimage方法
            给系统imageNamed添加功能, 只能用Runtime
     
        解决方法: 
            1.给系统添加方法分类
            2.自己实现一个带有扩展功能的方法
            3.交换方法, 只需要交换一次    
     *
     */
    
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        /** 麻烦的方法 */
        UIImage *image = [UIImage imageNamed:@"1.png"]; 
    }
    
    

    三 动态添加方法

    • 描述: 添加一个类, 想要在适当的时候添加一个方法, 例如当用户成为会员的时候, 进行一些操作
    • 问题: Runtime动态添加方法:OC中都是懒加载机制, 只要有个方法实现了, 就立马添加到方法列表中
      美团有个面试题 有没有使用过 self performSelector:<#(SEL)#>, 什么时候使用?动态添加方法的时候使用过.
      怎么动态添加方法?用Runtime添加. 为什么要动态添加?

    */

    • 准备: 在ViewDidLoad中创建一个类, 并且添加一个方法

        - (void)viewDidLoad {
          [super viewDidLoad];
           
          Person *per = [[Person alloc] init];
          [per performSelector:@selector(eat)];
      
          [per performSelector:@selector(run:) withObject:@10];
      }
      
      

    在类中添加一个方法

    • 这个方法在一个地方调用了未实现的方法就会调用: + (BOOL)resolveInstanceMethod:(SEL)sel
    
    /** 没有返回值没有参数 */
    void aaa(id self, SEL _cmd) {
       NSLog(@"吃东西啊啊 啊啊啊啊!");
    }
    
    void run(id self, SEL _cmd, NSNumber *num) {
       NSLog(@"跑了%@", num);
    }
    
    
    /**
    * 解决添加的方法
    * 什么时候调用方法: 只要一个对象调用了一个未实现的方法就会调用这个方法, 进行处理 
    * 作用: 动态添加方法, 处理未实现
    */
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
       /** 任何方法都有两个隐士:sel, cmd->方法编号 */
       if (sel == NSSelectorFromString(@"eat")) {
           /**
            * 给谁添加方法
            * SEL: 添加那个方法
            * IMP: 方法实现
            * type: 方法类型
            */
           class_addMethod(self, sel, (IMP)aaa, "v@:");
       }
       
       if (sel == NSSelectorFromString(@"run:")) {
           class_addMethod(self, sel, (IMP)run, "v@:@");
       }
    
     return [super resolveInstanceMethod:sel];
    }
    
    

    相关文章

      网友评论

        本文标题:Runtime 运行时(未完待续)

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