-
常见的循环引用
如下代码所示,EOCClassA实例的other属性设置成某个EOCClassB 实例,而EOCClassB实例的other属性设置成某个EOCClassA 实例,保留环会导致内存泄漏,解决方法就是将某一个引用设置为weak(即非拥有关系)。
#import <Foundation/Foundation.h>
@class EOCClassA;
@class EOCClassB;
@interface EOCClassA : NSObject
@property (nonatomic, strong) EOCClassB *other;
@end
@interface EOCClassB : NSObject
@property (nonatomic, strong) EOCClassA *other;
@end
-
代理属性设置为weak
协议:用来指定代理双方可以做什么,必须做什么。
代理:根据指定的协议,完成委托方需要实现的功能。
委托:根据指定的协议,指定代理去完成什么功能。
代理的本质就是代理对象内存的传递和操作,我们在委托类设置代理对象后,实际上只是用一个id类型的指针将代理对象进行了一个弱引用。委托方让代理方执行操作,实际上是在委托类中向这个id类型指针指向的对象发送消息,而这个id类型指针指向的对象,就是代理对象。
实委托方的代理属性本质上就是代理对象自身,设置委托代理就是代理属性指针指向代理对象,相当于代理对象只是在委托方中调用自己的方法,如果方法没有实现就会导致崩溃。
举例:在tableViewController中,控制器的view就是tableView,这就相当于tableViewController强引用着tableView,然后当我们设置delegate的时候,一般都是让tableViewController成为代理,这个时候代理如果也使用strong,那么tableView的delegate又强引用着tableViewController,所以导致循环引用,因此代理得用weak。
-
block中为避免循环引用,常见写法
__weak typeof(self) weakSelf = self;
[self doSomeBlockJob:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
...
}
}];
-
什么时候在 block 里面用 self,不需要使用 weak self
当 block 本身不被 self 持有,而被别的对象持有,同时不产生循环引用的时候,就不需要使用 weak self 了。如: UIView动画block不会造成循环引用是因为这是类方法,不可能强引用一个类,所以不会造成循环引用。
[UIView animateWithDuration:0.2 animations:^{
self.alpha = 1;
}];
-
为什么 block 里面还需要写一个 strong self,不写会怎么样
在 block 中先写一个 strong self,其实是为了避免在 block 的执行过程中,突然出现 self 被释放的尴尬情况。通常情况下,如果不这么做的话,很容易出现一些奇怪的逻辑,甚至闪退。以 AFNetworking 中 AFNetworkReachabilityManager.m
的一段代码举例:
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
如果没有 strongSelf 的那行代码,那么后面的每一行代码执行时,self 都可能被释放掉了,这样很可能造成逻辑异常。特别是当我们正在执行 strongSelf.networkReachabilityStatusBlock(status);
这个 block 闭包时,如果这个 block 执行到一半时 self 释放,那么多半情况下会 Crash。
-
有没有这样一个需求场景,block会产生循环引用,但是业务又需要你不能使用 weak self
场景:在网络请求API会持有回调的block,回调的block会持有self,而self也持有网络请求API的话,我们就构造了一个循环引用。虽然我们构造出了循环引用,但是因为网络请求结束时,网络请求API会主动释放对block的持有,因为整个循环链条被解开,循环应用就被打破了,所以不会存在内存泄露问题。
- (void)clearCompletionBlock{
//nil out to break the retain cycle
self.successCompletionBlock = nil;
self.failureCompletionBlock = nil;
}
解决思路:
1、事前避免,我们在会产生循环引用的地方使用 weak 弱引用,以避免产生循环引用。
2、事后补救,我们明确知道会存在循环引用,但是我们在合理的位置主动断开环中的一个引用,使得对象得以回收。