RunTime

作者: fjytqiu | 来源:发表于2016-10-09 18:47 被阅读69次

一、runtime简介

Objective-C是一种动态语言,所谓动态语言,是在程序执行时动态的确定变量类型,执行变量类型对应的方法的。因此,在Object-C中常用字符串映射类的技巧来动态创建类对象。因为OC的动态语言特性,我们可以通过一些手段,在程序运行时动态的更改对象的变量甚至方法,这就是我们所说的runtime机制。

  1. RunTime简称运行时。OC是动态语言,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会确定变量类型,根据函数的名称找到对应的函数来调用。 而C语言,函数的调用在编译的时候会决定调用哪个函数。
  2. 在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错 ,而C语言调用未实现的函数就会报错。

二、runtime作用( 必须导入#import <objc/message.h> ;在build setting中搜objc_msg设置“NO”)

1.发送消息(最主要的运行时机制)

方法调用的本质,就是让对象发送消息。
objc_msgSend,只有对象才能发送消息,因此以objc开头.
消息机制原理:对象根据方法编号SEL去映射表查找对应的方法实现

    Dog *dog = [[Dog alloc] init];
    [dog bite];    // 对象方法
    //  底层是实现,对象发送消息
    objc_msgSend(dog, @selector(bite));
    
    
    [Dog bite];    // 类方法方法
    //  底层是实现,底层会自动把类名转换成类对象调用
    objc_msgSend([Dog class], @selector(bite));
2.交换方法(黑魔法)

需求: 在保持原有的功能的情况下,给系统自带的方法扩展一些功能。
方案:
一: 继承系统的类,重写方法.
二: 使用runtime,交换方法.

:给imageNamed方法扩展功能,每次加载图片后打印“输出图片”。


@implementation UIImage (Extention)

+ (void)load {
    // 交换方法
    // 1. 获取imageWithName方法地址
    Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));
    
    // 2. 获取imageWithName方法地址
    Method imageName = class_getClassMethod(self, @selector(imageNamed:));
    
    // 3. 交换方法地址,相当于交换实现方式
    method_exchangeImplementations(imageWithName, imageName);
    
}

+ (UIImage *)imageWithName:(NSString *)icon {
    
    // load方法中方法交换后,这里调用imageWithName,就相当于调用imageName(并不会发生死循环)
    UIImage *image = [self imageWithName:icon];
    NSLog(@"输出图片");
    return image;
}

3.动态添加方法

需求:如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。
经典面试题:有没有使用performSelector,其实主要想问你有没有动态添加过方法。
简单使用

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    Dog *dog = [[Dog alloc] init];
    [dog performSelector:@selector(eat)];    // 对象方法,直接使用[dog eat;似乎也可以
}

@end



@implementation Dog
// void(*)()
// 默认方法都有两个隐式参数,
void eat(id self,SEL sel)
{
    NSLog(@"%@ %@",self,NSStringFromSelector(sel));
}

// 当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来.
// 刚好可以用来判断,未实现的方法是不是我们想要动态添加的方法

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    
    if (sel == @selector(eat)) {
        
        // 动态添加eat方法
        
        // 第一个参数:给哪个类添加方法
        // 第二个参数:添加方法的方法编号
        // 第三个参数:添加方法的函数实现(函数地址)
        // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
        class_addMethod(self, @selector(eat), eat, "v@:");
    }
    
    return [super resolveInstanceMethod:sel];
}

4.给分类添加属性
原理:给一个类声明属性,其实本质就是给这个类添加关联,并不是直接把这个值的内存空间添加到类存空间。

// 定义关联的key
static const char *key = "name";

@implementation NSObject (Property)

- (NSString *)name
{
    // 根据关联的key,获取关联的值。
    return objc_getAssociatedObject(self, key);
}

- (void)setName:(NSString *)name
{
    // 第一个参数:给哪个对象添加关联
    // 第二个参数:关联的key,通过这个key获取
    // 第三个参数:关联的value
    // 第四个参数:关联的策略
    objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end
5.获取对象的所有属性

需求:修改.m文件中的私有属性

    Dog *dog = [[Dog alloc] init];
  
    //我们先声明一个unsigned int
    //调用runtime的方法
    //Ivar:方法返回的对象内容对象,这里将返回一个Ivar类型的指针
    //class_copyIvarList方法可以捕获到类的所有变量,将变量的数量存在一个unsigned int的指针中
    unsigned int count;
    Ivar * memList = class_copyIvarList([Dog class], &count);
    //进行遍历
    for (int i=0; i< count ; i++) {
        //通过移动指针进行遍历
        Ivar var = memList[i];
        //获取变量的名称
        NSString *name = [NSString stringWithUTF8String:ivar_getName(var)];
        //获取变量的类型
        NSLog(@"%@",name);
    }
    object_setIvar(dog, memList[0], @"pp");

需求:调用.m文件中实现的方法

    unsigned int count;
    Method *memList = class_copyMethodList([Dog class], &count);
    //遍历
    for(int i=0; i<count; i++){
        SEL name = method_getName(memList[i]);
        NSString * method = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];
        NSLog(@"%@\\n",method);
    }
    SEL name = method_getName(memList[0]);

    NSString *method = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];
    [dog performSelector:name withObject:nil];
6.用我们的函数替换掉类中的函数(黑魔法)

这个太屌,可以将任何一个类的方法替换掉

- (void)viewDidLoad {  
  [super viewDidLoad];    
 Dog *dog = [[Dog alloc]init];    //替换之前的方法  
 class_replaceMethod([Dog class], @selector(bite), (IMP)logHAHA, "q");   
 [dog bite];   
 }
void logHAHA(){    
NSLog(@"HAHA");}

相关文章

网友评论

      本文标题:RunTime

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