小喇叭: 个人笔记 个人笔记 个人笔记啦
当在浏览器输入runtime,经过0.几秒,哇,这绝对是iOS界的热搜啊。
这里作为个人笔记,没有太多关于runtime的概念解释,毕竟有官方圣经可以看
runtime笔记分为:
1. 消息转发机制 (OC && swift)
2. 通过分类扩展属性 (OC && swift)
3. 拦截系统方法 (OC && swift)
4. 自动化归档 (OC && swift)
5. swift反射
对象(object),类(class),方法(method)的结构体
在obje.h runtime.h 里边找到对应的结构体定义
/**
对象
*/
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/**
类
*/
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/**
方法列表
*/
struct objc_method_list {
struct objc_method_list *obsolete OBJC2_UNAVAILABLE;
int method_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
/* variable length structure */
struct objc_method method_list[1] OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE;
/**
方法
*/
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
}
看过这几个结构体定义,来看一下消息传递的实现
eg: [someone doSomething]
编译器转成消息发送objc_msgSend(someone, doSomething)
1.系统首先找到消息的接收对象 someone ,然后通过someone的isa找到它的类。
2.在它的类中查找method_list,是否有doSomething方法。
3.没有则查找父类的method_list。
4.找到对应的method,执行它的IMP。
5.转发IMP的return值。
再来看几个关键词
- 类对象(objc_class)
- 实例(objc_object)
- 元类(Meta Class)
- Method(objc_method)
- SEL(objc_selector)
- IMP
- 类缓存(objc_cache)
- Category(objc_category)
类对象(objc_class)
typedef struct objc_class *Class
Objective-C类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针。
通过上边的结构体,可以看到结构体里保存了指向父类的指针、类的名字、版本、实例大小、实例变量列表、方法列表、缓存、遵守的协议列表等。这个结构体存放的数据称为元数据(metadata)。
该结构体的第一个成员变量也是isa指针,这就说明了Class本身其实也是一个对象,因此我们称之为类对象,类对象在编译期产生用于创建实例对象,是单例。
实例(objc_object)
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
类对象中的元数据存储的都是如何创建一个实例的相关信息。
元类(Meta Class)
既然类对象中的元数据存储的都是如何创建一个实例的相关信息,那么类对象和类方法应该从哪里创建呢?
就是类对象的isa指针指向的我们称之为元类(metaclass)
元类(Meta Class)是一个类对象的类。
所有的类自身也是一个对象,我们可以向这个对象发送消息(即调用类方法)。
为了调用类方法,这个类的isa指针必须指向一个包含这些类方法的一个objc_class结构体。这就引出了meta-class的概念,元类中保存了创建类对象以及类方法所需的所有信息。
任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而基类的meta-class的isa指针是指向它自己。
Method(objc_method)
Method和我们平时理解的函数是一致的,就是表示能够独立完成一个功能的一段代码。
eg:
- (void)doSomething {
NSLog(@"doSomething");
}
通过开始我们看的objc_method结构体,我们已经看到了==SEL==和==IMP==,说明==SEL==和==IMP==其实都是Method的属性。
SEL(objc_selector)
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
可以看到objc_msgSend函数第二个参数类型为SEL,它是selector在Objective-C中的表示类型(Swift中是Selector类)。
selector是方法选择器,就是映射到方法的C字符串。
IMP
/// A pointer to the function of a method implementation.
typedef id (*IMP)(id, SEL, ...);
#endif
就是指向最终实现程序的内存地址的指针。
在iOS的Runtime中,Method通过selector和IMP两个属性,实现了快速查询方法及实现,相对提高了性能,又保持了灵活性。
类缓存
对于我们所写的诸多方法,可能只调用它们的一小部分,并且每次查找时,搜索所有选择器的类分派表没有意义。所以类实现一个缓存,每当你搜索一个类分派表,并找到相应的选择器,它把它放入它的缓存。所以当objc_msgSend查找一个类的选择器,它首先搜索类缓存。这是基于这样的理论:如果你在类上调用一个消息,你可能以后再次调用该消息。所以尽管OC是动态调用方法,但是runtime系统实际上非常快,接近直接执行内存地址的程序速度。
参考: http://www.cocoachina.com/ios/20150818/13075.html
Category(objc_category)
一个指向分类的结构体的指针
struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
};
这里说一下instanceProperties:表示Category里所有的properties,这就是我们可以通过objc_setAssociatedObject和objc_getAssociatedObject扩展实例变量的原因,不过这个和一般的实例变量是不一样的。
消息转发
==这里说一下 swift 是没有签名的那两个步骤的==,也就是图中第3块。
进行一次发送消息会在相关的类对象中搜索方法列表,如果找不到则会沿着继承树向上一直搜索知道继承树根部(通常为NSObject),如果还是找不到并且消息转发都失败了就回执行doesNotRecognizeSelector:方法报unrecognized selector错。那么消息转发到底是什么呢?接下来将会逐一介绍最后的三次机会。
- 动态方法解析
- 备用接收者
- 完整消息转发
下面看一下例子
OC
动态方法解析
我们创建一个 God 类,在.h中声明一个方法
-(void)dispatchOrder: (NSString *)message;
我们不在.m中做实现话,直接[[[God alloc] init] dispatchOrder: @"I am God"];肯定是会崩溃的。
在.m中
首先,Objective-C运行时会根据实例方法或者类方法调用 +resolveInstanceMethod: 或者 +resolveClassMethod: 让我们去提供一个函数实现。
如果你添加了函数并返回YES, 那运行时系统就会重新启动一次消息发送的过程。
+ (BOOL)resolveInstanceMethod:(SEL)sel {
/**
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types)
*/
if (sel == @selector(dispatchOrder:)) {
return class_addMethod(self, sel, (IMP)dispatchOrder, "v@:@");
}
// 如果没有我们走继承树
return [super resolveInstanceMethod: sel];
}
void dispatchOrder(id obj, SEL _cmd, NSString *message) {
NSLog(@"%@", message);
}
备用接收者(快速转发)
如果resolve方法返回 NO ,运行时就会执行:forwardingTargetForSelector
创建一个Person类,实现
@implementation Person
-(void)dispatchOrder: (NSString *)message {
NSLog(@"%@,person", message);
}
@end
在God中
+ (BOOL)resolveInstanceMethod:(SEL)sel {
/**
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types)
*/
// if (sel == @selector(dispatchOrder:)) {
// return class_addMethod(self, sel, (IMP)dispatchOrder, "v@:@");
// }
// 如果没有我们走继承树
return [super resolveInstanceMethod: sel];
}
void dispatchOrder(id obj, SEL _cmd, NSString *message) {
NSLog(@"%@", message);
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(dispatchOrder:)) {
return [Person new];
}
return [super forwardingTargetForSelector:aSelector];
}
完整消息转发 (swift不支持)
分为2步:
- 方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(dispatchOrder:)) {
return [NSMethodSignature signatureWithObjCTypes: "v@:@"];
}
return [super methodSignatureForSelector:aSelector];
}
- 消息转发
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = [anInvocation selector];
Person *person = [[Person alloc] init];
if ([person respondsToSelector:sel]) {
[anInvocation invokeWithTarget: person];
return;
}
[super forwardInvocation: anInvocation];
}
消息无法处理
- (void)doesNotRecognizeSelector:(SEL)aSelector {
NSLog(@"消息无法处理");
}
==以上是OC的消息转发过程==
swift
- 动态方法解析
override class func resolveInstanceMethod(_ sel: Selector!) -> Bool {
if sel == Selector(("dispatchOrder:")) {
if let method = class_getInstanceMethod(self, #selector(God.dispatchMessage(_:))) {
let imp = method_getImplementation(method)
let type = method_getTypeEncoding(method)
return class_addMethod(self, Selector(("dispatchOrder:")), imp, type)
}
}
return false
}
@objc func dispatchMessage(_ message: String) {
print("\(message)")
}
- 备用接受者
class Person: NSObject {
@objc func dispatchOrder(_ message: String) {
print("\(message), person")
}
}
override class func resolveInstanceMethod(_ sel: Selector!) -> Bool {
// if sel == Selector(("dispatchOrder:")) {
// if let method = class_getInstanceMethod(self, #selector(God.dispatchMessage(_:))) {
// let imp = method_getImplementation(method)
// let type = method_getTypeEncoding(method)
// return class_addMethod(self, Selector(("dispatchOrder:")), imp, type)
// }
// }
return false
}
@objc func dispatchMessage(_ message: String) {
print("\(message)")
}
override func forwardingTarget(for aSelector: Selector!) -> Any? {
return Person()
}