美文网首页
关于block的认识

关于block的认识

作者: Carson_Zhu | 来源:发表于2018-02-02 16:11 被阅读3次

    前言

    block是一种比较特殊的数据类型。它可以保存一段代码,在合适的时候取出来调用。

    block的声明
    • 方式一
    typedef int(^myBlock)(int, int);
    
    @interface Person : NSObject
    @property (nonatomic, copy) myBlock myBlock;
    @end
    
    • 方式二
    @property (nonatomic, copy) void (^callback)(NSString *name);
    
    • 方式三
    - (void)func:(int (^)(int, int))callback;
    
    block的实现
    self.carson.callback = ^NSString *(NSString *name) {
        NSLog(@"%@", name);
    };
    
    block的作用域

    block需要注意的一个特性就是Variable Capturing,直译过来就是捕捉变量。block会将“捕捉”到的变量复制一份,然后对复制品进行操作。如果希望block作用域内可以修改外边的变量,可以使用__block

    __block int i = 0;
    self.carson.callback = ^NSString *(NSString *name) {
        NSLog(@"%@", name);
        i ++;
    };
    somePerson.callback(@"张三");
    NSLog(@"i == %d", i);
    

    打印结果:

    2018-02-02 15:53:13.386997+0800 Block[6754:448733] i == 1
    
    block的三种类型
    • _NSConcreteGlobalBlock(全局block)
      全局块存储在静态区(也叫全局区),相当于Objective-C中的单例。

    • _NSConcreteStackBlock (栈块)
      栈块存储在栈区,超出作用域则马上被销毁。

    • _NSConcreteMallocBlock(堆块)
      堆块存储在堆区中,是一个带引用计数的对象,需要自行管理其内存。

    怎么判断一个block所在的存储位置呢?

    • block不访问外界变量(包括栈中和堆中的变量)此时就为全局块
    • 访问外界变量的block默认存放在堆中,实际上是先放在栈区,在ARC情况下自动又拷贝到堆区,自动释放。如果不是ARC则存放在栈区,。
    • 使用copy修饰符的作用就是将block从栈区拷贝到堆区
    声明block属性的时候为什么用copy

    使用copy修饰符的作用就是将block从栈区拷贝到堆区,复制到堆区的主要目的就是保存block的状态,延长其生命周期。因为block如果在栈上的话,其所属的变量作用域结束,该block就被释放掉。

    不同类型的block使用copy方法的效果也不一样,如下所示:

    block的循环引用

    在使用block的时候,我们要特别注意循环引用的问题,先来看一个循环引用的例子:

    @interface Person : NSObject
    @property (nonatomic, copy) void (^callback)(NSString *name);
    @property (nonatomic, assign) NSInteger age;
    @end
    
    @interface ViewController ()
    @property (nonatomic, strong) Person *carson;
    @end
    
    self.carson.callback = ^void (NSString *name) {
        self.carson.age = 5;
    };
    

    在上面的代码中Person类声明了一个block属性,所以Person对象carsonblock有一个强引用。而在block内部又对carson进行了一次强引用,这样就形成了一个封闭的环,也就是我们经常说的强引用循环。由于其相互引用,内存不能够进行释放,就造成了内存泄漏的问题。

    __weak解决循环引用
    __weak typeof(self) weakSelf = self;
    self.carson.callback = ^void (NSString *name) {
        weakSelf.carson.age = 5;
    };
    

    但是在并发执行的时候,block的执行是可以抢占的,而且对weakSelf指针的调用时序不同可以导致不同的结果,比如在一个特定的时序下weakSelf可能会变成nil,这个时候在执行doAnotherThing就会造成程序的崩溃。为了避免出现这样的问题,采用__strong的方式来进行避免,更改后的代码如下:

    __weak typeof(self) weakSelf = self;
    self.carson.callback = ^void *(NSString *name) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        strongSelf.carson.age = 5;
    };
    

    总结:

    • self没有直接或间接强引用block,可以在block里面直接使用self,比如UIViewanimation动画block
    • self强引用对象方法里的block也可以直接使用self,因为没有强引用到block,所以不会造成循环引用。
    • 当和并发执行相关的时候,当涉及异步的服务的时候,block可以在之后被执行,并且不会发生关于self是否存在的问题。

    相关文章

      网友评论

          本文标题:关于block的认识

          本文链接:https://www.haomeiwen.com/subject/aklezxtx.html