循环引用:
案例一:
A持有B
B持有A
如果A释放会发给B一个dealloc但是A要释放的前提是要B释放掉。这样就出现,就产生一个环。
例子:
self.name = "hello"
self.block = ^{
self.name="hello world"
}
解决:
self.name = "hello"
__weak typeof(self) weakSelf = self;
self.block = ^{
weakSelf.name="hello world"
}
另一种解决:
self.name = "hello"
//self -> block ->vc(nil)->self
__block ViewController *vc = self; //初始化一个临时变理的结构体
self.block = ^{
vc.name="hello world"
vc = nil;//这里自己手动释放掉,就打破循环
}
第三种解决:(这种性能最高)
self.name = "hello"
self.blockVc = ^(ViewController *vc){ //直接传参
vc.name="hello world"
}
self.blockVc(self)
上面只是一般解决:
但是遇到线程的时候 ,还是会有问题,要用strong-weak
self.name = "hello"
__weak typeof(self) weakSelf = self;
self.block = ^{
dispath_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2*NSEC_PER_SEC)),dispatch_getMain_queue(),^{
weakSelf.name="hello world" //这里weakself已经是nil了,所有会崩溃
});
}.
解决:
self.name = "hello"
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(self) strongSelf=weakSelf; //这里只是个临时变量,只是暂时保留了self引用,出括号外还是会销掉,但是对于线程 运行完才销掉。就达到我们的目的了。
dispath_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2*NSEC_PER_SEC)),dispatch_getMain_queue(),^{
strongSelf.name="hello world" //这里weakself已经是nil了,所有会崩溃
});
}.
案例二:
NStimer的循环引用
self.timer = [NSTimer scheduledTimerWithInterval:1 target:self selector:@selector(hello) userInfo:nil repeats:YES];
-(void)dealloc:{
[self.timer invation];
self.timer =nil;
}
在这里是释放不了,timer会添加runloop
self -> runloop -> timer -> self
解决方案:
在退出视图的时候 我先把timer销掉,下次退出这个控制器的时候 (dealloc时就不用管)这个timer了
-(void didMoveTParentViewController:(UIViewController *) parent{
if(parent == nil){
[self.timer invation];
self.timer =nil;
}
}
第二方案:
引入第三者的变量去解决(self.target)
self.timer = [NSTimer scheduledTimerWithInterval:1 target:self.target selector:@selector(hello) userInfo:nil repeats:YES];
-(void)dealloc:{
[self.timer invation];
self.timer =nil;
}
现在模型变成
self->runloop->timer->self.target(第三者)
target创建一个类(TimerWapper)
实现vc需要的方法selector传进来,然后去调用
-(instancetype) initwithTimeInterval:(NStimeInterval)ti target:(id)target selector:(SEL)aselector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo{
self.target = target;
self.selector = selector;
self.timer = [NSTimer scheduledTimeWithTimeInterval:ti target:self selector:@selector(action) userInfo:userInfo repeats:yesOrNo];
}
-(void)action{
if([self.target respondsToSelector:self.selector]){
[self.target performSelector:self.selector];
}
}
再写一个释放timer
-(void)timerInvalidate{ //这里可以手动释放
[self.timer invalidate];
self.timer = nil;
}
最后VC里改成
self -> timerWapper(这里相互持有,释放不了,但是我们留出timerInvalidate可以自己手动释放,在dealloc里释放掉,然后再释放掉TimerWapper)<-> timer <- runloop
self.target = [TimerWapper initwithTimeInterval:1 target:self selector:@selector(action) userInfo:nil repearts:YES];
-(void)dealloc:{
[self.target timerInvalidate]; //这里对timer释放,就不会持有timer了
}
第三种方案
利用timer的block方式,这种调用,系统自己优化过,就不会产生循环引用了
//在NSTimer里target就自己(NSTimer)这样就不会循环引用了,[https://www.jianshu.com/p/823ef4fb63bc](https://www.jianshu.com/p/823ef4fb63bc)
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer){
}];
第四种方案(同样适用于 通知的循环引用)
利用代理Proxy与第二种类似
把target调用变成自己的Proxy
Proxy里面不用重写方法,直接 调用runtime的消息转发
+(instancetype)proxyWithTransformObject:(id)object{
Proxy *proxy = [Proxy alloc];
proxy.object=object;
return proxy;
}
//消息转发出去
-(NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
return [self.object methodSignatureForSelector:sel];
}
-(void)forwardInvocation:(NSInvocation *)invocation{
[invocation invokeWithTarget:self.object];
}
最后VC改成
//self->timer<-runloop
// |
// proxy
self.proxy = [Proxy proxyWithTransformObject:self];
self.timer = [NSTimer scheduledTimerWithInterval:1 target:self.proxy selector:@selector(hello) userInfo:nil repeats:YES];
-(void)dealloc:{
[self.timer invation]; //这里断掉timer引用
self.timer =nil;
}
网友评论