首先 , 什么是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;
目前就简单总结一点, 纯属个人理解, 欢迎指正沟通!