对iOS中block的一点领悟

看之前可以先做一下这个测试题目,如果都不能难倒你,那么下面的你也不用看了。

在Objective-C语言中,一共有3种类型的block:

  1. _NSConcreteGlobalBlock 全局的静态block,不会访问任何外部变量。
  2. _NSConcreteStackBlock 保存在栈中的block,当函数返回时会被销毁。
  3. _NSConcreteMallocBlock 保存在堆中的block,当引用计数为0时会被销毁。
一、全局静态的block和C函数是一样的,位于代码段中,其特征就是么有引用任何外部变量
{
    //create a NSGlobalBlock
    float (^sum)(float, float) = ^(float a, float b){
 
        return a + b;
    };
 
    NSLog(@"block is %@", sum); //block is <__NSGlobalBlock__: 0x47d0>
}

二、保存在栈中的block,block函数返回时被销毁,其引用的外部变量会被copy到栈上去,所以在这一类型block中引用了外部变量是无法改变外部变量的,当然我们可以使用__block来修饰你想要改变的外部变量以告知编译器“我需要在block内部修改这个外部变量”,这样编译器就会在block内部直接使用该变量的引用而不是再copy一份。
{
    NSArray *testArr = @[@"1", @"2"];
 
    void (^TestBlock)(void) = ^{
 
        NSLog(@"testArr :%@", testArr);
    };
 
    NSLog(@"block is %@", ^{
 
        NSLog(@"test Arr :%@", testArr);
    });
    //block is <__NSStackBlock__: 0xbfffdac0>
    //打印可看出block是一个 NSStackBlock, 即在栈上, 当函数返回时block将无效
 
    NSLog(@"block is %@", TestBlock);
    //block is <__NSMallocBlock__: 0x75425a0>
    //上面这句在非arc中打印是 NSStackBlock, 但是在arc中就是NSMallocBlock
    //即在arc中默认会将block从栈复制到堆上,而在非arc中,则需要手动copy.
}

此外保存在栈中的block对于arc和非arc来说有一点的区别,arc中默认会将栈中的block拷贝到堆中来(其原因本也不是很清楚,但是从下面提到的一些参考资料中很可能是因为arc中默认是strong的引用类型,那么将默认把block拷贝到堆上由arc来管理其内存),来看下面一个例子:
void exampleB_addBlockToArray(NSMutableArray *array) {
  char b = 'B';
  [array addObject:^{
    printf("%c\n", b);
  }];
}
 
void exampleB() {
  NSMutableArray *array = [NSMutableArray array];
  exampleB_addBlockToArray(array);
  void (^block)() = [array objectAtIndex:0];
  block();
}
由于该block引用了外面变量“b”,所以这个block的应该是保存在栈上的。在来看看它在arc和非arc中区别:
首先在非arc中,它是一个栈block所以它会在函数返回是销毁,那么在addObject的时候,此block已经被销毁了;
在arc中,它会默认被copy到堆中来成为一个堆block,函数返回不会立即被销毁,将由arc来管理其内存,所以以上代码是正确可行的。

三、保存在堆的block,我们可以通过copy栈block来得到堆block,C系列的程序员应该都知道堆上的内存是由我们自己管理的,所以在非arc中需要自己管理其内存,而在arc中我们只需要交给arc去管理就好了。

此外使用block有一点是需要特别注意的就是防止循环引用:
@property (nonatomic, copy)TestBlock block;

 self.block = ^{
        //arc中会编译会发出有循环饮用风险的警告
        [self doSomething];
    };

    self.block();

如上:self持有myblock,在block中又引用了self,block在被copy到堆上的时候会retain其引用的外部变量,这样myblock也持有了self,他们相互持有就造成了循环引用,谁都无法释放。
arc中正确的做法是:
    __weak testBViewController* weakSelf = self;

    self.block = ^{
        //arc中会编译会发出有循环饮用风险的警告
        [weakSelf doSomething];
    };

    self.block();

非arc中正确的做法是:
    self.block = ^{
        //arc中会编译会发出有循环饮用风险的警告
        [weakSelf doSomething];
    };

    self.block();
    self.block = nil;
当然在arc中也可以加上“self.block = nil”这句来达到释放block的目的,但是这样的做法是不推荐的。


以上领悟主要参考了一下blog

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值