关于block(4)
标签: iOS 技术
接上篇,我们继续探究block。
block的copy属性
研究到这里,想来对block的定义和基本用法也熟悉了,那么在实战中,我们经常会定义block属性,并用copy修饰,这是为啥?
- 在MRC时代,为了能够方便操作block属性,以及不至于造成循环引用,那么一般会把block属性copy到堆上,定义其为属性时,便用copy修饰。
- 在ARC时代,系统已经对block属性进行了一次copy操作,即从栈上copy到堆上,所以用strong修饰或者用copy修饰没什么区别,但一般情况下为了block属性的特点,还是用copy修饰。
代码
@property (nonatomic, copy) void (^myBlock)();
说明
上面是定义了一个变量名为myBlock的block属性,就从属性来看,跟定义一般属性略有不同,类型是void的block,变量为myBlock
block属性赋值
在很多情况下,既然定义了block属性,那么就应该给它赋值,即初始化,这个十分重要,只有当属性被赋值了,那么在回调或者用到时才会有用,看代码:
- (void)myBlockDemo5 {
int i = 5;
void (^aBlock)() = ^ {
NSLog(@"i = %d", i);
};
NSLog(@"%@", aBlock);
// 属性赋值
self.myBlock = aBlock;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%@", self.myBlock);
self.myBlock();
}
说明
第一个方法
-myBlockDemo5
,是给block属性赋值,第二个方法是重写系统手势触屏方法,目的是验证block属性是否有值
打印
2016-12-05 14:15:31.461 TCMyBlockDemo[7676:2190899] <NSMallocBlock: 0x60800005bea0>
2016-12-05 14:15:32.479 TCMyBlockDemo[7676:2190899] <NSMallocBlock: 0x60800005bea0>
2016-12-05 14:15:32.479 TCMyBlockDemo[7676:2190899] i = 5
说明
从上面打印来看,
aBlock
的地址和self.myBlock
的地址是完全一样的,而且调用self.myBlock()
也能够有打印,符合逻辑,如此便说明赋值正确。
block块内部的循环引用,造成内存泄露
在我们使用block作为属性,且经常去封装一段代码,亦或是在系统的block块里实现一个动画过程,都不可避免地会对一些视图去引用,即self.
,看代码:
@property (nonatomic, strong) UIImageView *imageView;
- (void)myBlockDemo6 {
self.imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
self.imageView.image = [UIImage imageNamed:@"image"];
self.imageView.alpha = 0;
[self.view addSubview:self.imageView];
self.myBlock = ^ {
// 报警!!!
self.imageView.alpha = 1.0;
self.imageView.frame = CGRectMake(100, 100, 100, 100);
};
}
说明
在给
self.myBlock
赋值时,当我们调用self.imageView
时,就会有黄色警告:Capturing 'self' strongly in this block is likely to lead to a retain cycle
,意思很明确,就是造成循环引用,会导致内存泄露,那么我们就必须解决。
解决办法
既然用self造成了循环引用,根据OC特性,那么一个对象若是没有了强引用就会被回收,所以我们必须打断循环,在OC中用
__weak
来修饰对象,那么它就是一个弱引用,如果想象不到,可以画一个循环引用图来帮助理解。
代码修改如下:
- (void)myBlockDemo6 {
self.imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
self.imageView.image = [UIImage imageNamed:@"image"];
self.imageView.alpha = 0;
[self.view addSubview:self.imageView];
// 用__weak修饰self,来打断循环引用
__weak typeof(self) weakSelf = self;
self.myBlock = ^ {
weakSelf.imageView.alpha = 1.0;
weakSelf.imageView.frame = CGRectMake(100, 100, 100, 100);
};
}
如此,由block属性引起的循环引用算是结束了,而对于
__weak
的使用,主要还是要看对内存管理中循环引用的理解程度,只要记住,并不是所有的block块中的self
都要用__weak
修饰,比如在系统提供的核心动画中
核心动画中
- (void)myBlockDemo7 {
self.imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
self.imageView.image = [UIImage imageNamed:@"image"];
self.imageView.alpha = 0;
[self.view addSubview:self.imageView];
[UIView animateWithDuration:1.0 animations:^{
self.imageView.alpha = 1.0;
self.imageView.frame = CGRectMake(100, 100, 100, 100);
} completion:^(BOOL finished) {
}];
}
能对比出不同吗?知道为什么吗?
写在文末
对于block块的内存管理,就说这么多了,欢迎批评指正,欢迎转载,欢迎点赞,未完待续...
网友评论