在使用runtime的时候,必须配置文件,buildSettings中搜索runtime,找到Enable Hardened Runtime选项,之后变成YES
- runtime:必须要导入头文件 <objc/message.h>
- 任何方法调用本质:发送一个消息,用runtime发送消息,OC底层实现通过runtime实现
- 验证;方法调用,是否真的是转换为消息机制
- runtime都有一个前缀,谁的事情使用谁
id objc = [NSObject alloc];
- 类方法本质:类对象调用[NSObject class];
- id;谁发送消息
- SEL:发送什么消息
((NSObject ()(id, SEL))(void *)objec_msgSend)([NSObject class], @selector(alloc)); - Xcode6之前,苹果运行使用objc_msgSend,而且有参数提示
- Xcode6苹果不推荐我们使用runtime
解决方法提示
- 找到build setting -> 搜索msg
- 最终生成消息机制,编译器做的事情
- 最终代码,需要把当前代码重新编译,用xcode编译器,clang
- clang -rewrite-objc main.m 查看最终生成代码
runtime使用场景
- 装逼
- 不得不用,可以帮我们调用私有方法
Person *p = [Person alloc];
Person *p = objc_msgSend(obje_getClass("Person"), sel_registerName("alloc"));
p = [p init];
p = objc_msgSend(p,sel_registerName("init"));
runtime调用有参数的方法
objc_msgSend(p, @selector(run:), <参数>);
//可以传多个参数,看p对象中方法需要传的参数
//@selector == sel_registerName
方法调用流程
面试回答
怎么区去调用eat方法,对象方法:类对象的方法列表 类方法:元类中方法类表
- 1.通过isa区对应的类中查找
- 2.注册方法编号
- 3.根据方法编号去查找对应方法
- 4.找到只是最终函数实现地址,根据地址去方法区调用对应函数
补充
5大区
- 1.栈
- 2.堆
- 3.静态区
- 4.常量区
- 5.方法区
栈:不需要手动管理内存, 自动管理
堆:需要手动管理内存, 自己去释放
交换方法/自定义UIImage
- 给系统的imageNamed添加功能,只能使用runtime(交换方法)
1.给UIImage添加一个分类
2.导入头文件
#import <objc/message.h>
3.先写一个方法,这个方法实现了实现了我们需要的功能(比如我们想让imageNamed方法中存在判断是否加载成功的功能,我们调用imageNamed方法的时候就可以实现)
+ (UIImage *)lht_iamgeNamed:(NSString *)name{
UIImage *image = [UIImage lht_imageNamed:name];
if(image){
NSLog(@"加载成功");
}else{
NSLog(@"加载失败");
}
}
4.在load中实现交换
+ (void)load{
//获取imageNamed
//获取哪个类方法
//SEL:获取哪个方法
Method imageNamedMethod = class_getClassMethod(self, @seletor(imageNamed:));
//获取lht_imageNamed
Method lht_imageNamedMethod = class_getClassMethod(self, @seletor(lht_imageNamed:));
//交换方法:runtime
method_exchangeImplementations(imageNamedMethod, lht_imageNamedMethod)
}
动态添加方法(系统的类添加方法)
- Runtime(动态添加方法):OC都是懒加载机制,只要一个方法实现了,就会马上添加到方法列表中
- app:免费版,收费版
- QQ,微博,直播等等应用,都有会员机制
美团面试题:有没有使用过performSelector?什么时候使用?动态添加方法的时候使用过。怎么动态添加方法?用runtime。为什么要用动态添加方法?
没有参数
调用
Person *person = [[Person alloc] init];
[person performSelector:@selector(eat)];
//void, (id, SEL)
//@相当于id, :相当于SEL, v相当于void
void aaa(id self, SEL _cmd){
NSLog(@"吃");
}
//任何方法默认都有两个隐式参数,self, _cmd
//什么时候调用:只要一个对象调用了一个未实现的方法就会调用这个方法,进行处理
//作用:动态添加方法,处理未实现
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSString *selString = NSStringFromSelector(sel);
if ([selString isEqualToString:@"eat"]) {
//class:给哪个类添加方法
//SEL: 添加哪个方法
//IMP: 方法实现 => 函数 => 函数入口 => 函数名
//type: 方法类型
class_addMethod(self, sel, (IMP)aaa, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
带有参数
调用
Person *person = [[Person alloc] init];
[person performSelector:@selector(run:) withObject:@10];
void aaa(id self, SEL _cmd, NSNumber *meter){
NSLog(@"跑了%@", meter );
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSString *selString = NSStringFromSelector(sel);
if ([selString isEqualToString:@"run:"]) {
//class:给哪个类添加方法
//SEL: 添加哪个方法
//IMP: 方法实现 => 函数 => 函数入口 => 函数名
//type: 方法类型
class_addMethod(self, sel, (IMP)aaa, "v@:@");
return YES;
}
return [super resolveInstanceMethod:sel];
}
动态添加属性(系统的类添加属性)
- 什么时候需要动态添加属性:给系统的类添加属性的时候,可以使用runtime动态添加属性方法
- 本质:动态添加属性,就是让某个属性与对象产生关联
- 需求:让NSObject类保存一个字符串
- runtime一般都是准对系统的类
1.先给NSObject添加一个分类
2.在分类.h中定义属性
//property分类:只会生成get,set方法声明,不会生成实现,也不会生成下划线成员属性
@property NSString *name;
3.在分类.m中实现(实现get,set方法)
- (void)setName:(NSString *)name{
/*
让这个字符串与当前对象产生联系
object:给哪个对象添加属性
key:属性名称
value:属性值
policy:保存策略
*/
objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)name{
return objc_getAssociatedObject(self, @"name");
}
4.外界调用
NSObject *objc = [[NSObject alloc] init];
objc.name = @"123";
自动生成属性代码(给系统的字典类新增加一个方法)
字典转模型的时候,我们需要在模型中按照字典的key一个一个的添加属性,很麻烦,我们就给字典添加一个分类,调用方法,直接生成属性,我们直接拷贝过去就好
1.给字典添加一个分类
2.在分类的.文件中声明方法
- (void) createPropertyCode;
3.在分类的.m文件中实现方法
- (void) createPropertyCode{
NSMutableString *codes = [NSMutableString string];
//遍历字典
[self enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop){
NSString *code;
if([value isKindOfClass:[NSString class]]){
code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSString *%@", key];
}else if([value isKindOfClass:NSClassFromString(@"__NSCFBoolean")]){
code = [NSString stringWithFormat:@"@property (nonatomic, assign) BOOL %@", key];
}else if([value isKindOfClass:[NSNumber class]]){
code = [NSString stringWithFormat:@"@property (nonatomic, assign) NSInteger %@", key];
}else if([value isKindOfClass:[NSArray class]]){
code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSArray *%@", key];
}else if([value isKindOfClass:[NSDictionary class]]){
code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSDictionary *%@", key];
}
[codes appendFormat:@"\n%@\n", code];
}];
NSLog(@"%@", codes);
}
4.外界调用
//获取文件全路径
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"<文件全名称>" ofType:nil];
//文件全路径
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
//设计模型,创建属性代码 => dict
[dict createProperty];
- 这个时候因为.m文件中我们打印出来了他根据字典的key转换出来的说有属性,我们直接复制,拷贝到模型类中,这样不仅快速,而且准确
字典转模型
1.给NSObject添加一个分类
2.在分类.h中方法声明
+ (instancetype)modelWithDict:(NSDictionary *)dict;
3.在分类.m中方法实现
- Ivar:成员变量(带下划线的)
- Property:属性
+ (instancetype)modelWithDict:(NSDictionary *)dict{
id objc = [[self alloc] init];
//runtime:根据模型中属性,去字典中取出对应的value给模型属性赋值
/*
第一个参数:获取哪个类的成员变量
第二个参数:成员变量的个数
*/
unsigned int count = 0;
//获取成员变量数组
Ivar ivarList = class_copyIvarList(self, &count);
//遍历所有成员变量
for (int i = 0; i < count; i++) {
//获取成员变量
Ivar ivar = ivarList[i];
//获取成员变量类型
NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
//处理字符串(因为我们得到的字符串,他会带有一些没有用的东西,导致获取类失败,我们将这些都替换成空)
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
//获取成员变量名字
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
//获取key
NSString *key = [ivarName substringFromIndex:1];
//去字典中查找对应value
id value = dict[key];
//二级转换
if ([value isKindOfClass:[NSDictionary class]] && [ivarType hasPrefix:@"NS"]){
//转换成哪个模型
//获取类
Class modelClass = NSClassFromString(ivarType);
value = [modelClass modelWithDict:value];
}
//给模型中属性赋值
if (value) {
[objc setValue:value forkey:key];
}
}
return objc;
}