1.Runtime定义:
运行时,系统在运行时的一些机制,其中最主要的就是消息机制。对于C语言,函数的调用在编译时决定调用哪个函数。
OC函数调用称为消息发送,属动态调用过程。在编译的是否并不能决定真正调用哪个函数(编译看指针,运行看实质,而C语言在编译阶段就会报错),只有真正运行的时候才会根据函数名称找到对应的函数来调用。
2.基本功能点
①交换方法②动态添加方法③动态添加属性④字典转模型
更好理解类、对象、消息机制,以及运行期间代码动态交互
3.常用的方法
Person *p = [[Person alloc] init]; //alloc:分配内存空间 init 初始化对象(苹果不建议我们使用objc_msgSend,在build-setting 搜索 msg 打开
导入 #import <objc/message.h>
①消息机制 (objc_msgSend 消息发送)
#objc_getClass 获取类名
#sel_registerName 注册方法编号
Person * p = objc_msgSend(objc_getClass(“Person”),sel_registerName(“alloc”))
#1.p = [p init];
p = objc_msgSend(p,sel_registerName(“init”));
#2.传参数 [p eatWith:@"汉堡!!"];
objc_msgSend(p,sel_registerName(“eatWith:”),@“汉堡!!”);
#3.给父类发送消息
#objc_super 结构体指针
#objc_getSuperclass 获取父类指针
HKPerson * p = [[HKPerson alloc] init]; //定义hkSuper结构体 struct objc_super hkSuper = {p,class_getSuperclass(objc_getClass(“HKPerson”))}; //给父类发送消息 objc_msgSend(&hkSuper, @selector(eatWith:),@“汉堡!!”);
②方法欺骗(MethodSwizzling 方法欺骗)
(C语言的API。可以在OC编译运行阶段,动态的进行操作)
1.SEL 方法的编号
2.IMP 方法实现
OC调用方法:通过消息机制,给某个对象,发送方法编号信息,通过SEL可以找到对应的方法实现。SEL和IMP是一一对应的。
面向切面编程-HOOK思想
HOOK:钩子-修改原来的方法调用顺序,改变对应关系。
例子:NSURL不会检测是否为nil, 给系统的NSURL添加此功能
HOOK: 在整个项目中,一旦你调用URLWithString ,就来到 HK_URLWithString (Runtime)
#class_getClassMethod 获取类方法
#class_getInstanceMethod 获取对象/实例方法
实现步骤:
//1->创建一个NSURL的类别 +(instancetype)HK_URLWithString:(NSString *)URLString; //2->.m实现文件中 #class_getClassMethod 获取类方法 #class_getInstanceMethod 获取对象/实例方法 #import "NSURL+hook.h" #import <objc/runtime.h> @implementation NSURL (hook) +(void)load { NSLog(@"Load.."); //先执行 Load ,后执行Main函数 //下钩子!! method_exchangeImplementations (A,B) 交换SEL 和IMP 的对应关系和指向 /* SEL(目录) -- IMP(才是指针!) */ //获取Method //class_getClassMethod 获取类方法 //class_getInstanceMethod 获取对象/实例方法 Method URLWithString = class_getClassMethod(self, @selector(URLWithString:)); Method HK_URLWithString = class_getClassMethod(self, @selector(HK_URLWithString:)); //method_exchangeImplementations //交换,下钩子 method_exchangeImplementations(URLWithString, HK_URLWithString); } + (instancetype)HK_URLWithString:(NSString *)URLString { NSURL * url = [NSURL HK_URLWithString:URLString];//递归 发送 URLWithString 消息 又调用 HK_URLWithString if (url==nil) { NSLog(@"url 为 nil !!!"); } return url; }
③动态添加方法(class_addMethod 动态添加方法)
OC的方法调用,会传递两个隐式参数 (objc_msgSend(self, _cmd))id self 方法调用者; SEL _cmd方法编号
我们可以使用resolveInstanceMethod和resolveClassMethod动态添加实例方法和类方法
//如果该类接收到一个没有实现的实例方法,就会来到这里 +(BOOL)resolveInstanceMethod:(SEL)sel { //NSLog(@"%@",NSStringFromSelector(sel)); //动态添加一个方法!! /* 1.class 哪个类 2.SEL 3.IMP 函数的指针 4.返回值类型 */ class_addMethod(self, sel, (IMP)haha, "v@:@"); return [super resolveInstanceMethod:sel]; } void haha(id obj, SEL sel , NSString*objc) { //NSLog(@"吃到了%@",objc); //obj 调用者 //sel 方法编号 //objc 参数 NSLog(@"%@--%@--%@",obj,NSStringFromSelector(sel),objc);//(null)--eat:--汉堡!! //objc_msgSend(p,@selector(eat:),@"汉堡"); } void myMethodIMP(id self, SEL _cmd)//默认参数 { // implementation .... }
Runtime数据结构
/* id : typedef struct objc_object *id; SEL : typedef struct objc_selector *SEL; Class : Class 也有一个 isa 指针,指向其所属的元类(meta)。 ·super_class:指向其超类。 ·name:是类名。 ·version:是类的版本信息。 ·info:是类的详情。 ·instance_size:是该类的实例对象的大小。 ·ivars:指向该类的成员变量列表。 ·methodLists:指向该类的实例方法列表,它将方法选择器和方法实现地址联系起来。methodLists 是指向 ·objc_method_list 指针的指针,也就是说可以动态修改 *methodLists 的值来添加成员方法,这也是 Category 实现的原理,同样解释了 Category 不能添加属性的原因。 ·cache:Runtime 系统会把被调用的方法存到 cache 中(理论上讲一个方法如果被调用,那么它有可能今后还会被调用),下次查找的时候效率更高。 ·protocols:指向该类的协议列表(对象方法列表的扩展)。 */
③动态添加属性
#import <Foundation/Foundation.h> @interface NSObject (Property) // @property分类:只会生成get,set方法声明,不会生成实现,也不会生成下划线成员属性 @property NSString *name; @end #import "NSObject+Property.h" #import <objc/message.h> @implementation NSObject (Property) //static NSString *_name; - (void)setName:(NSString *)name { // 让这个字符串与当前对象产生联系 // _name = name; // object:给哪个对象添加属性 // key:属性名称 // value:属性值 // policy:保存策略 objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (NSString *)name { return objc_getAssociatedObject(self, @"name"); } @end
Runtime常用API汇总
objc_setAssociatedObject Sets an associated value for a given object using a given key and association policy 对象关联
objc_getAssociatedObject Returns the value associated with a given object for a given key . 对象关联
method_exchangeImplementations Exchange the implementations of two methods . 方法调配(method swizzle)
objc_getClassList Obtains the list of registered class definitions 获取所有类的列表
objc_getIvar Reads the value of an instance variable in an object . 读取一个实例变量的值
class_copyPropertyList Describes the properties declared by a class . 获取属性列表(模型转字典)
protocol_copyPropertyList Returns an array of the properties declared by a protocol.
RunTime用法
①给Category添加属性
Category中的属性只会生成set和get方法,不会生成成员变量
重写setter和getter方法
②UIButton添加点击的时间间隔(class_replaceMethod、method_exchangeImplemetations)
③字典与模型互转
④自动归档
⑤动态方法解析(Dynamic Method Resolution)
⑥发送消息(objc_msgSend)
⑦消息转发(Message Forwarding)
⑧内存泄漏查找(MethodSwizzling替换ViewWillAppear,ViewDidDisappear)
⑨方法欺骗:钩子-修改原来的方法调用顺序,改变对应关系。一旦你调用URLWithString ,就来到 HK_URLWithString