iOS 开发中使用block的注意点
最近工作较忙,比较少写博客了,最近研究block的时候遇到一些小坑,就写出来避免大家走我的旧路,做一个帮大家填坑的填坑人,进入主题。
相信大家对使用block是又爱又恨,爱它给开发带来了多大的便利,但是又恨它是否循环引用掌握不够准确,导致经常出现循环引用的问题。对于新手来说,出现循环引用时,有时候通过Leaks不一定能检测出来,更重要的还是得靠自己有一双能看出哪里循坏引用的一双慧眼。
代码与分析:
我们先定义一个view,用于与Controller相互传值。当点击view的按钮时,就会通过block回调给controller,也就反馈到控制器了,并将对应的数据传给控制器以记录:
.h头文件
typedef void(^KTTestBlock)(id model);
@interface KTTestView : UIView
- (instancetype)initWithBlock:(KTTestBlock)block;
@end
.m文件
@interface KTTestView ()
@property (nonatomic, copy) KTTestBlock block; // 私有属性
@end
@implementation KTTestView
- (void)dealloc {
NSLog(@"dealloc: %@", [[self class] description]);
}
- (instancetype)initWithBlock:(KTTestBlock)block {
if (self = [super init]) {
self.block = block;
UIButton *button = {(
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setTitle:@"进行与controller的传值" forState:UIControlStateNormal];
btn = CGRectMake(50, 200, 200, 45);
btn.backgroundColor = [UIColor redColor];
[btn setTitleColor:[UIColor yellowColor] forState:UIControlStateNormal];
[btn addTarget:self action:@selector(feedback) forControlEvents:UIControlEventTouchUpInside];
return ban;
)}
}
return self;
}
- (void)feedback {
if (self.block) { // 需要判断block是否有值
// 传模型回去,这里没有数据,假设传nil
self.block(nil);
}
}
@end
接下来看KTTestViewController,有两个属性,在viewDidLoad时,创建了aView属性:
@interface KTTestViewController()
@property (nonatomic, strong) KTTestView *aView;
@property (nonatomic, strong) id currentModel;
@end
@implementation KTTestViewController
- (instancetype)initWithCallback:(KTCallbackBlock)callback {
if (self = [super init]) {
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"KTTestViewController";
self.view.backgroundColor = [UIColor whiteColor];
self.aView = [[KTTestView alloc] initWithBlock:^(id model) {
// 假设要更新model
self.currentModel = model;
}];
// 假设占满全屏
self.aView.frame = self.view.bounds;
[self.view addSubview:self.aView];
self.aView.backgroundColor = [UIColor whiteColor];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"进入控制器:%@", [[self class] description]);
}
- (void)dealloc {
NSLog(@"控制器被dealloc: %@", [[self class] description]);
}
@end
很明显,现在已经循环引用了
所形成的环有2个:
vc->aView->block->vc(self)
vc->aView->block->vc.currentModel
—-解决的办法可以是:在创建aView时,block内对currentModel的引用改成弱引用:
__weak __typeof(self) weakSelf = self;
self.aView = [[KTTestView alloc] initWithBlock:^(id model) {
// 假设要更新model
weakSelf.currentModel = model;
}];
我见过很多类似这样的代码,直接使用成员变量,而不是属性,然后他们以为这样就不会引用self,也就是控制器,从而不形成环:
self.aView = [[KTTestView alloc] initWithBlock:^(id model) {
// 假设要更新model
_currentModel = model;
}];
这是错误的理解,当我们引用了_currentModel时,它是控制器的成员变量,因此也就引用了控制器。要解决此问题,也是要改成弱引用:
__block __weak __typeof(_currentModel) weakModel = _currentModel;
self.aView = [[KTTestView alloc] initWithBlock:^(id model) {
// 假设要更新model
weakModel = model;
}];
千万要记得这里还要加上__block哦!
暂时就讲这么多吧,目的是教大家如何分析内存是否形成环,只要懂得了如何去分析内存是否循环引用了,那么在开发时一定会特别注意内存管理问题,而且查找内存相关的问题的bug时,也就少走弯路,填好坑。