简单来说,就是在Objective-C中,使用对象进行方法调用是一个消息发送的过程(Objective-C采用“动态绑定机制”,所以所要调用的方法直到运行期才能确定)。
方法在调用时,系统会查看这个对象能否接收这个消息(查看这个类有没有这个方法,或者有没有实现这个方法。),如果不能并且只在不能的情况下,就会调用下面这几个方法,给你“补救”的机会,你可以先理解为几套防止程序crash的备选方案,我们就是利用这几个方案进行消息转发,注意一点,前一套方案实现后,后一套方法就不会执行。如果这几套方案你都没有做处理,那么程序就会报错crash
+ (BOOL)resolveInstanceMethod:(SEL)sel + (BOOL)resolveClassMethod:(SEL)sel
- (id)forwardingTargetForSelector:(SEL)aSelector
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector; - (void)forwardInvocation:(NSInvocation *)anInvocation;
`
具体的看下面这篇文章就可以了:
http://my.oschina.net/Jacedy/blog/625343
这是本文的重点,主要讲下消息转发机制在实际中的使用场景,发现网上关于这方面的讲解还是比较少。
我们自定义了一个SGScrollview,继承自系统的UITableView,然后把SGScrollview添加到ViewController上面。要求在SGScrollview内部监听scrollview的代理事件做一些处理,同时在viewcontroller上也要监听scrollview的代理事件做处理,保证他们不会冲突和覆盖。
如果按照传统的做法,我们把SGScrollview的代理设置为viewcontroller,那么这个时候scrollViewWillBeginDragging方法只会被viewcontroller监听到,而在SGScrollview内部的 scrollViewWillBeginDragging
方法就失效了。
反之如果在SGScrollview设置代理为自己,那么viewcontroller的代理方法监听也会失效。
我们无法同时在两个不同的类里面监听同一个父类(UIScrollview)的代理方法,为了解决这个问题,我们可以用runtime的消息转发机制,建立一个代理转发类,让它来负责把代理方法的消息发送给不同的消息接受者。
下面直接上代码
#import <Foundation/Foundation.h> @interface MessageInterceptor : NSObject @property (nonatomic, assign) id receiver; @property (nonatomic, assign) id middleMan; @end ==================================== #import "MessageInterceptor.h" @implementation MessageInterceptor - (id)forwardingTargetForSelector:(SEL)aSelector { if ([self.middleMan respondsToSelector:aSelector]) { return self.middleMan; } if ([self.receiver respondsToSelector:aSelector]) { return self.receiver; } return [super forwardingTargetForSelector:aSelector]; } - (BOOL)respondsToSelector:(SEL)aSelector { if ([self.middleMan respondsToSelector:aSelector]) { return YES; } if ([self.receiver respondsToSelector:aSelector]) { return YES; } return [super respondsToSelector:aSelector]; } @end
#import <UIKit/UIKit.h> #import "MessageInterceptor.h" @interface SGScrollview : UITableView<UITableViewDelegate> @property(nonatomic,strong)MessageInterceptor *delegate_interceptor; @end =================== #import "SGScrollview.h" @implementation SGScrollview -(instancetype)initWithFrame:(CGRect)frame{ if (self == [super initWithFrame:frame]) { self.delegate_interceptor = [[MessageInterceptor alloc] init]; self.delegate_interceptor.middleMan = self; [super setDelegate:(id)self.delegate_interceptor]; } return self; } -(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ NSLog(@"%s的方法被调用",__PRETTY_FUNCTION__); if ([self.delegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]) { [self.delegate scrollViewWillBeginDragging:scrollView]; } } - (id)delegate { return self.delegate_interceptor.receiver; } - (void)setDelegate:(id)newDelegate { [super setDelegate:nil]; self.delegate_interceptor.receiver = newDelegate; [super setDelegate:(id)self.delegate_interceptor]; } @end
#import "ViewController.h" #import <objc/message.h> #import "SGScrollview.h" @interface ViewController ()<UIScrollViewDelegate,UITableViewDataSource,UITableViewDelegate> @end @implementation ViewController -(void)viewDidLoad{ [super viewDidLoad]; SGScrollview *SV = [[SGScrollview alloc]initWithFrame:self.view.bounds]; SV.dataSource = self; SV.delegate =self; [self.view addSubview:SV]; [SV registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; } -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return 20; } -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; cell.textLabel.text = [NSString stringWithFormat:@"%zd",indexPath.row]; return cell; } -(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ NSLog(@"%s的方法被调用",__PRETTY_FUNCTION__); } @end
此时拖动tableview,输出如下:
2016-08-12 13:31:09.579 test1[4474:410866] -[SGScrollview scrollViewWillBeginDragging:]的方法被调用 2016-08-12 13:31:11.630 test1[4474:410866] -[ViewController scrollViewWillBeginDragging:]的方法被调用
可以看到两个类都可以监听到代理方法。
Demo下载地址