一、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形式传递的(假如用堆来传的话,可能在新的方法里面调用的时候,堆区已经释放了)。
网友评论