RunTime是什么?
- Runtime是指将数据类型的确定由编译时推迟到了运行时
- Runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API
- 平时编写的OC代码,在程序运行过程中,其实最终会转换成Runtime的C语言代码,Runtime是Object-C的幕后工作者
- Object-C需要Runtime来创建类和对象,进行消息发送和转发
RunTime用来干什么?
- 在程序运行过程中,动态的创建类,动态添加、修改这个类的属性和方法
- 遍历一个类中所有的成员变量、属性、以及所有方法
- 消息传递、转发
RunTime用在哪些地方?
- 给分类添加属性、方法
- 方法交换
- 获取对象的属性、私有属性
- 字典转换模型
- KVC、KVO
- 归档(编码、解码)
- NSClassFromString class<->字符串
- block
- 类的自我检测
RunTime有什么样的对象关系?
先看代码:
NSObject *o = [[NSObject alloc] init];
Class o_class = [o class];
Class o_class_class = object_getClass(o_class);
Class o_class_class_class = object_getClass(o_class_class);
Class o_class_class_class_class = object_getClass(o_class_class_class);
NSLog(@"NSObject对象 %@ %p", o, o);
NSLog(@"NSObject类对象 %@ %p", o_class, o_class);
NSLog(@"NSObject类元对象 %@ %p", o_class_class, o_class_class);
NSLog(@"NSObject类元对象的类元对象 %@ %p", o_class_class_class, o_class_class_class);
NSLog(@"NSObject类元对象的类元对象的类元对象 %@ %p", o_class_class_class_class, o_class_class_class_class);
A *a = [[A alloc] init];
Class a_class = [a class];
Class a_class_class = object_getClass(a_class);
Class a_class_class_class = object_getClass(a_class_class);
Class a_class_class_class_class = object_getClass(a_class_class_class);
NSLog(@"A对象 %@ %p", a, a);
NSLog(@"A类对象 %@ %p", a_class, a_class);
NSLog(@"A类元对象 %@ %p", a_class_class, a_class_class);
NSLog(@"A类元对象的类元对象 %@ %p", a_class_class_class, a_class_class_class);
NSLog(@"A类元对象的类元对象的类元对象 %@ %p", a_class_class_class_class, a_class_class_class_class);
B *b = [[B alloc] init];
Class b_class = [b class];
Class b_class_class = object_getClass(b_class);
Class b_class_class_class = object_getClass(b_class_class);
Class b_class_class_class_class = object_getClass(b_class_class_class);
NSLog(@"B对象 %@ %p", b, b);
NSLog(@"B类对象 %@ %p", b_class, b_class);
NSLog(@"B类元对象 %@ %p", b_class_class, b_class_class);
NSLog(@"B类元对象的类元对象 %@ %p", b_class_class_class, b_class_class_class);
NSLog(@"B类元对象的类元对象的类元对象 %@ %p", b_class_class_class_class, b_class_class_class_class);
Class o_class_superclass = class_getSuperclass(o_class);
Class o_class_class_superclass = class_getSuperclass(o_class_class);
NSLog(@"NSObject类对象的父类 %@ %p", o_class_superclass, o_class_superclass);
NSLog(@"NSObject类元对象的父类 %@ %p", o_class_class_superclass, o_class_class_superclass);
Class a_class_superclass = class_getSuperclass(a_class);
Class a_class_class_superclass = class_getSuperclass(a_class_class);
NSLog(@"A类对象的父类 %@ %p", a_class_superclass, a_class_superclass);
NSLog(@"A类元对象的父类 %@ %p", a_class_class_superclass, a_class_class_superclass);
Class b_class_superclass = class_getSuperclass(b_class);
Class b_class_class_superclass = class_getSuperclass(b_class_class);
NSLog(@"B类对象的父类 %@ %p", b_class_superclass, b_class_superclass);
NSLog(@"B类元对象的父类 %@ %p", b_class_class_superclass, b_class_class_superclass);
然后看运行结果:
NSObject对象 <NSObject: 0x2807986b0> 0x2807986b0
NSObject类对象 NSObject 0x1fd64ee18
NSObject类元对象 NSObject 0x1fd64edf0
NSObject类元对象的类元对象 NSObject 0x1fd64edf0
NSObject类元对象的类元对象的类元对象 NSObject 0x1fd64edf0
A对象 <A: 0x280798670> 0x280798670
A类对象 A 0x100b0d530
A类元对象 A 0x100b0d508
A类元对象的类元对象 NSObject 0x1fd64edf0
A类元对象的类元对象的类元对象 NSObject 0x1fd64edf0
B对象 <B: 0x280790230> 0x280790230
B类对象 B 0x100b0d4e0
B类元对象 B 0x100b0d4b8
B类元对象的类元对象 NSObject 0x1fd64edf0
B类元对象的类元对象的类元对象 NSObject 0x1fd64edf0
NSObject类对象的父类 (null) 0x0
NSObject类元对象的父类 NSObject 0x1fd64ee18
A类对象的父类 NSObject 0x1fd64ee18
A类元对象的父类 NSObject 0x1fd64edf0
B类对象的父类 A 0x100b0d530
B类元对象的父类 A 0x100b0d508
从中可以整理出下面的关系图:
消息机制
消息机制就是向接收者发送消息,并带有参数,根据接收者对象的数据结构,找到相关发放实现,最后达到这个消息的目的
objc_msgSend
是Runtime的核心,Objective-C中调用对象方法就是消息传递
objc_msgSend
并不是直接调用方法实现(IMP)而是发送消息,让类的结构体去动态查到方法实现,所以在为查找到方法实现之前我们可以动态的去修改这个方法的实现
示例代码:
#import <objc/message.h>
# Build Settings中将Enable Strict Checking of objc_msgSend Calls设为NO
B *b = [[B alloc] init];
objc_msgSend(b, @selector(walk));
Objc-msgSend的工作
- 找到方法的实现。由于通过单独的类以不同方式创建相同的方法,因此这个方法的实现的确定,取决于接收消息的类对象,也即是说多个实例类可以创建同样的方法,每个实例对象中的该方法都是独立存在的
- 调用该方法实现。将接收消息类指针,以及该方法的参数传递给这个类
- 最后将过程的返回值作为自己的返回值传递
Objc-msgSend的发送过程
- 消息发送给对象时,消息传递函数遵循对象的isa指针指向类结构的指针,在该结构中它查询结构体变量
methodLists
中的方法SEL
(方法选择器) - 如在isa指向的类结构中找不到
SEL
(方法选择器),Objc_msgSend会指向supercalss
(父类)指针并再次尝试查找该SEL
- 如连续失败直到
NSObject
类,它的superclass
也就是它自己本身
一旦找到SEL
,该函数就会调用methodLists
的方法并将接收对象的指针传给它
消息转发
在Objective-C中,使用对象进行方法调用是一个消息发送的过程(Objective-C采用“动态绑定机制”,所以所要调用的方法直到运行期才能确定)
方法在调用时,系统会查看这个对象能否接收这个消息(查看这个类有没有这个方法,或者有没有实现这个方法),如果不能并且只在不能的情况下,就会调用下面这几个方法,给你“补救”的机会
你可以先理解为几套防止程序crash的备选方案,苹果就是利用这几个方案进行消息转发
注意一点,前一套方案实现后一套方法就不会执行。如果这几套方案你都没有做处理,那么程序就会报错crash
OC的运行时在程序崩溃前提供了三次拯救程序的机会
方案一:
+(BOOL)resolveInstanceMethod:(SEL)sel
+(BOOL)resolveClassMethod:(SEL)sel
方案二:
-(id)forwardingTargetForSelector:(SEL)aSelector
方案三:
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
-(void)forwardInvocation:(NSInvocation *)anInvocation
代码实现:
A *a = [[A alloc] init];
[a walk];
[a run];
[a fly];
/// 方案1
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"A reslove instance method %@", NSStringFromSelector(sel));
if (sel == @selector(walk)) {
class_addMethod(self, sel, (IMP)do_method, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void do_method(id self, SEL sel) {
NSLog(@"A do method %@", NSStringFromSelector(sel));
}
/// 方案2
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@"A forwarding target for selector %@", NSStringFromSelector(aSelector));
if (aSelector == @selector(run)) {
return [[B alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
/// 方案3
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSLog(@"A method signature for selector %@", NSStringFromSelector(aSelector));
if (aSelector == @selector(fly)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"A forward invocation %@ %@", anInvocation, NSStringFromSelector([anInvocation selector]));
B *b = [[B alloc] init];
if ([b respondsToSelector:[anInvocation selector]]) {
[anInvocation invokeWithTarget:b];
}
}
运行结果:
A reslove instance method walk
A do method walk
A reslove instance method run
A forwarding target for selector run
B run
A reslove instance method fly
A forwarding target for selector fly
A method signature for selector fly
A reslove instance method _forwardStackInvocation:
A forward invocation <NSInvocation: 0x2802fc380> fly
B fly
案例
归档/解档
#import <objc/message.h>
// 归档
- (void)encodeWithCoder:(NSCoder *)coder {
unsigned int count = 0;
Ivar *pIvar = class_copyIvarList([self class], &count);
for (int i = 0; i < count; ++i) {
Ivar ivar = pIvar[i];
const char *name = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:name];
[coder encodeObject:[self valueForKey:key] forKey:key];
}
free(pIvar);
}
// 解档
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
unsigned int count = 0;
Ivar *pIvar = class_copyIvarList([self class], &count);
for (int i = 0; i < count; ++i) {
Ivar ivar = pIvar[i];
const char *name = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:name];
id value = [coder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
free(pIvar);
}
return self;
}
方法交换
#import <objc/message.h>
@implementation NSURL (Extension)
+ (void)load {
Method method_1 = class_getClassMethod(self, @selector(URLWithString:));
Method method_2 = class_getClassMethod(self, @selector(extension_URLWithString:));
method_exchangeImplementations(method_1, method_2);
}
+ (instancetype)extension_URLWithString:(NSString *)URLString {
NSURL *url = [self extension_URLWithString:URLString];
if (url == nil) {
NSLog(@"NSURL URLWithString return nil");
}
return url;
}
@end