runtime 理解及实际应用

42 篇文章 0 订阅
28 篇文章 0 订阅

首先 , 什么是runtime?

1>OC 是一个全动态语言,OC 的一切都是基于 Runtime 实现的
平时编写的OC代码, 在程序运行过程中, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者, 例如:

OC :
[[Person alloc] init]
runtime :
objc_msgSend(objc_msgSend("Person" , "alloc"), "init")

2>runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API
3>runtimeAPI的实现是用 C++ 开发的(源码中的实现文件都是mm),是一套苹果开源的框架

简单的概括一下就是 :OC语言尽可能地把数据类型的确定从编译时和链接时移到了运行时, OC具备运行时特性就意味着 Objective-C 语言不仅需要一个编译器,同时也需要一个运行时系统来执行编译好的代码。

下面简单说一下runtime的几种使用方法:

  • 方法替换 (如:避免数组加入nil, 导致crash)

/*
     如果没有引入"NSMutableArray+Extension.h" ,运行crash报错 “object cannot be nil”
     反之 引入"NSMutableArray+Extension.h", 运行正常, 原因就在于 在NSMutableArray+Extension 里使用了runtime 修改了addObject方法,判断如果是你nil 将不会在向数组里添加空对象 ,因此不会再报错
     */

    [self.mArray addObject:@"第一个"];
    [self.mArray addObject:@"第一个"];
    [self.mArray addObject:nil];
    NSLog(@"%@", self.mArray);

runtime实现代码

#import "NSMutableArray+Extension.h"
#import <objc/runtime.h>

@implementation NSMutableArray (Extension)
+(void)load
{
    Method oldMethod = class_getInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(addObject:));
    Method newMethod = class_getInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(newAddObject:));
    method_exchangeImplementations(oldMethod, newMethod);

}

- (void)newAddObject:(id)object{
    if (object != nil) {
        [self newAddObject:object];
    }
}



@end

这里做的就是替换addObject方法, 进而达到免疫nil的目地

  • 给一个类添加属性
    在这里我给NSObject添加了一个name属性, 代码如下:
#import "NSObject+Property.h"
#import <objc/runtime.h>

static const NSString *key = @"name";

@implementation NSObject (Property)

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

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


@end

验证代码:

//用于给一个类添加属性
- (void)runTimeTestForAddProperty
{
    NSObject *obj = [[NSObject alloc] init];
    obj.name = @"我是新加的";
    NSLog(@"%@", obj.name);
}

运行之后打印出:” 我是新加的“, 证明属性添加成功

  • 利用runtime 进行json转model
- (id)initWithDic:(NSDictionary *)dic {
     if (self = [super init]) {
     unsigned int outCount = 0;
     Class currentClass = [self class];
     while (currentClass) {
     //获取当前类所有的属性
     objc_property_t *properties = class_copyPropertyList(currentClass, &outCount);
     for (int i = 0; i < outCount; i++) {
     //获取该类的一个属性指针
     objc_property_t property = properties[i];;
     //property_getName(property) 获取属性的名称
     //initWithCString, c的字符串转OC的字符串
     NSString *propertyNameString =[[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
     //因为id是一个OC的关键字, 你不能声明一个属性名称为id, 所以遇到属性名称是id就携程id_i
     //如果属性名称是id_i, 则把id赋值给当前属性
     if ([propertyNameString isEqualToString:@"id_i"]) {
     propertyNameString = @"id";
     }
     id value;
     //判断传过来的参数是不是字典类型
     if ([dic isKindOfClass:[NSDictionary class]]) {
     //根据key获取json数据对应的value
     //如果对应的可以不存在, 使用字典去获取一个value值, 则返回值为nil
     value = [dic objectForKey:propertyNameString];
     }
     //如果该值不存在
     if (value == [NSNull null]|| value == nil) {
     //则返回继续寻找
     continue;
     }
     //如果值存在
     if (value) {
     //但度处理名称为id的
     if ([propertyNameString isEqualToString:@"id"]) {
     [self setValue:[dic objectForKey:@"id"] forKey:@"id_i"];
     }else{
     //根据属性, 使用kvc赋值
     [self setValue:value forKey:propertyNameString];
     }
     }
     }
     //最后别忘记释放
     free(properties);
     Class superclass = [currentClass superclass];
     currentClass = superclass;
     }
     }
     return self;
     }

这样做的好处就是 防止因为后台返回的字段没有或者key值错误,进而导致model转换出现问题

用于多参数传递

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"我来测试一下" message:@"" preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *action = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

    }];
    UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

        NSString *number1 = objc_getAssociatedObject(alert, "number1");
        NSString *number2 = objc_getAssociatedObject(alert, "number2");

        NSLog(@"%@ %@", number1, number2);
    }];

    // 传递多参数
    objc_setAssociatedObject(alert, "number1", @"1", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    objc_setAssociatedObject(alert, "number2", @"2", OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    [alert addAction:action];
    [alert addAction:action1];
    [self presentViewController:alert animated:YES completion:nil];

点击确定按钮 打印1 2 , 说明传了两个参数 1 2;

目前就简单总结一点, 纯属个人理解, 欢迎指正沟通!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值