在使用NSTimer或者CADisplayLink的的时候,如果我们把当前的控制器作为target传入进入的话,那么他会在runloop中被持有,无法释放。
self.timer = [NSTimer timerWithTimeInterval:1 target:pro selector:@selector(timeAction) userInfo:nil repeats:true];
这中问题最好的解决方案应该就是使用NSProxy,来打破循环引用。
- NSProxy是一个实现了NSObject协议的根类。
苹果的官方文档是这样描述它的:
NSProxy 是一个抽象基类,它为一些表现的像是其它对象替身或者并不存在的对象定义API。一般的,发送给代理的消息被转发给一个真实的对象或者代理本身引起加载(或者将本身转换成)一个真实的对象。NSProxy的基类可以被用来透明的转发消息或者耗费巨大的对象的lazy 初始化。
NSProxy实现了包括NSObject协议在内基类所需的基础方法,但是作为一个抽象的基类并没有提供初始化的方法。它接收到任何自己没有定义的方法他都会产生一个异常,所以一个实际的子类必须提供一个初始化方法或者创建方法,并且重载forwardInvocation:方法和methodSignatureForSelector:方法来处理自己没有实现的消息。一个子类的forwardInvocation:实现应该采取所有措施来处理invocation,比如转发网络消息,或者加载一个真实的对象,并把invocation转发给他。methodSignatureForSelector:需要为给定消息提供参数类型信息,子类的实现应该有能力决定他应该转发消息的参数类型,并构造相对应的NSMethodSignature对象。详细信息可以查看NSDistantObject, NSInvocation, and NSMethodSignature的类型说明。
相信看了这些描述我们应该能对NSProxy有个初步印象,它仅仅是个转发消息的场所,至于如何转发,取决于派生类到底如何实现的。比如我们可以在内部hold住(或创建)一个对象,然后把消息转发给该对象。那我们就可以在转发的过程中做些手脚了。甚至也可以不去创建这些对象,去做任何你想做的事情,但是必须要实现他的forwardInvocation:和methodSignatureForSelector:方法。
@implementation WXProxy
-(instancetype)initTarget:(id)target{
_target = target;
return self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
NSLog(@"%s",__func__);
// return [NSObject instanceMethodSignatureForSelector:sel];
return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}
- (id)forwardingTargetForSelector:(SEL)selector {
NSLog(@"%s",__func__);
return _target;
}
-(void)forwardInvocation:(NSInvocation *)invocation{
NSLog(@"%s",__func__);
// invocation.target = nil;
// [invocation invoke];
void *null = NULL;
[invocation setReturnValue:&null];
}
-(void)dealloc{
NSLog(@"%s",__FUNCTION__);
}
self.timer = [NSTimer timerWithTimeInterval:1 target:pro selector:@selector(timeAction) userInfo:nil repeats:true];
网友评论