什么是runtime? 平时项目中有使用过么?
1. oc是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
2. oc的动态性就是runtime来支撑和实现的,runtime是一套c语言的api,封装了很多动态性相关的api
3.平时编写的oc代码,底层都是转换成了runtime api进行调用
具体应用
1.利用关联对象associateObject给分类添加属性
2.遍历类的所有成员变量(获取系统控件的ivar,字典转模型,自动归档解档)
3.交换方法实现(交换系统的方法实现)
一 、 获取ISA指向的class对象;
object_getClass(per);
二 、 设置ISA指向的class;
object_setClass(per, [lzhCarclass]);
[persetValue:@5forKey:@"_b"];
NSNumber*f = [pervalueForKey:@"_f"];
[perrun];
三、判断一个类是否是类对象;
object_isClass(per); //0
object_isClass([perclass]);// 1
四、动态创建一个类;
1. ClassnewClass = objc_allocateClassPair([NSObjectclass],"DOG",0);
实际是上面函数是创建一对类(元类和类);
参数 : 父类 类名; 返回值: 类对象
2. class_addIvar(newClass,"_age",4,1,@encode(int));
类名 该变量占用多少字节 对齐是1个长度 类型的type;
class_addIvar(newClass,"_wight",4,1,@encode(int));
3. class_addMethod(newClass,@selector(run), (IMP)run,"V@:");
4. 注册一个类 ;相等于创建这个类的结构体;所以添加类的成员变量一定放到注册函数之前;因为注册完成以后类的结构体已经生成出来,而且成员变量是在ro_t里面只读的属性,而方法可以放到注册类后面写,因为方法是放到了rw_t里面,可以读可以写的,所以方法可以放到注册函数后面;已经注册的类不可以添加成员变量;
objc_registerClassPair(newClass);
5.创建完成以后的实际使用如下:
id dog = [[newClassalloc]init];
[dogsetValue:@10forKey:@"_age"];
[dogsetValue:@20forKey:@"_wight"];
[dogrun];
NSLog(@"%@,%@",[dogvalueForKey:@"_age"],[dogvalueForKey:@"_wight"]);
NSLog(@"%zd",class_getInstanceSize(newClass));
五、获取成员变量信息
IvarageIvar = class_getInstanceVariable([lzhPersonclass],"_age");
NSLog(@"----%s %s",ivar_getName(ageIvar),ivar_getTypeEncoding(ageIvar));
// 设置成员变量的值;int 类型设置number类型是不行
lzhPerson*person = [lzhPersonnew];
object_setIvar(person, ageIvar,@"1234");
NSLog(@"-----%@",person.age);
六: copy成员变量列表;获取成员变量列表list:使用场景比较多;
unsignedintcount;//获取成员变量数量
Ivar* ivars =class_copyIvarList([lzhPersonclass], &count);
for(inti =0; i
Ivarivar = ivars[i];
NSLog(@"====== %s %s",ivar_getName(ivar),ivar_getTypeEncoding(ivar));
}
free(ivars);//使用完成以后需要释放ivars的数组对象了;
实际使用场景如字段转模型:
+(instancetype)lzh_objcetWithJson:(NSDictionary *)json{
id obj = [[selfalloc]init];
unsignedintcount;
Ivar*ivars =class_copyIvarList([objclass], &count);
for(inti =0; i
// 取出来i位置的成员变量
Ivarivar = ivars[i];
NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivar)];
[namedeleteCharactersInRange:NSMakeRange(0, 1)];
// 设置
[objsetValue:json[name]forKey:name];
}
free(ivars);
returnobj;
}
七、替换方法实现
lzhPerson*person = [lzhPersonnew];
// 参数1;如果替换实例方法传入的是类方法,如果替换类方法传入的是元类方法;
class_replaceMethod([lzhPerson class], @selector(run), (IMP)myRun, "v");
[personrun];
class_replaceMethod([lzhPerson class], @selector(run), imp_implementationWithBlock(^{
NSLog(@"1234");
}),"v");
八:方法交换
交换2个方法的实现;交换的是rw_t里面的array_t的methodlist的monthed_t里面的imp;
lzhPerson*person = [lzhPersonnew];
MethodrunMenthod =class_getInstanceMethod([lzhPersonclass],@selector(run));
MethodtestMenthod =class_getInstanceMethod([lzhCarclass],@selector(test));
method_exchangeImplementations(runMenthod, testMenthod);
[personrun];
例如:拦截系统所有的按钮的点击事件:
+(void)load
{
Methodmethod1 =class_getInstanceMethod(self,@selector(sendAction:to:forEvent:));
Methodmethod2 =class_getInstanceMethod(self,@selector(lzh_sendAction:to:forEvent:));
method_exchangeImplementations(method1, method2);
// 交换的是rw_t里面的array_t的methodlist的monthed_t里面的imp;
}
- (void)lzh_sendAction:(SEL)actionto:(nullableid)targetforEvent:(nullableUIEvent*)event{
NSLog(@"%@ --- %@-------%@",self,target,NSStringFromSelector(action));
if ([self isKindOfClass:[UIButton class]]) {
// [target performSelector:action];
// [self sendAction:action to:target forEvent:event];
[selflzh_sendAction:actionto:targetforEvent:event];
}
}
例如:拦截数组添加防止添加nil闪退问题:
+(void)load
{
// 防止load函数会调用2次;
staticdispatch_once_tonceToken;
dispatch_once(&onceToken, ^{
// 类簇 :NSString;NSArray; NSMutableArray;真正的类型并不是看到的类型;
Classclass =NSClassFromString(@"__NSArrayM");
Methodmethod1 =class_getInstanceMethod(class,@selector(insertObject:atIndex:));
Methodmethod2 =class_getInstanceMethod(class,@selector(lzh_insertObject:atIndex:));
method_exchangeImplementations(method1, method2);
// 交换的是rw_t里面的array_t的methodlist的monthed_t里面的imp;
});
}
- (void)lzh_insertObject:(id)anObjectatIndex:(NSUInteger)index{
if(anObject ==nil) {
return;
}
[selflzh_insertObject:anObjectatIndex:index];
dispatch_async(dispatch_get_main_queue(), ^{
if([anObjectisKindOfClass:NSString.class] && [anObjectisEqualToString:@"jack11111111"]) {
NSLog(@"Extension");
}
});
}
例如:拦截字典添加防止添加nil为key的闪退问题:
NSMutableDictionary * dic = [[NSMutableDictionary alloc]init];
dic[@"name"] = @"jack";
dic[obj] = obj;
NSLog(@"%@",dic);
// 底层调用的是下面的方法;
// 设置值
// dic setObject:<#(nullable id)#> forKeyedSubscript:<#(nonnull id)#>
// 取出来的值
// dic objectForKeyedSubscript:<#(nonnull id)#>