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的异常,应用崩溃。

本篇到此为止,希望这对你有帮助,如果有错误或是有需要补充的地方,望告知。


种一棵树最好的时间是在十年前,而后是现在。

Loading Disqus comments...
Table of Contents