首页
学习
活动
专区
圈层
工具
发布

ios objc向一个对象发送消息时,发生了什么?

objc向一个对象发送消息时,发生了什么?

根据对象的isa指针找到该对象所属的类,去obj的对应的类中找方法 1.首先,在相应操作的对象中的缓存方法列表中找调用的方法,如果找到,转向相应实现并执行。 2.如果没找到,在相应操作的对象中的方法列表中找调用的方法,如果找到,转向相应实现执行 3.如果没找到,去父类指针所指向的对象中执行1,2. 4.以此类推,如果一直到根类还没找到,转向拦截调用,走消息转发机制。 5.如果没有重写拦截调用的方法,程序报错。

objc中向一个nil对象发送消息将会发生什么?

如果向一个nil对象发送消息,首先在寻找对象的isa指针时就是0地址返回了,所以不会出现任何错误。也不会崩溃。

如果寻找不到相应的方法,会如何进行后续处理 ?

转向拦截调用。如果没有重写拦截调用的方法,程序报错。 拦截调用就是,在找不到调用的方法程序崩溃之前,你有机会通过重写NSObject的四个方法来处理。

方案一:

  • (BOOL)resolveInstanceMethod:(SEL)sel
  • (BOOL)resolveClassMethod:(SEL)sel 方案二:
  • (id)forwardingTargetForSelector:(SEL)aSelector 方案三:
  • (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
  • (void)forwardInvocation:(NSInvocation *)anInvocation;

1448535-f50463d32de94289.jpg

上图显示了消息转发的具体流程,接收者在每一步中均有机会处理消息。步骤越往后处理消息的代价越大。首先,会调用

  • (BOOL)resolveInstanceMethod:(SEL)sel。 若方法返回YES,则表示可以处理该消息。在这个过程,可以动态地给消息增加方法。
代码语言:javascript
复制
id ForwardingTarget_dynamicMethod(id self, SEL _cmd) {
    NSLog(@"没有找到方法:%@",NSStringFromSelector(_cmd));
    return [NSNull null];
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    class_addMethod(self.class, sel, (IMP)ForwardingTarget_dynamicMethod, "@@:");
    [super resolveInstanceMethod:sel];
    return YES;
}

调用

代码语言:javascript
复制
 People *people = [[People alloc] init];
 [people performSelector:@selector(speak)];

若方法返回NO,则进行消息转发的第二步,查找是否有其它的接收者。对应的处理函数是:

  • (id)forwardingTargetForSelector:(SEL)aSelector。 可以通过该函数返回一个可以处理该消息的对象。 现在新建一个类MsgForwarding,在MsgForwarding中实现一个speak方法. MsgForwarding.m
代码语言:javascript
复制
- (void)speak
{
    NSLog(@"MsgForwarding method speak called");
}

然后在People类中实现

代码语言:javascript
复制
- (id)forwardingTargetForSelector:(SEL)aSelector {

    NSString *selStr = NSStringFromSelector(aSelector);

    if ([selStr isEqualToString:@"speak"]) {
         // 这里返回MsgForwarding类对象,让MsgForwarding类去处理speak消息
        return [[MsgForwarding alloc] init];
    }

    return [super forwardingTargetForSelector: aSelector];
}

若第二步返回nil,则进入消息转发的第三步。调用 *- (void)forwardInvocation:(NSInvocation )anInvocation。

代码语言:javascript
复制
- (void)forwardInvocation:(NSInvocation *)anInvocation {
  
    SEL selector = [anInvocation selector];
    // 新建需要转发消息的对象
    MsgForwarding *msgForwarding = [[MsgForwarding alloc] init];
    if ([msgForwarding respondsToSelector:selector]) {
        // 唤醒这个方法
        [anInvocation invokeWithTarget:msgForwarding];
    }
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    
    if (aSelector == @selector(speak)) {
        return [NSMethodSignature signatureWithObjCTypes:"V@:@"];
    }
    return [super methodSignatureForSelector:aSelector];
}

Demo:https://github.com/destinyzhao/MessageForwarding

举报
领券