Runtime-总

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 

 

 

posted @ 2018-11-08 10:01  淡然微笑_Steven  阅读(279)  评论(0编辑  收藏  举报