runtime解析及常用方法

作者: 其字德安 | 来源:发表于2016-03-27 18:57 被阅读147次

    什么是runtime?

    • runtime直译:运行时机制;OC发送消息的本质,就是 runtime去调用苹果底层的一些函数;

    • C语言在编译时,就知道该调用那些方法, 能成功调用吗;

    • 基于runtime的OC语言, 在编译阶段只要方法有声明,就不会报错;在运行时才会检测到底有没有方法,该调用哪个方法;

    查看runtime底层实现:

    • 在终端编译文件 clang -rewrite-objc main.m Person.m 查看最终生成代码
        // 测试代码
        Person *p = [Person alloc];
        p = [p init];
    
    

    生成cpp文件,找到对应代码:

    
        Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc"));
    
        p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("init"));
    
    

    解析: 简化代码后,其实runtime底层也就是调用苹果封装的方法而已

       (Person *(*)(id, SEL))(void *) 强制类型转换, 把objc_msgSend转换成有返回值(Person *), 两个参数(id, SEL)的指向函数的指针;故可去掉,简化代码如下:
    
        Person *p = objc_msgSend([Person class], @selector(alloc));
        p = objc_msgSend(p, @selector(init));
    
    
    由于苹果不推荐我们使用其底层的runtime, 但是有时一些功能只能由runtime实现,故我们首先配置Xcode(以XCocde7为例)
    • 导入头文件#import <objc/message.h>
    • 配置文件 : 项目 ---> BuildSetting ----> msg
    Snip20160327_1.png

    runtime常用方法:

    • 动态添加方法
      • 开发使用场景:如果一个类方法非常多,加载类到内存的时候要给每个方法生成相应映射表,非常耗费内存资源;这时候可以实现用动态给某个类添加方法.
      • 比如某应用的VIP用户才能使用一些功能.

    代码实现:

    @implementation Person
    
    // 定义函数
    // 默认OC方法都有两个隐式参数,self,_cmd
    void run(id self, SEL _cmd) {
        NSLog(@"run");
    }
    
    // 只要调用没有实现的方法 就会来到方法
    // 作用:去解决没有实现方法,动态添加方法
    + (BOOL)resolveInstanceMethod:(SEL)sel{
    
        if (sel == @selector(run)) {
            // 添加方法
            class_addMethod(self, sel, (IMP)run, nil);
    
            return YES;
        }
    
        return [super resolveInstanceMethod:sel];
    }
    
    @end
    
    方法解析:
        // 添加方法到类
        class_addMethod(__unsafe_unretained Class cls, SEL name, IMP imp, const char *types);
    
            class:给谁添加方法
            SEL:添加哪个方法
            IMP:方法实现,函数入口,传入函数名
            type:方法类型 默认nil即可
    

    然后我们利用performSelector方法调用一个没有实现的方法:

        [p performSelector:@selector(run)]
    
        // 会自动执行上面方法添加一个动态方法run
        // 打印输出:run
    

    tips:

    // 没有实现对象方法时,调用该方法
    +(BOOL)resolveInstanceMethod:(SEL)sel {
    
        // 添加方法
    }
    
    // 没有实现类方法时, 调用该方法
    +(BOOL)resolveClassMethod:(SEL)sel {
    
        // 添加方法
    }
    
    • 交换两个方法的实现
    • 需求:想要在调用imageNamed加载图片时,提示是否加载成功,
    • 特别是大项目时,不希望更改系统方法(得改好多代码, 工作量忒大...)

    此时可以使用runtime的动态交换方法来实现功能:

    • 新建一个UIImage的分类

    • 在分类中添加一个该功能的方法;(注意别覆盖系统方法)

    • 在+load方法中实现方法的交换

    #import "UIImage+image.h"
    #import <objc/message.h>
    
    @implementation UIImage (image)
    +(void)load {
    
        // 1.0 获取方法
        Method abel_imageNamed = class_getClassMethod(self, @selector(abel_imageNamed:));
        Method imageNamed = class_getClassMethod(self, @selector(imageNamed:));
    
        // 2.0 交换方法的实现
        method_exchangeImplementations(abel_imageNamed, imageNamed);
    
    }
    
    // 添加该功能方法
    + (UIImage *)abel_imageNamed:(NSString *)name
    {
        // 调用系统方法
        UIImage *image = [self abel_imageNamed:name];
    
        // 添加功能
        if (image == nil) {
    
            NSLog(@"加载失败");
        }
        return image;
    }
    
    @end
    
    

    相关文章

      网友评论

        本文标题:runtime解析及常用方法

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