1 Runtime简介
-
RunTime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制。
-
对于C语言,函数的调用在编译的时候会决定调用哪个函数。
-
对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
-
事实证明:
1)在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。
2)在编译阶段,C语言调用未实现的函数就会报错。
2 Runtime消息机制
- 类方法本质:类对象调用[NSObject class]
id:谁发送消息
SEL:发送什么消息
((NSObject *(*)(id, SEL))(void *)objc_msgSend)([NSObject class], @selector(alloc));
xcode6之前,苹果运行使用objc_msgSend.而且有参数提示
xcode6苹果不推荐我们使用runtime
- 解决消息机制方法提示步骤
查找build setting -> 搜索msg
最终生成消息机制,编译器做的事情
最终代码,需要把当前代码重新编译,用xcode编译器,clang
clang -rewrite-objc main.m
查看最终生成代码
3 Runtime消息机制调用多个参数
- (void)runtime {
/*
runtime:方法都是有前缀,谁的事情谁开头
开发中使用场景:
需要用到runtime,消息机制
不得不用runtime消息机制,可以帮我调用私有方法.
*/
Person *p = objc_msgSend(objc_getClass("Person"), @selector(alloc));
p = objc_msgSend(p, @selector(init));
objc_msgSend(p, @selector(eat));
}
4 方法调用流程
- 怎么去调用eat方法 ,对象方法:类对象的方法列表 类方法:元类中方法列表
- 1.通过对象的isa去对应的类对象中查找
- 2.注册方法编号
- 3.根据方法编号去查找对应方法
- 4.找到只是最终函数实现地址,根据地址去方法区调用对应函数
5 内存5大区
-
1.栈 2.堆 3.静态区 4.常量区 5.方法区
-
1) 栈:不需要手动管理内存,自动管理
-
2) 堆,需要手动管理内存,自己去释放
Snip20191220_1.png
6 Runtime交换方法
#import "UIImage+Exention.h"
#import <objc/message.h>
@implementation UIImage (Exention)
// 把类加载进内存的时候调用,只会调用一次
+ (void)load {
// 获取类方法
// 获取哪个类的方法
// 获取哪个方法
Method originalMethod = class_getClassMethod(self, @selector(imageNamed:));
Method currentMethod = class_getClassMethod(self, @selector(ZYX_imageNamed:));
// 交换方法:runtime
method_exchangeImplementations(originalMethod, currentMethod);
}
+ (UIImage *)ZYX_imageNamed:(NSString *)imageName {
// 这里其实就是调用系统的imageNamed: 方法
UIImage *image = [self ZYX_imageNamed:imageName];
NSLog(@"%s",__func__);
return image;
}
@end
7 Runtime动态添加方法
Runtime(动态添加方法):OC都是懒加载机制,只要一个方法实现了,就会马上添加到方法列表中.
- app:免费版,收费版
- QQ,微博,直播等等应用,都有会员机制
#import "Person.h"
#import <objc/message.h>
@implementation Person
void run(id self, SEL _cmd, NSNumber *meter) {
NSLog(@"run %@ meter", meter);
}
// 如果调用的该对象的方法没有实现,则会走这个方法
// 任何方法都包含两个隐式参数:self _cmd
// 动态添加方法,处理未实现
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"%@",NSStringFromSelector(sel));
if (sel == NSSelectorFromString(@"run:")) {
// class: 给哪个类添加方法
// SEL: 添加哪个方法
// IMP: 方法实现 => 函数 => 函数入口 => 函数名
// type: 方法类型
class_addMethod(self, sel, (IMP)run, "v@:@");
return YES;
}
return [super resolveClassMethod:sel];
}
@end
8 Runtime动态添加属性
#import "NSObject+Extension.h"
#import <objc/message.h>
// 动态添加属性:什么时候需要动态添加属性
// 开发场景
// 给系统的类添加属性的时候,可以使用runtime动态添加属性方法
// 本质:动态添加属性,就是让某个属性与对象产生关联
// 需求:让一个NSObject类 保存一个字符串
// runtime一般都是针对系统的类
@implementation NSObject (Extension)
- (void)setName:(NSString *)name {
/*
参数1:给哪个对象添加属性
参数2:属性名称
参数3:属性的值
参数:保存策略
*/
objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)name {
return objc_getAssociatedObject(self, "name");
}
@end
9 自动生成属性代码
#import "NSDictionary+Extension.h"
@implementation NSDictionary (Extension)
// isKindOfClass:判断是否是当前类或者子类
// 生成属性代码 => 根据字典中所有key
- (void)createProperty {
__block NSMutableString *codes = [NSMutableString string];
[self enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) {
// NSLog(@"%@",NSStringFromClass([value class]));
NSString *code;
if ([value isKindOfClass:[NSString class]]) {
code = [NSString stringWithFormat:@"@property (strong, nonatomic) NSString *%@;",key];
} else if ([value isKindOfClass:NSClassFromString(@"__NSCFBoolean")]) {
code = [NSString stringWithFormat:@"@property (assign, nonatomic) BOOL %@;",key];
}
else if ([value isKindOfClass:[NSNumber class]]) {
code = [NSString stringWithFormat:@"@property (assign, nonatomic) NSInteger %@;",key];
} else if ([value isKindOfClass:[NSArray class]]) {
code = [NSString stringWithFormat:@"@property (strong, nonatomic) NSArray *%@;",key];
} else if ([value isKindOfClass:[NSDictionary class]]) {
code = [NSString stringWithFormat:@"@property (strong, nonatomic) NSDictionary *%@;",key];
}
[codes appendFormat:@"\n%@\n",code];
}];
NSLog(@"%@",codes);
}
@end
10 Runtime字典转模型
KVC方式:[item setValue:@"来自即刻笔记" forKey:@"source"]:
- 首先去模型中查找有没有setSource,找到,直接调用赋值 [self setSource:@"来自即刻笔记"]
- 去模型中查找有没有source属性,有,直接访问属性赋值 source = value
- 去模型中查找有没有_source属性,有,直接访问属性赋值 _source = value
- 找不到,就会直接报错 setValue:forUndefinedKey:报找不到的错误
// 获取类里面所有方法
// class_copyMethodList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)// 本质:创建谁的对象
// 获取类里面属性
// class_copyPropertyList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)
// Ivar:成员变量 以下划线开头
// Property:属性
+ (instancetype)modelWithDict:(NSDictionary *)dict {
id objc = [[self alloc] init];
// runtime:根据模型中属性,去字典中取出对应的value给模型属性赋值
// 1.获取模型中所有成员变量 key
// 获取哪个类的成员变量
// count:成员变量个数
unsigned int count;
// 获取成员变量数组
Ivar *ivarList = class_copyIvarList(self, &count);
for (int i = 0; i < count; i++) {
// 获取成员变量
Ivar ivar = ivarList[i];
// 获取成员变量名字
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 获取成员变量类型
NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
// @\"User\" -> User
[ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
[ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
// 获取key
NSString *key = [ivarName substringToIndex:1];
id value = dict[key];
// 二级转换:判断下value是否是字典,如果是,字典转换层对应的模型
// 并且是自定义对象才需要转换
if ([ivarType isKindOfClass:[NSDictionary class]] && ![ivarType hasPrefix:@"NS"]) {
Class class = NSClassFromString(ivarType);
[class modelWithDict:value];
}
if (value) {
[objc setValue:value forKey:key];
}
}
return objc;
}
网友评论