iOS 方法交换的另一种方式

88 阅读3分钟

方法的劫持?方法的拦截?总之就是差不多的意思.

在oc上想要监听某个方法是否调用,常用的操作就是Method Swizzling.这个方法使用起来很方便.但是还有没有别的方式可以获取这个方法呢?

有.通过消息的转发

消息转发的方式也有很多,1.直接通过方法解析然后转发,2.新建一个类,接受方法在转发出去,3 利用 NSProxy ..

我这边就是采用NSProxy

背景:

我们这有有个一高度封装的列表展示控件(后面叫它A).但是他提供给外界的方法只有几个如只需要 返回cell 以及给 cell 填充数据.但是有很多 tableView 的代理方法就没有传递到外面.那么我们需要获取到这个tableVie调用的代理方法需要怎么处理呢.

方案:

创建自定义的 Proxy我们称他 BProxy,外界需要知道代理方法的类为 C

整体结构先说一下,控制器 C 引用了 A 控件,A 控件里面实现了 tableView 的所有代理方法,但并没有将所有的方法往外接抛出来,那么现在的问额提

原本方式是 A.tableView.delegate = A 所有的代理都在 A 实现了

现在我们需要让A.tableView.delegate = BProxy 然后当 BProxy讲这些方法转发给 A 以及 C,而且还要不影响到 A 以前的逻辑

我们为 BProxy 添加两个属性major主要的Minor次要的,Minor 可以实现为数组到时候可以一对多进行转发

创建 BProxy 实例

BProxy *proxy = [[BProxy alloc] init];

A.tableView.delegate = proxy;

proxy.major = A;
proxy.minor = C;

C.proxy = proxy;

问题 1:

如果只是普通的转发那么到这里可能就结束了.但是这里需要转发的是 tableView 的代理方法.那这个就有点不一样.应为 tableView 在响应 delegate 的方法时会判断当前 delegate 是否已经实现这个代理conformsToProtocol.,所以在这个 Bproxy 中需要添加

问题 2:

这个地方tableView的delegate 是 weak 修饰的.我们需要对BProxy进行强引用 C.proxy = proxy

代码:

.h

@interface BProxy : NSProxy@property(nonatomic, weak) id major;@property(nonatomic, weak) id Minor;- (instancetype)init;@end

.m

@interface BProxy()<UITableViewDelegate, UITableViewDataSource>@end@implementation BProxy- (instancetype)init {    return self;}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {    return [self.major methodSignatureForSelector:sel];}
- (void)forwardInvocation:(NSInvocation *)invocation {    SEL selector = [invocation selector];    BOOL returenValueSet = NO;    void *returenValue = NULL;    if ([self.Minor respondsToSelector:selector]) {        [invocation invokeWithTarget:self.Minor];    }    if ([self.major respondsToSelector:selector]) {        [invocation invokeWithTarget:self.major];    }}@end

问题 2:

我们通过invocation将方法都抛出去了,那必然也会收到多个类处理后的返回值,我们需要做的是讲 major 处理的返回值保存将结果返回.如果没有返回的值的方法那会更好

所以我这边采去的是最后调用 major 中的方法

问题 2:

如果 major 没有实现某个代理方法,那许不要 minor 也能接收到呢? 这就存在两个问题.1 minor 需要收到,2minor 不需要接收到,那么这个看需求,也可以在添加个属性做过滤.如果需要接收到那么就引出第三个问题 3.minor 的返回需要需要进行处理,按道理应该不处理,但是 tableView 已经调用 delegate 的方法了 ,消息已经发送出去了,不处理又不太好

建议:如果 major 没有实现的方法,minor 也不需要接收到,

具体就需要看你自己需求了

有个注意点,tableView 调用 delegate 中有几个方法需要注意

它首先会调用

- (BOOL)conformsToProtocol:(Protocol *)aProtocol;

然后在调用

- (BOOL)respondsToSelector:(SEL)aSelector;

最后在进行调用 delegate 中实现的方法,这几个方法也会被invocation给转发出去

总结: 我感觉这个方法也是一个比较好用的方式来做方法的获取.当然它也有一定的局限性.这里面也有很多问题没有进行处理,如有更好的方法也可以一起讨论....