第十更 block剖析

一、block有三种
1.1 NSGlobalBlock
静态区block,这是一种特殊的bloclk,因为不引用外部变量而存在。我们可以通过是否引用外部变量识别,未引用外部变量即为NSGlobalBlock。另外,作为静态区的对象,它的释放是有操作系统控制的。
类似函数,位于text段
1.2 NSStackBlock 栈区block,位于内存的栈区,一般作为函数的参数出现。
位于栈内存,函数返回后Block将无效
1.3NSMallocBlock 堆区block,位于内存的堆区,一般作为对象的property出现。
位于堆内存

{  
    //create a NSGlobalBlock  
    float (^sum)(float, float) = ^(float a, float b){  
   
        return a + b;  
    };  
   
    NSLog(@"block is %@", sum); //block is <__NSGlobalBlock__: 0x47d0>  
} 
{  
  
    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的引用计数
2.1 NSGlobalBlock:retain、copy、release操作都无效,
2.2 NSStackBlock:retain、release操作无效,必须注意的是,NSStackBlock在函数返回后,Block内存将被回收。即使retain也没用。支持copy,copy之后生成新的NSMallocBlock类型对象。容易犯的错误是[[mutableAarry addObject:stackBlock],(补:在arc中不用担心此问题,因为arc中会默认将实例化的block拷贝到堆上)在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock copy到堆上,然后加入数组:[mutableAarry addObject:[[stackBlock copy] autorelease]]。
2.3 NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;

三、基本数据类型
3.1、局部变量
局部自动变量,在Block中只读。Block定义时copy变量的值,在Block中作为常量使用,所以即使变量的值在Block外改变,也不影响他在Block中的值。

{  
  
    int base = 100;  
  
    long (^sum)(int, int) = ^ long (int a, int b) {  
  
        return base + a + b;  
  
    };  
  
    base = 0;  
  
    printf("%ld\n",sum(1,2));  
  
    // 这里输出是103,而不是3, 因为块内base为拷贝的常量 100  
  
}  

3.2、STATIC修饰符的全局变量
因为全局变量或静态变量在内存中的地址是固定的,Block在读取该变量值的时候是直接从其所在内存读出,获取到的是最新值,而不是在定义时copy的常量

{  
  
    static int base = 100;  
  
    long (^sum)(int, int) = ^ long (int a, int b) {  
  
        base++;  
  
        return base + a + b;  
  
    };  
  
    base = 0;  
  
    printf("%ld\n",sum(1,2));  
  
    // 这里输出是4,而不是103, 因为base被设置为了0  
  
    printf("%d\n", base);  
  
    // 这里输出1, 因为sum中将base++了  
  
}  

3.3、Block变量,被__block修饰的变量称作Block变量。 基本类型的Block变量等效于全局变量、或静态变量。
注:BLOCK被另一个BLOCK使用时,另一个BLOCK被COPY到堆上时,被使用的BLOCK也会被COPY。但作为参数的BLOCK是不会发生COPY的

综上所述,总结:没有引用外部变量的block是NSGlobalBlock,作为属性的话是NSStackBlock,而NSStackBlock使用copy或者赋值的话,程序会把block从栈上面拷贝到堆上面,block作为函数的参数传递的时候是以NSStackBlock形式传递的(假如用堆来传的话,可能在新的方法里面调用的时候,堆区已经释放了)。

你可能感兴趣的:(第十更 block剖析)