美文网首页iOS Developer
iOS 消息转发:这个锅谁背

iOS 消息转发:这个锅谁背

作者: Allan_野草 | 来源:发表于2017-03-21 16:52 被阅读108次

一、片头

在Objective-C中,方法调用可以说成是消息发送。向一个对象发送任意一条消息都是可以的,即使类中没有实现该消息(方法)。
比如下面的rev的类中没有willCrash这个方法,向对象rev发送消息

[rev performSelector:@selector(willCrash)]

objc_msgSend(rev,@selector(willCrash))

当执行到上面的这句话时,显然会发生崩溃。
(抛出 unrecognized selector sent to instance。)

好在,像这种情况,苹果贴心地提供了一种“补全”机制,
也叫“消息转发”机制,可以让编程者有机会处理崩溃。

消息转发,通俗来讲就是找人背锅。主要走的流程,是重写NSObject的三个方法:
1、自己试下补锅:+resolveInstanceMethod:(要处理静态方法就是+resolveClassMethod:)
2、让别人来背锅:-forwardingTargetForSelector:
3、还是自己解决吧:-(void)forwardInvocation:(NSInvocation *)anInvocation

二、详解

举个例,Person类,类中只有-run方法

// Person.h
@interface Person  : NSObject
- (void)run;
@end 

// Person.m
@implememtation
- (void)run {
  NSLog(@"run");
}
@end

向这个类对象调用一个不存在的方法fly

Person *man = [Person new];
[man performSelector:@selector(fly)]; // fly并没有相应实现

下面来看看,怎么用“消息转发”,从而让它不崩溃。

1、resolveInstanceMethod(自己试下补锅)

首先来重写Person类的-resolveInstanceMethod:方法,打个断点,可以看到在程序挂掉之前该方法被回调

+ (BOOL)resolveInstanceMethod:(SEL)sel {
  BOOL didResolve;
  // do something ..
  return didResolve;
}
  • 无论该方法return YES或者NO,都不影响转发行为。虽然不清楚return的作用,反正结果就是没什么卵用
  • 总之在这个方法结束之前,动态实现fly方法。下面是让@selector(fly)指向@selector(getTwoWings)的实现(其实可以这么理解,@selector(fly)和@selector(getTwoWings)都是一个符号,对应指向的都是同一个实现)
@implememtation
+ (BOOL)resolveInstanceMethod:(SEL)sel {
     if (sel==@selector(fly)) NSLog(@"try fly");

     // 获得方法对象
     Method impMethod = class_getInstanceMethod(self, @selector(getTwoWings));
     // 为fly方法添加getTwoWings的实现
     class_addMethod(self, @selector(fly), method_getImplementation(impMethod), method_getTypeEncoding(impMethod));

     // return YES NO都一样
     return YES;
}
- (void) getTwoWings {
      NSLog(@"get two wings and i will fly");
}
@end
// 不会发生崩溃,控制台打印 try fly
// 控制台打印 get two wings and i will fly
[man performSelector:@selector(fly)];

如果不给fly动态实现,接下来会进入“转发”

2、forwardingTargetForSelector(让别人来背锅)

“转发”会回调forwardingTargetForSelector:
把该消息转发给某个对象来处理:

- (id)forwardingTargetForSelector:(SEL)aSelector {
      id handler; // 可以处理该消息的对象
      return handler;
}
  • 若return为nil,不转发消息,后续仍然是自己来处理
  • 若return不为nil,由对象handler去回调处理这个消息。也就是相当于
[hander performSelector:@selector(fly)];
// 实现上是 IMP ptr = class_getMethodImplementation([handler class], @selector(fly));
// if ptr == NULL {让handler处理,转发到此结束}else{还有下一步}

3、forwardInvocation(还是自己解决吧)

回调Person的-(void)forwardInvocation:(NSInvocation *)anInvocation

-(void)forwardInvocation:(NSInvocation *)anInvocation {
     if (anInvocation.selector==@selector(fly)) {
          // 相应处理,消息转发完成
     }
}

三、测试例子

实现以下的场景:
向person发送fly消息,实际上让另一个对象去调用它的fly方法

重写-forwardingTargetForSelector:,把fly消息转发给Plane类对象去处理

// Person类
@implememtation
+ (BOOL)resolveInstanceMethod:(SEL)sel {
     if (sel==@selector(fly)) NSLog(@"try fly");// 没有实现-fly,那么消息将转发
     return NO;
}
-(id)forwardingTargetForSelector:(SEL)aSelector {
      Plane *plane= [Plane new];
      return plane;// 转发给plane对象
}
@end

Plane是另外一个类:

// - Plane.h
@interface Plane : NSObject
@end 
// - Plane.m
@implememtaion Plane
-(void)fly {
      NSLog(@"take a plane and i will fly");
}
@end

测试结果

Person *man = [Person new];
[man performSelector:@selector(fly)]; 
// 控制台打印 try fly
// 控制台打印 take a plane and i will fly

相当于调用了[plane fly];

片尾

当向一个对象发送一个它不能处理的消息/方法,苹果会不断地询问你,去动态实现这个方法,简而言之,消息转发就是这么一回事。

实际应用上,消息转发配合上Runtime也有很多玩法。比如。。就不比如了,哪天写一篇文章,先挖个坑吧~

再至于流程3的forwardInvocation中,NSInvocation怎么个用法?请看另外下面这篇文章,拉到最底就能看到了
NSInvocation:iOS 不走寻常路:调用方法的6种姿势,你知道几种

End

相关文章

  • iOS 消息转发:这个锅谁背

    一、片头 在Objective-C中,方法调用可以说成是消息发送。向一个对象发送任意一条消息都是可以的,即使类中没...

  • iOS 消息发送与转发详解

    iOS 消息发送与转发详解 iOS 消息发送与转发详解

  • iOS理解Objective-C中消息转发机制附Demo

    iOS理解Objective-C中消息转发机制附Demo iOS理解Objective-C中消息转发机制附Demo

  • 外卖垃圾这个锅该谁背?

    要点 | 速读 外卖平台看上去非常强大,但是那个假设中的“上帝”(消费者)才是问题的关键所在。 消费者的责任最大,...

  • 熬夜长胖,这个锅谁背?

    看着自己一层又一层的游泳圈,小编泪目,长胖了,这个锅究竟谁来背? 近日,国外某医院的研究者,在调查了1600名44...

  • runtime系列文章总结

    《iOS Runtime详解(消息机制,类元对象,缓存机制,消息转发)》《消息转发机制与Aspects源码解析》《...

  • iOS消息转发

    title: “iOS消息转发”date: 2016-03-22 13:48:38tags: 消息转发这块,我学习...

  • ios 消息转发

    ios在类中,没有定义的函数,要走消息转发流程。如果不走消息转发流程,程序会奔溃。消息转发流程分四步调用。 第一步...

  • iOS 消息转发

    有时候我们常常看到一个cash 信息,意思是这个对象不存在这个方法,你向这个对象发送消息就会crash。 我们除了...

  • iOS 消息转发

    消息转发是Objective-C运行时的一个重要特性,具体表现是当调用一个不存在的方法时,并不会立马Crash,R...

网友评论

    本文标题:iOS 消息转发:这个锅谁背

    本文链接:https://www.haomeiwen.com/subject/atbkmttx.html