一、Runtime的介绍
- Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不同
- Objective-C的动态性是由Runtime API来支撑的
- Runtime API提供的接口基本都是C语言的,源码由C\C++\汇编语言编写
二、isa详解
- 要想学习Runtime,首先要了解它底层的一些常用数据结构,比如isa指针
- 在arm64架构之前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址
- 从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息
- isa详解 – 位域
- nonpointer
- 0,代表普通的指针,存储着Class、Meta-Class对象的内存地址
- 1,代表优化过,使用位域存储更多的信息
- has_assoc
- 是否有设置过关联对象,如果没有,释放时会更快
- has_cxx_dtor
- 是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
- shiftcls
- 着Class、Meta-Class对象的内存地址信息
- magic
- 用于在调试时分辨对象是否未完成初始化
- weakly_referenced
- 是否有被弱引用指向过,如果没有,释放时会更快
- deallocating
- 对象是否正在释放
- extra_rc
- 里面存储的值是引用计数器减1
- has_sidetable_rc
- 引用计数器是否过大无法存储在isa中
- 如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
- 什么是isa?
答:在 arm64之前isa就是一个普通的指针,里面就是直接存储的类对象
和元类对象
的地址值,但是从arm64位之后,isa经过优化,它采用共用体的结构,将一个64位的内存数据分开来存储了很多的东西,而其中的33位来存储具体的地址值的。
三、objc_msgSend执行流程可以分为3大阶段
OC的方法调用:消息机制(objc_msgSend(id receiver, SEL selector)
),给方法调用者发送消息
3.1、消息发送(其实也就是isa与superclass寻找方法(对象方法
或类方法
))
- 如果是从 class_rw_t 中查找方法: 已经排序的二分查找,没有排序的遍历查找
- receiver通过isa指针找到receiverClass
- receiverClass通过superclass指针找到superClass
- 3.2、动态方法解析(在消息发送机制找不到方法之后进行动态方法解析)
- 可以实现以下方法,来动态添加方法实现
+ (BOOL)resolveInstanceMethod:(SEL)sel;(对象方法的动态添加)
+ (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(test)) { // 获取其他方法 Method method = class_getInstanceMethod(self, @selector(other)); // 动态添加test方法的实现 class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method)); // 返回YES代表有动态添加方法 return YES; } return [super resolveInstanceMethod:sel]; } -(void)other{ NSLog(@"%s",__func__); }
- + (BOOL)resolveClassMethod:(SEL)sel;
(类方法的动态添加)
+ (BOOL)resolveClassMethod:(SEL)sel { if (sel == @selector(classTest)) { // 第一个参数是object_getClass(self) //class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8"); // 获取其他方法 Method method = class_getClassMethod(self, @selector(classOther)); // 动态添加test方法的实现 class_addMethod(object_getClass(self), sel, method_getImplementation(method), method_getTypeEncoding(method)); // 返回YES代表有动态添加方法 return YES; } return [super resolveClassMethod:sel]; } #pragma mark 类方法的转换(类方法classTest找不到就转换classOther方法) +(void)classOther{ NSLog(@"类方法classTest找不到就转换classOther方法"); }
- Runtime动态转换方法的demo Person 类
- 3.3、消息转发
解释上面的图:
- 3.3.1、当动态解析过都找不到一个方法时候就会走消息转发,首先会调用
forwardingTargetForSelector
(前面可能是+(void)
或者-(void)
),如果不返回nil就会走 返回类的 Selector,如果返回nil就走 3.3.2
-(id)forwardingTargetForSelector:(SEL)aSelector{ if (aSelector == @selector(test)) { //return [[People alloc]init]; return nil; } return [super forwardingTargetForSelector:aSelector]; }
- 3.3.2、当forwardingTargetForSelector返回nil或者不写这个方法时候,就会调用下面的方法
#pragma mark 2 /** 方法签名:返回值类型、参数类型 */ - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { if (aSelector == @selector(test)) { // 获取其他方法(一),会走 #pragma mark 3 /* Method method = class_getInstanceMethod([People class], @selector(test)); return [NSMethodSignature signatureWithObjCTypes:method_getTypeEncoding(method)]; */ // 下面这一句和上面2句一个意思(二) 会走 #pragma mark 3 return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"]; /** 如果返回nil 就不会再走会走 #pragma mark 3 方法了,会报错返回(doesNotRecognizeSelector) -[NSObject(NSObject) doesNotRecognizeSelector:] + 132 */ // return nil; } return [super methodSignatureForSelector:aSelector]; } #pragma mark 3.上面的方法不返回nil才会走 /** NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数 anInvocation.target 方法调用者 anInvocation.selector 方法名 [anInvocation getArgument:NULL atIndex:0] */ - (void)forwardInvocation:(NSInvocation *)anInvocation { // anInvocation.target = [[MJCat alloc] init]; // [anInvocation invoke]; [anInvocation invokeWithTarget:[[People alloc] init]]; }
- 当上面的
#pragma mark 2
返回nil的时候,就不会走#pragma mark 3,且报错doesNotRecognizeSelector
Runtime消息转发的demo,看Student与People类