iOS的runtime浅析

一、runtime是什么?

runtime是运行时,是纯C语言的API,是OC的底层或者说幕后工作者,所有OC的代码最终都要到运行时去执行。一个对象的类型只有到了运行时才能最终确定,在编译时是不能完全确定的,比如多态,在编译时是父类,在运行时却是子类。runtime平时应用并不多,但是却是深入理解OC这门语言需要去花时间研究的。

二、runtime有什么用?

runtime功能比较多,可以看头文件,下边列出常用的几个功能。

  1. 获取类的属性列表、方法列表、成员变量列表等
  2. 为系统的类增加属性
  3. 方法交换(黑魔法)
  4. 动态的增加方法

三、runtime怎么用?

1. 获取类的属性列表、方法列表、成员变量列表等
// 1.导入头文件
#import <objc/runtime.h>

① 获取类的成员变量列表

#pragma mark - 获取成员变量
- (void)getIvarList{

    unsigned int numIvars;
    // 2.获取一个类所有的成员变量
    /*
     参数:第一个是要获取成员变量的类,第二个是成员变量数目的变量的地址。
    */
    Ivar *vars = class_copyIvarList(NSClassFromString(@"UIView"), &numIvars);

    // 3.遍历并输出
    NSString *key = nil;
    for (int i = 0; i < numIvars; i++) {
        Ivar thisIvar = vars[i];
        key = [NSString stringWithUTF8String:ivar_getName(thisIvar)];
        NSLog(@"变量名:%@",key);

        key = [NSString stringWithUTF8String:ivar_getTypeEncoding(thisIvar)];
        NSLog(@"变量类型:%@",key);
    }
    free(vars);
}

② 获取类的方法列表

#pragma mark - 获取方法列表
- (void)getMethodList{
    unsigned int numMethods;
    // 2.获取一个类的所有方法
    Method *methods = class_copyMethodList(NSClassFromString(@"UIView"), &numMethods);

    for (int i = 0; i < numMethods; i++) {
        Method thisMeth = methods[i];

        SEL sel = method_getName(thisMeth);
        const char *name = sel_getName(sel);
        NSLog(@"方法名:%s",name);
    }
    free(methods);
}

③ 获取类的属性列表

class_copyPropertyList(要获取属性列表的类,属性数目的变量的地址)
2. 为系统的类增加属性

利用分类为系统的类增加属性,例如为UIButton类增加一个name属性
在UIButton分类的.h中声明一个name属性

// 按钮的名称
@property (nonatomic,copy) NSString *name;

在.m中通过运行时添加该属性set和get方法的实现

// 导入runtime头文件
#import <objc/runtime.h>
@implementation UIButton (Extension)

const void *key = "key";
- (void)setName:(NSString *)name{
    /*
     第一个参数: 关联的类
     第二个参数: 记录的一个key
     第三个参数: value
     第四个参数: 内存策略
     */
    objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN);
}

- (NSString *)name{
    return objc_getAssociatedObject(self, key);
}
@end
3. 方法交换(黑魔法)

方法交换分为对象方法交换和类方法的交换,下边以对象方法交换为例。

// 1.导入头文件
#import <objc/runtime.h>
// 2.实现下边两个方法
- (void)running{
    NSLog(@"小明在跑步");
}

- (void)dancing{
    NSLog(@"小花在跳舞");
}
- (void)viewDidLoad {
    [super viewDidLoad];
    // 3.先获取两个方法,然后交换两个方法
    Method running = class_getInstanceMethod([self class], @selector(running));
    Method dancing = class_getInstanceMethod([self class], @selector(dancing));
    method_exchangeImplementations(running, dancing);

    // 4.调用
    [self running];
    [self dancing];
}

如果是类方法的方法交换,上述第三步中改用class_getClassMethod()即可。

4. 动态的增加方法

如果A类需要调用一个方法,但是自己没有实现该方法,B类实现了这个方法,但是两个类没有任何关系,A类无法调用B类的这个方法,这个时候可以通过运行时为A类增加一个该方法的实现。
例如在Test类的.m中定义了一个sayHello的私有方法。

- (void)sayHello{
    NSLog(@"hello");
}

在控制器中通过运行时添加一个新的方法sayHello2,来实现和上述方法同样的功能。

- (void)viewDidLoad {
    [super viewDidLoad];

    Test *test = [[Test alloc] init];
    /**
     *  class cls:原方法所属的类
     *
     *  SEL name:要添加的新方法的名称
     *
     *  IMP imp:IMP就是implementation,即实现的意思,就是原来方法的实现。需要用运行时来获取
     *
     *   const char *types:方法的类型,第一个字母为方法返回值的首字母,接下来的“@:”表示没有参数传入,如果有参数的话后边还需要再加一个@。例如“i@:@”就表示返回值为int类型,又有一个参数传入的方法。
     */
     class_addMethod([Test class], @selector(sayHello2),class_getMethodImplementation([Test class], @selector(sayHello)) , "v@:");
    [test performSelector:@selector(sayHello2)];

}

四、runtime什么时候用?

iOS runtime实战应用:成员变量和属性
讲述了json转model、快速归解档、访问私有变量三个应用场景。
iOS runtime实战应用:关联对象
讲述了添加公共属性,私有成员变量及关联KVO观察者三个应用场景。
iOS runtime实战应用:Method Swizzling

讲述了全局替换图片名称以及数据统计两个案例。

五、其他介绍runtime的博客链接

iOS运行时Runtime浅析
这篇博客对runtime的几乎所有方法都做了介绍,非常详细,建议大家看看。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值