iOS高级开发 RunTime机制讲解五---runtime原理
下载DEMO:https://github.com/sleepsheep/RunTimeDemo05
1、首先创建一个项目;
2、创建一个父类:Animal
;
@interface Animal : NSObject
- (void)say;
@end
@implementation Animal
- (void)say {
NSLog(@"Animal say!");
}
@end
3、创建两个子类:Dog、DogOther
;
Dog:
消息的转发流程:
- 1、(动态添加方法)接收到未知消息时:(main中调用的says方法在Dog类及其父类中没有实现)runtime会调用: (实例方法)
+ (BOOL)resolveInstanceMethod:(SEL)sel
或者 (类方法)+ (BOOL)resolveClassMethod:(SEL)sel
; - 2、(备用接收者-->消息分发)如果1没有做任何处理,那么runtime就会调用
- (id)forwardingTargetForSelector:(SEL)aSelector
;
返回值满足的条件 : 1.非nil 2.非self 3.必须返回一个实现了该方法的对象(例如返回对象dogOther,让dogOther实现says方法,这样消息就能被成功的转发出去,被dogOther对象接受,并输出结果) - 3、(完整的消息转发)
- (void)forwardInvocation:(NSInvocation *)anInvocation
中选择转发消息的对象,Invocation
封装了未知消息的所有细节;
注意:必须重写methodSignatureForSelector才能走到forwardInvocation方法;
@class DogOther;
@interface Dog : Animal
@property (nonatomic, strong) DogOther *dogOther;
- (void)bark;
@end
#import "Dog.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import "DogOther.h"
@implementation Dog
- (instancetype)init
{
self = [super init];
if (self) {
self.dogOther = [DogOther new];
}
return self;
}
- (void)bark {
NSLog(@"Dog bark!");
}
////1、类方法调用最终找不到 会走这个方法
//+ (BOOL)resolveClassMethod:(SEL)sel {
//
// return [super resolveClassMethod:sel];
//}
//
////对象调用最终找不到 会走这个方法 我们可以在该方法中实现对应的处理逻辑
//+ (BOOL)resolveInstanceMethod:(SEL)sel {
// NSString *selStr = NSStringFromSelector(sel);//获取方法名
// if ([selStr isEqualToString:@"says"]) {
// //动态添加方法
// class_addMethod([self class], @selector(says), (IMP)notFoundSaysMethod, "@:");
// }
//
// return [super resolveInstanceMethod:sel];
//}
//
//void notFoundSaysMethod(id self, SEL _cmd) {
// printf("notFoundSaysMethod\n");
//}
////2、备用的接收者--->消息转发
//- (id)forwardingTargetForSelector:(SEL)aSelector {
// NSString *selStr = NSStringFromSelector(aSelector);
// if ([selStr isEqualToString:@"says"]) {
// return self.dogOther;
// }
//
// return [super forwardingTargetForSelector:aSelector];
//}
//3、完整消息转发
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([DogOther instancesRespondToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:self.dogOther];
} else {
[super forwardInvocation:anInvocation];
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if (!signature) {
if ([DogOther instancesRespondToSelector:aSelector]) {
signature = [DogOther instanceMethodSignatureForSelector:aSelector];
}
}
return signature;
}
DogOther:
@interface DogOther : Animal
- (void)says;
@end
@implementation DogOther
- (void)says {
NSLog(@"DogOther-->says");
}
@end
main:
准备工作:
#import <objc/runtime.h>
#import <objc/message.h>
在Build Settings 中设置enable strict checking of objc_msgsend calls
为 no
理解objc_class的结构体组成:
#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;
@brief 消息机制的运行原理 : 用[dog say]
为例来描述方法调用的流程
- 1、首先编译器会把
[dog say]
转换为objc_msgsend(dog, @selector(say))
两者的效果相同 - 2、Runtime会在dog对象对应的类Dog中的方法缓存中查找方法的SEL 即查找是否存在say方法(
objc_cache
) - 3、如果没有找到就会去Dog类的方法列表中查找say方法的SEL (通过类的isa指针指向的
methodlist
) - 4、如果没有找到,就会去Dog的父类Animal的方法列表中查找say方法的SEL (通过Dog类的
super_class
指向其父类) - 5、如果没有找到就继续在Animal的父类(NSObject)中查找,依次类推
- 6、如果在缓存、本类或者父类中找到,则定位到方法的入口开始执行
- 7、如果一直找到NSObject还是没有找到,那么就会根据以下两种情况进行处理: 1、如果方法的调用时使用的是[dog say]----->那么编译器就会因为找到不到响应的方法而报错; 2、如果方法的调用时使用的是
[dog performSelector:@selector(say)]
------>就会进入消息的转发流程;
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Dog *dog = [[Dog alloc] init];
// [dog say]; //等价于 objc_msgSend(dog, @selector(say));
// [dog bark];
//这种情况属于7中的第二种,进入消息的转发流程 参考 Dog类中的注释
[dog performSelector:@selector(says)];//notFoundSaysMethod
}
return 0;
}
不懂就药问