OC的消息机制
首先需要了解三个概念
实例(instance)
实例是类通过alloc生成的对象,每次调用alloc都会产生新的实例,占据着不同的内存。
实例的数据结构:
- isa指针(指向实例的类对象)
- 成员变量
- …
类(class)
类的数据结构:
- isa指针(指向元类)
- superclass指针(指向父类)
- property列表
- 实例方法列表
- 协议列表
- 成员变量
- …
元类(meta-class)
元类的数据结构:
- isa指针(指向基类的元类,也就是NSObject的元类)
- superclass指针(指向父类的元类)
- 类方法列表
- …
tip: NSObject元类的isa指针指向自身,而NSObject元类的superclass指针指向了NSObject的类对象,NSObject的类对象的superclass指针指向nil。
以下是方法查找流程
第一步: 查找方法缓存与方法表
- 根据对象或类的isa指针找到相应的类或元类,使用SEL(函数名地址)查找其方法缓存(cache_t,哈希表)
- 若方法缓存没有则查找方法表(method list,链表),若有则调用并将函数指针加入方法缓存。
- 如果没有则继续向上查父类的父类,直到NSObject。
如果调用的类方法仅在NSObject中有实例方法的实现,也是可以找到的,因为NSObject是其元类的父类。
第二步:动态解析
在以上流程走完后仍未找到消息处理的实现,则进入动态解析流程
-
- (BOOL)resolveClassMethod:(SEL)sel;
-
- (BOOL)resolveInstanceMethod:(SEL)sel;
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSString *method = NSStringFromSelector(sel);
if ([method isEqualToString:@"labelTouchAction"]) {
NSLog(@"在这里动态绑定方法");
return class_addMethod(self, sel, (IMP)sendIMP, "v@:@");
}
return NO;
}
void sendIMP(id self, SEL _cmd, NSString *msg) {
NSLog(@"动态绑定的方法 = %@", msg);
}
该方法返回一个BOOL,若为YES则表明消息已经处理,应用不会崩溃,若返回NO,则进入消息转发流程。
消息转发(直接转发)
-
- (id)forwardingTargetForSelector:(SEL)aSelector
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSString *method = NSStringFromSelector(aSelector);
if ([method isEqualToString:@"labelTouchAction"]) {
return [Object new];
}
return [super forwardingTargetForSelector:aSelector];
}
该方法返回一个id类型,系统会在这个对象中寻找方法实现,如果有则处理成功,如果没有则进入invocation的消息转发
消息转发(Invocation)
如果在这里还不了解NSInvocation,可以先去查文档了解一下,简而言之就是一种类似performSelector的消息转发方式,可以将消息转发给其他的对象,只不过performSelector至多带两个参数,而NSInvocation可以带多个参数。
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSMethodSignature *sig =nil;
NSString *selStr = NSStringFromSelector(aSelector);
if ([selStr isEqualToString:@"labelTouchAction"]) {
sig = [Method2Model methodSignatureForSelector:@selector(Method2)];
}else{
sig = [super methodSignatureForSelector:aSelector];
}
return sig;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
NSString *selStr = NSStringFromSelector(anInvocation.selector);
if ([selStr isEqualToString:@"labelTouchAction"]) {
[anInvocation setTarget:self.companyModel];
[anInvocation setSelector:@selector(Method2:)];
BOOL Argument =YES;
[anInvocation setArgument:&Argument atIndex:2];
[anInvocation retainArguments];
[anInvocation invoke];
}else{
[super forwardInvocation:anInvocation];
}
}
系统会先调用methodSignatureForSelector,这个方法需要你返回给它一个方法签名,然后系统调用forwardInvocation,可以在这个方法中用Invocation处理已经签名过的消息。(如果不理解请先了解NSInvocation)
同样都是消息转发,forwardingTargetForSelector与forwardInvocation的区别在于前者只能同时将消息转发给作为返回值的那一个对象,而后者则可以利用NSInvocation将消息转发给多个对象。
如果methodSignatureForSelector返回为nil,或者forwardInvocation中的消息转发得不到响应,系统则会调用doesNotRecognizeSelector,同时抛出unrecognized selector的异常,应用崩溃。