前言ios
其余编程语言所说的函数调用,在oc中被称做为发送消息;消息转发的做用,开发者能够在找不到的方法的状况下,能够经过动态添加方法或者是消息转发,肯定本次发送消息是否成功,经过这样的特性开发者能够作不少必要的善后处理。编程
ios消息转发的做用;app
(1)对象发送了未实现的消息,能够经过消息转发机制,转移到其余对象去处理该消息;编程语言
(2)除了协议、类别,也能够经过消息转发机制实现多继承;函数
实现ios消息转发前的准备工做;atom
(1)为了配合下面的实例预先定义好SleepViewController,实现sleep和eat方法;代理
#import "SleepViewController.h" @interface SleepViewController () @end @implementation SleepViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } /* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */ - (void)sleep { NSLog(@"excute SleepViewController sleep"); } - (void)eat { NSLog(@"ok,begin eat"); } @end
ios消息转发的过程;code
(1)动态方法解析,好比执行[self performSelector:@selector(name)];在本类父类以及NSObject都未能查找到该方法,就是先判断是否须要动态添加该方法,本例运用runtime动态添加了testClassName方法,执行结果会输出"add testClassName method";orm
+ (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(name)) { class_addMethod(self, sel, class_getMethodImplementation([self class], @selector(testClassName:)), method_getTypeEncoding(class_getInstanceMethod([self class], @selector(testClassName:)))); return YES; }else if (sel == @selector(sleep)){ return NO; } return [super resolveInstanceMethod:sel]; } - (void)testClassName:(NSString *)string { NSLog(@"add testClassName method"); }
(2)若是(BOOL)resolveInstanceMethod:(SEL)sel或者(BOOL)resolveClassMethod:(SEL)sel返回NO,消息转发就会进入转发机制;好比执行[self performSelector:@selector(sleep)],这个sleep方法在SleepViewController声明并实现,结果输出"excute SleepViewController sleep";对象
- (id)forwardingTargetForSelector:(SEL)aSelector { NSLog(@"not found method sleep"); if (aSelector == @selector(sleep)) { return [[SleepViewController alloc] init]; }else{ NSLog(@"not found eat"); return nil; } }
(3)当重定向还不做处理的时候,这消息转发机制就会出发,这个时候就会调用- (void)forwardInvocation:(NSInvocation *)anInvocation这个方法;可是在这个方法执行以前会先调用methodSignatureForSelector方法,因此咱们也要复写这个方法,不然就会报异常;因此咱们要重写这个方法。好比执行[self performSelector:@selector(eat)];既没有动态添加该方法,也没有重定向该方法,因此执行了一下的两个方法,作最后的逻辑处理;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSLog(@"method name -- %@",NSStringFromSelector(aSelector)); if (aSelector == @selector(eat)) { //签名方法; return [NSMethodSignature signatureWithObjCTypes:"v@:"]; } return [super methodSignatureForSelector:aSelector]; } - (void)forwardInvocation:(NSInvocation *)anInvocation { SEL sele = [anInvocation selector]; SleepViewController *slee = [[SleepViewController alloc] init]; if ([slee respondsToSelector:sele]) { //转发到SleepViewController执行eat方法; [anInvocation invokeWithTarget:slee]; }else{ [super forwardInvocation:anInvocation]; } }
解释:在没有动态添加方法状况下,第(2)(3)步,都是将处理消息的操做,转移给了其余的对象。区别就是在第(2)步转发,只能转发一个指定的对象;在第(3)步的话,能够转发多个对象。
好玩的东西:利用oc消息转发,实现多重delegate代理;
你们都比较清楚,在一般状况下,delegate只能对象之间是一对一通讯的,经过上述的消息转发的分析,在转发的第(3)步,能够实现多重代理,即多个委托对象。我先上一段代码,后面再解读代码逻辑;
(1)多重代理的处理类,保存多个委托对象,经过消息转发将要执行委托函数转发至其余委托对象(记住本类不实现任何委托相关的逻辑);
#import "MultipleDelegateProxy.h" @interface MultipleDelegateProxy () @property (nonatomic,strong) NSPointerArray *weakRefTargets; @end @implementation MultipleDelegateProxy - (void)setDelegateTargets:(NSArray *)delegateTargets { self.weakRefTargets = [NSPointerArray weakObjectsPointerArray]; for (id delegate in delegateTargets) { [self.weakRefTargets addPointer:(__bridge void *)delegate]; } } - (BOOL)respondsToSelector:(SEL)aSelector{ if ([super respondsToSelector:aSelector]) { return YES; } for (id target in self.weakRefTargets) { if ([target respondsToSelector:aSelector]) { return YES; } } return NO; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{ NSMethodSignature *sig = [super methodSignatureForSelector:aSelector]; if (!sig) { for (id target in self.weakRefTargets) { if ((sig = [target methodSignatureForSelector:aSelector])) { break; } } } return sig; } //转发方法调用给全部delegate - (void)forwardInvocation:(NSInvocation *)anInvocation{ for (id target in self.weakRefTargets) { if ([target respondsToSelector:anInvocation.selector]) { [anInvocation invokeWithTarget:target]; } } } @end
(2)使用MultipleDelegateProxy,multipleDelegate做为scrollview的委托对象,multipleDelegate的delegateTargets添加了两个委托对象self和scrollDelegate,在scrollview滚动的过程当中会经过MultipleDelegateProxy类中重写的- (BOOL)respondsToSelector:(SEL)aSelector预先判断multipleDelegate是否实现了相应的委托方法(这里确定要返回yes的,否则就不会出现消息转发),找不到委托方法,最后将消息转发至self和scrollDelegate进行处理,实现多重委托。
@interface MemberCenterViewController () <UIScrollViewDelegate> { MultipleDelegateProxy *multipleDelegate; ScrollDelegate *scrollDelegate; } @property (nonatomic, strong) UIScrollView *scroll; @end @implementation MemberCenterViewController - (void)viewDidLoad { [super viewDidLoad]; self.scroll = [[UIScrollView alloc] initWithFrame:self.view.bounds]; self.scroll.backgroundColor = [UIColor lightGrayColor]; self.scroll.contentSize = CGSizeMake(375, 800); multipleDelegate = [MultipleDelegateProxy new]; scrollDelegate = [ScrollDelegate new]; multipleDelegate.delegateTargets = @[self,scrollDelegate]; _scroll.delegate = multipleDelegate; [self.view addSubview:_scroll]; } - (void)scrollViewDidScroll:(UIScrollView *)scrollView { NSLog(@"%@",self); } @end
在这里我解释一下为何要重写MultipleDelegateProxy类中的- (BOOL)respondsToSelector:(SEL)aSelector方法?缘由很简单,在设置scrollview的delegate属性后,会判断delegate是否实现了相应的委托方法,若没有实现的话,是不会执行这个委托方法(可选的)的;因此咱们须要重写,逻辑是经过真正会执行委托的两个委托对象去判断是否实现的委托方法。
总结
经过消息转发机制,你能够看到oc这门语言的独特之处,增长了不少灵活性。可使用它来作一些特殊值的判断处理,好比null值等,减小咱们在接收后台返回数据时返回null对咱们处理逻辑的影响。固然消息转发的做用不止如此,更多好玩的东西,等待着咱们去发掘。