其实这个问题不需要说太多了,只需要抓住会造成循环引用的本质原因就可以了.
如果block没有直接或者间接被self存储,就不会产生循环引用。就不需要用weak self####
引发循环引用,是因为当前self在强引用着block,而block又引用着self,这样就造成了循环引用。而需不需要使用[weak self]就是由循环引用来决定,如果造成了循环引用,就必须使用[weak self]来打破循环.
1.直接强引用####
来分析一个自己设计的block模块:// 这种情况不必要弱引用 [self oneBlockSucess:^{ [self doSomething]; }]; //这种情况就有必需用weakself来打破引用环 self.secondBlock = ^{ [self doSomething]; };
self.secondBlock
说明当前self持有了secondBlock这个block属性,block属性是由copy来声明的,@property(nonatomic, copy)BtnSecondBlockBlock secondBlock;
属于对象self的强引用属性.
所以如果在block回调内想拿到self去做一些业务处理时,如果直接使用self,就会造成block持有了self,两者互相持有,造成循环引用.打破这个环,就要使用如typeof(self) __weak weakSelf = self;
的weakSelf方式;
2.间接强引用####
再来分析一个自己设计的block模块:
这是一个持有block的view: XXSubmitBottomView
typedef void(^BtnPressedBlock)(UIButton *btn);@interface XXSubmitBottomView : UIView@property(strong,nonatomic)UILabel *allPriceLab;@property(strong,nonatomic)UIButton *submittBtn;@property(nonatomic, weak)XXConfirmOrderController *currentVc;@property(nonatomic, weak)XXConfimOrderModel *model;@property(nonatomic, copy)BtnPressedBlock block; -(void)submittBtnPressed:(BtnPressedBlock)block;@end
这是一个持有bottomView属性的控制器: XXConfirmOrderController
@interface XXConfirmOrderController ()@property(nonatomic, strong) XXConfimOrderTableView *tableView;@property(nonatomic, strong) XXSubmitBottomView *bottomView;@property(nonatomic, strong) XXConfimOrderModel *confimModel;@end@implementation XXConfirmOrderController-(void)viewDidLoad{ [super viewDidLoad]; self.title = @"确认下单"; self.view.backgroundColor = DDCJ_Gray_Color; //UI [self.view addSubview:self.tableView]; [self.view addSubview:self.bottomView]; //Data [self loadData]; }
下面是self.bottomView的懒加载以及block的回调处理
-(XXSubmitBottomView *)bottomView{ if (!_bottomView) { _bottomView = [[XXSubmitBottomView alloc] initWithFrame:CGRectMake(0, self.view.height - 50, Width, 50)]; _bottomView.currentVc = self; #warning self.bottomView.block self间接持有了BtnPressedBlock 必须使用weak! WEAKSELF //ps: weakSelf的宏定义#define WEAKSELF typeof(self) __weak weakSelf = self; [_bottomView submittBtnPressed:^(UIButton *btn) { NSLog(@"do提交订单"); MBProgressHUD *hud = [MBProgressHUD showMessage:@"加载中..." toView:weakSelf.view]; NSMutableDictionary *dynamic = [NSMutableDictionary dictionary]; [dynamic setValue:weakSelf.confimModel.orderRemark forKey:@"orderRemark"]; if (weakSelf.agreementId) { [dynamic setValue:weakSelf.agreementId forKey:@"agreementId"]; } if (weakSelf.isShoppingCartEnter) { [dynamic setValue:@"0" forKey:@"orderOrigin"]; }else{ [dynamic setValue:@"1" forKey:@"orderOrigin"]; } [[APIClientFactory sharedManager] requestConfimOrderWithDynamicParams:dynamic success:^(NSMutableArray *dataArray) { [hud hideAnimated:YES]; [weakSelf handlePushControllerWithModelList:dataArray]; } failure:^(NSError *error) { [hud hideAnimated:YES]; [MBProgressHUD showError:error.userInfo[@"message"]]; }]; }]; } return _bottomView; }
这里的warning信息其实已经写的很清楚了,#warning self.bottomView.block self间接持有了BtnPressedBlock 必须使用weak!
此处的控制器self并没有直接持有block属性,但是却强引用了bottomView,bottomView强引用了block属性,这就造成了间接循环引用. block回调内必须使用[weak self]来打破这个循环,否则就会导致这个控制器self永远都不会被释放掉产生常驻内存。如果self.bottomView
与bottomView.block
有一环不是强引用,就不会产生循环引用问题,因为不会形成一个引用环. 如果一个应用程序里面你有很多循环引用,那么内存占用就会比较大,并且由于一些特殊操作,会产生一个控制器的N个对象实例常驻内存无法释放,造成大量的系统内存泄漏,这当然是谁都不想看到的结果.#####
打破这个环,就要使用如typeof(self) __weak weakSelf = self;
的weakSelf方式;
自己设计的block模块都可以在合适时机进行打断。打断的方式就是使用self的弱引用即可.
如果是对系统类加扩展方法导致的循环引用,那么需要找得到合适的时机打断,也是没问题的。
另外有个简单的方法可以绕过这个问题,如果self引用了一个block,block又需要调用self,可以
把self通过参数回传给block,这样就不会产生循环引用了。
block回传的self可以声明成id类型,这样使用的时候可以在入参声明具体self类型,避免显式类型转换,方便开发。
typedef void (^Block) (id selfRef); Block block = ^(XXX *selfRef){ };
这是一种比较巧妙一点的处理方式.
作者:inxx
链接:https://www.jianshu.com/p/fd9c67db2e9f