- Runtime的介绍
- Runtime消息的传递和转发
- Runtime的应用
1.Runtime的介绍
Objective-C 是一门动态的语言,相比较于C语言,函数的调用在编译时期就已经确定,而OC在编译的时候不能确定调用那个函数,因为在运行时期,可以动态的为类添加方法,这一功能的实现依赖于Runtime。这意味着它不仅需要一个编译器,也需要一个运行时系统来动态的创建类和对象、进行消息传递和转发。
2.Runtime消息的传递与转发
2.1消息的传递
在Objective-C中,如果向某对象传递信息,那就会使用动态绑定机制来决定需要调用的方法。在底层,所有方法都是普通的C语言函数,然而对象收到消息之后,究竟该调用哪个方法则完全取决运行期决定,甚至可以在程序运行时改变,这些特性使得Objective-C成为一门真正的动态语言。
给对象发送消息可以这样来写:
id returnValue = [someObject messageName:parameter];
在本例中,someObject叫做"接收者",messageName叫做"选择子"。选择子与参数合起来称为"消息"。编译器看到此消息后,将其转化为一条标准的C语言函数调用,所调用的函数乃是消息传递机制中的核心函数,叫做objc_msgSend,其原型如下:
void objc_msgSend(id self,SEL cmd, ...)
Runtime执行的流程大致如下:
- 首先通过someObject的isa指针找到所属的类。
- 在当前类的method_list中寻找名为messageName的方法,找到了就执行IMP的方法实现。
- 如果没找到就通过类中的superclass指针,沿着类的继承体系继续查找,找到了,就跳到实现代码。
- 如果没有找到,就执行“消息转发”操作。
这么说来,想调用一个方法需要许多步骤。索性objc_msgSend会将匹配结果缓存在“快速映射表”里面,每一个类都有一个这样的缓存,若是以后还要发送和选择子相同的消息,那么执行起来就快了。这也就是objc_class 中另一个重要成员objc_cache 做的事情 - 再找到messageName 之后,把messageName 的method_name 作为key ,method_imp作为value 给存起来。当再次收到messageName 消息的时候,可以直接在cache 里找到,避免去遍历objc_method_list。
这里补充一张类的结构图,以便更好地理解:
2.2消息的转发
消息转发分为两大阶段:
- 第一阶段先征询接收者,所属的类,看其能否动态添加方法,以处理当前这个“未知的选择子”,这叫做“动态方法解析”。
- 第二阶段为“完整的消息转发机制”,这一阶段又细分为两个阶段:
- 请接收者看看有没有其它对象能处理这条消息,若有,则运行时系统会把这个消息转给那个对象,到此,消息转发过程结束。若没有“备援接收者”,则启动完整的消息转发机制。
- 运行时系统会把与消息有关的全部细节都封装到NSInvocation对象中,再给对象最后一次机会。
动态方法解析
- (void)viewDidLoad{
[super viewDidLoad];
[self performSelector:@selector(messageName:)];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSString * selString = NSStringFromSelector(sel);
if([selString isEqualToString:@"messageName"]){
class_addMethod([self class],sel, (IMP)test,"v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void test(id obj,SEL _cmd){
NSLog(@"add instance method success!");
}
返回YES,则消息转发结束,返回NO,则进入下一步,"备援接收者"。
备援接收者
@interface Person :NSObject
@end
@implementation
- (void)run{
NSLog(@"run ------");
}
@end
- (void)viewDidLoad{
[super viewDidLoad];
[self performSelector:@selector(messageName:)];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
return NO;// 进入备援接收者流程
}
- (id)forwardingTargetSelector:(SEL)sel{
if(sel == @selector(messageName)){
return [Person alloc] init];
}
return [super forwardingTargetSelector:sel];
}
若当前接收者能找到备援对象,则将其返回,若找不到,就返回nil。
完整的消息转发流程
@interface Person: NSObject
@end
@implementation Person
- (void)run{
NSLog(@"run ------");
}
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:@selector(messageName:)];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
return NO;//返回YES,消息转发结束,返回NO,进入下一步转发
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
return nil;//返回nil,进入下一步转发
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if ([NSStringFromSelector(aSelector) isEqualToString:@"messageName"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = anInvocation.selector;
Person *p = [[Person alloc] init];
if([p respondsToSelector:sel]) {
[anInvocation invokeWithTarget:p];
}
else {
[self doesNotRecognizeSelector:sel];
}
}
@end
3.Runtime的应用
- 动态添加方法
在面试过程中经常会被问到:有没有使用performSelector,其主要想问你有没有动态添加过方法
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad{
[super viewDidLoad];
Person * p = [[Person alloc] init];
// 默认person没有实现eat方法
[p performSelector:@selector(eat)];
}
@end
@implementation Person
void eat (id self, SEL sel){
NSLog(@"%@ , %@",self, NSStringFromSelector(sel));
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if(see == @selector(eat)){
// 动态添加eat方法
// 第一个参数:给哪个类添加方法
// 第二个参数:添加方法的方法编号
// 第三个参数:添加方法的函数实现
// 第四个参数:函数的类型
class_addMethod(self, @selector(eat), eat, "v@:");
return YES
}
[super resolveInstanceMethod:sel];
}
- 关联对象
下面实现一个UIView 的category 添加自定义属性defaultColor
@interface UIVIew (DefaultColcor)
@property (nonatomic, strong) UIColor * defalutColor;
@end
@implementation
static char *kDefaultColor = @“kDefaultColor”;
- (void)setDefaultColor:(UIColor *)defaultColor{
objc_setAssociatedObject(self, &kDefaultColorKey,defaultColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)defaultColor{
return objc_getAssociatedObject(self, &kDefaultColor);
}
@end
- KVO的实现
Runtime梳理(二)KVO原理及实现 - KVC的实现(待完成)