关于block(4)

关于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 *)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块的内存管理,就说这么多了,欢迎批评指正,欢迎转载,欢迎点赞,未完待续...

你可能感兴趣的:(关于block(4))