一 、Runtime 简介
- Runtime简称运行时。OC就是运行时机制 即在运行时候的一些机制,其中最主要的是消息机制。
- 对于C语言,函数的调用在编译的时候会决定调用哪个函数。
- 对于OC的函数,属于动态调用过程,在编译的时候并不能决-- 定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
- 在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。
- 在编译阶段,C语言调用未实现的函数就会报错。
二 、Runtime作用
- xcode5之后需要设置 在xcode 里面build Settings 搜索msg(Enable StrictChecking of objc_msgSend Calls)设为NO
- 方法调用的本质(对象发送消息)。 使用前必须导入<objc/message.h>
//创建person对象 Person *p = [[Person alloc]init];
// 让p发送消息
本质:让对象发送消息
objc_msgSend(p, @selector(eat));
objc_msgSend(p, @selector(run:),10);
用类名调用类方法,底层会自动把类名转换成类对象调用
本质:让类对象发送消息
objc_msgSend([Person calss], @selector(run));
- 消息机制原理:对象根据方法编号SEL去映射查找对应的方法实现。
2.交换方法 (开发使用场景:系统自带的方法功能不够,给系统自带的方法扩展一些功能,并且保持原有的功能。)
- 继承系统的类
- 使用Runtime 交换方法
需要分类
@implementation UIImage (Image)
// 加载分类到内存的时候调用
+(void)load
{
// 交换方法
// 获取imageWithName方法地址
Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));
// 获取imageWithName方法地址
Method imageName = class_getClassMethod(self, @selector(imageNamed:));
// 交换方法地址,相当于交换实现方式
method_exchangeImplementations(imageWithName, imageName);
}
// 不能在分类中重写系统方法imageNamed,因为会把系统的功能给覆盖掉,而且分类中不能调用super.
// 既能加载图片又能打印
+(instancetype)imageWithName:(NSString *)name
{
// 这里调用imageWithName,相当于调用imageName
UIImage *image = [self imageWithName:name];
if (image == nil) {
NSLog(@"加载空的图片");
}
return image;
}
@end
3.动态添加方法
- 开发使用场景:如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。
- 经典面试题:有没有使用performSelector,其实主要想问你有没有动态添加过方法。
#import@interface ViewController ()
@end
@implementation ViewController
-(void)viewDidLoad {
[super viewDidLoad];
Person *p = [[Person alloc] init];
objc_msgSend(p, @selector(eat:) ,@10);
// [p performSelector:@selector(eat:) withObject:@111];
#import "Person.h"#import@implementation Person
/*IMP imp
typedef void (*IMP)(void /* id, SEL, ... */ );
/*默认一个方法都有两个参数,self,_cmd,隐式参数
self:方法调用者
_cmd:调用方法的编号
id :param1 参数
*/
void eatFnv(id self ,SEL _cmd, id param1)
{
NSLog(@"调用eat %@ %@ %@",self,NSStringFromSelector(_cmd),param1);
}
// 动态添加方法,首先实现这个resolveInstanceMethod
//resolveInstanceMethod调用:当调用了方法没有实现时 就会调用
// resolveInstanceMethod作用:就知道哪些方法没有实现,从而动态添加方法
//sel:没有实现的方法
+(BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"%@",NSStringFromSelector(sel));
// if ([NSStringFromSelector(sel) isEqualToString:@"eat:"]) {
if (sel == @selector(eat:)) {
/*
cls:给哪个类添加方法
SEL:添加方法的方法编号是什么
IMP:方法实现,函数入口,函数名
type:方法类型 Type encodings
*/
class_addMethod(self, sel, (IMP)eatFnv, "v@:@");
return YES;
}
return [super resolveInstanceMethod:sel];
}
4.Runtime给分类添加属性
#import "NSObject+Objc.h"#import@implementation NSObject (Objc)
//定义常量 必须是C语言字符串
static char *PersonNameKey = "name";
- (void)setName:(NSString *)name{
/*
OBJC_ASSOCIATION_ASSIGN; //assign策略
OBJC_ASSOCIATION_COPY_NONATOMIC; //copy策略
OBJC_ASSOCIATION_RETAIN_NONATOMIC; // retain策略
OBJC_ASSOCIATION_RETAIN;
OBJC_ASSOCIATION_COPY;
*/
/*
* id object 给哪个对象的属性赋值
const void *key 属性对应的key
id value 设置属性值为value
objc_AssociationPolicy policy 使用的策略,是一个枚举值,和copy,retain,assign是一样的,手机开发一般都选择NONATOMIC
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
*/
objc_setAssociatedObject(self, PersonNameKey, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)name{
return objc_getAssociatedObject(self, @"name");
}
5.自动生成属性代码 分类拼写 Property(,, )NSStirng *name;
// 解决KVC报错
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
if ([key isEqualToString:@"id"]) {
_ID = [value integerValue];
}
// key:没有找到key
// value:没有找到key对应的值
NSLog(@"%@ %@",key,value);
}
6.字典转模型
方法1.KVC (遍历字典) 方法2.runtime(遍历属性名)
// runtime:遍历模型中所有成员属性,去字典中查找
// 属性定义在哪,定义在类,类里面有个属性列表(数组)
// 遍历模型所有成员属性
// ivar:成员属性
// class_copyIvarList:把成员属性列表复制一份给你
// Ivar *:指向Ivar指针
// Ivar *:指向一个成员变量数组
// class:获取哪个类的成员属性列表
// count:成员属性总数
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList(self, &count);
for (int i = 0 ; i < count; i++) {
// 获取成员属性
Ivar ivar = ivarList[i];
// 获取成员名
NSString *propertyName = [NSString stringWithUTF8String:ivar_getName(ivar)];
;
// 成员属性类型
NSString *propertyType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
}