iOS開發中經常會使用block結合gcd來完成多線程編程,block也屬於對象,主要有三種類型:
1、 _NSConcreteStackBlock ,存儲在棧上;
2、_NSConcreteGlobalBlock,存儲在程序的數據區域(text段);
3、_NSConcreteMallocBlock,存儲在堆上。
下面分別介紹一下這三種類型的block,block在arc和非arc的模式下會有些需要注意的地方:
NSGlobalBlock:在block內部沒有引用任何外部變量
void (^globalBlock) () = ^ () {
NSLog(@"global block");
};
NSLog(@"%@", globalBlock);//<__NSGlobalBlock__: 0x1096e20c0>
對NSGlobalBlock的retain、copy、release操作都無效。
NSStackBlock:在block內部引用外部變量
先討論下MRC模式:
int base = 100;
long (^stackBlock) (int, int) = ^ long (int a, int b) {
return base +a + b;
};
NSLog(@"%@",stackBlock);//<__NSStackBlock__: 0x7fff57c6bce0>
棧block在當函數退出的時候,該空間就會被回收,因此如果再調用該block會導致crash:
void example_addBlockToArray(NSMutableArray *array) {
char b = 'B';
[array addObject:^{
printf("%cn", b);
}];
}
void example() {
NSMutableArray *array = [NSMutableArray array];
example_addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
block();
}
在example_addBlockToArray函數中添加的block由於為棧block,因此在example函數中調用的話會導致程序crash掉,可以通過將block拷貝到堆上來解決這個問題:
[array addObject:[[^{
printf("%cn", b);
} copy autorelease]]];
retain、release這種類型的block不起作用。下面看看在ARC模式下會有啥不同的地方:
int base = 100;
long (^stackBlock) (int, int) = ^ long (int a, int b) {
return base +a + b;
};
NSLog(@"%@",stackBlock);//<__NSMallocBlock__: 0x7f8da961e590>
我們發現在ARC模式下,打印出來的結果並不是NSStackBlock這個類型,很多人以為在ARC模式下block的類型只有NSGlobalBlock和NSMallocBlock兩種,其實這種觀點是錯誤的。在ARC情況下,生成的block也是NSStackBlock,只是當賦值給strong對象時,系統會主動對其進行copy:
int i=0;
NSLog(@"%@", ^{
NSLog(@"stack block here, i=%d", i);
});//<__NSStackBlock__: 0x7fff592eacf8>
void (^block)()=^{
NSLog(@"stack block here, i=%d", i);
};
NSLog(@"%@",block);//<__NSMallocBlock__: 0x7fae49e02660>
NSMallocBlock
如果NSStackBlock需要在其作用域外部使用的時候,在MRC的模式下需要手動將其copy到堆上,NSMallocBlock支持retain、release,會對其引用計數+1或-1,copy不會生成新的對象,只是增加了一次引用,類似retain;而在ARC模式下會自動對其進行copy,不需要自己手動去管理,盡可能使用ARC。