NSProxy

2017-12-21  本文已影响33人  请叫我啊亮

作用一: 代理
想要拦截自定义CPTabBarController(UITabBarController子类)中tabbar的点击事件,最简单的就是直接设置自己为自己的代理self.delegate=self,再实现代理方法tabBarController:shouldSelectViewController。这样设置代理方法一看就不爽,寻求此类事件的解决方法。
NSProxy是专门为消息转发而设计的抽象类,稍微设计下就可以达到此目的。

@interface CPWeakProxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@end

#import "CPWeakProxy.h"
@implementation CPWeakProxy{
    __weak id _target;
}

- (instancetype)initWithTarget:(id)target {
    _target = target;
    return self;
}

+ (instancetype)proxyWithTarget:(id)target {
    return [[CPWeakProxy alloc] initWithTarget:target];
}

- (id)forwardingTargetForSelector:(SEL)selector {
    return _target;
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    return [_target respondsToSelector:aSelector];
}
@end

使用时在CPTabBarController的viewDidLoad方法中

    _proxy = [CPWeakProxy proxyWithTarget:self];
    self.delegate = _proxy;

这里必须将CPWeakProxy对象设置为控制器的属性强引用避免释放。
这里要注意的是CPWeakProxy对象必须实现respondsToSelector方法,因为UITabBarController的实现中肯定有诸如下面的代码

- (void)tabbarDidSelect{ // tabbarItem被点击时调用
    if (self.delegate && [self.delegate respondsToSelector:@selector(xxx)]) {
        [self.delegate xxx];
    }
}

CPWeakProxy是一个抽象类,respondsToSelector并未有默认实现,需要自行实现,否则会找不到方法崩溃。
tabbaritem点击时,调用代理CPWeakProxy的xxx方法,肯定找不到,触发消息转发进入CPWeakProxy的forwardingTargetForSelector方法,返回_target(就是我们自己写的CPTabBarController),调用回控制器中实现的代理方法,解决问题。

作用二:NSTimer的target
NSProxy还可以用来转发NSTimer的消息,避免NSTimer强引用控制器无法销毁。常规使用如下

 [NSTimer scheduledTimerWithTimeInterval:1 target:[CPWeakProxy proxyWithTarget:self] selector:@selector(xxx) userInfo:nil repeats:YES];

用上面方法后控制器能正常释放,但是程序崩溃了。。。

追查原因,是NSTimer会使CPWeakProxy对象无法释放,控制器销毁时没有销毁NSTimer,定时器继续回调CPWeakProxy的xxx方法,而消息转发方法forwardingTargetForSelector返回nil,转发失败程序崩溃。
结论,在控制器中NSTimer使用CPWeakProxy转发消息可以避免控制器无法销毁,但是控制器销毁时必须手动销毁定时器来保证CPWeakProxy对象正常释放。

设计的转发类继承自NSProxy而不是NSObject,是因为给NSProxy发送消息时只会在当前类中查找方法,一旦找不到就执行消息转发操作,相比NSObject少去了递归父类查找方法等流程,效率更高

上一篇 下一篇

猜你喜欢

热点阅读