根據Block在內存中的位置,Block可分為三種類型NSGlobalBlock,NSStackBlock,NSMallocBlock。
NSGlobalBlock
這種Block存儲在程序的數據區域(跟函數存儲在一起),一個沒有引用外部變量的Block即為NSGlobalBlock。對于NSGlobalBlock,我們不需要使用copy,即使copy,也不會copy到堆上!
NSStackBlock
如果Block中引用了外部變量,則該Block為NSStackBlock。
對于NSStackBlock,如果不做任何操作,隨棧自生自滅。一旦block被調用,則會被copy到堆上,變成NSMallocBlock。
請看如下代碼:
typedef void (^dBlock)();
dBlock example_getBlock() {
char d = 'aaa';
return ^{
printf("%c\n", d);
};
}
void example() {
example_getBlock()();
}
上面的代碼中,example_getBlock返回了一個NSStackBlock,正常情況下返回一個棧上的對象是會出錯的,但是在ARC下,返回的時候會將NSStackBlock拷貝到堆上,而且是autorelease類型的。example_getBlock返回堆上的NSMallocBlock,所以上面的代碼可以正常運行。
再看如下代碼:
- (NSArray*) getBlockArray
{
int num = 123;
return [[NSArray alloc] initWithObjects:
^{ NSLog(@"this is block 0:%i", num); },
^{ NSLog(@"this is block 1:%i", num); },
^{ NSLog(@"this is block 2:%i", num); },
nil];
}
- (void)test
{
NSArray* obj = [self getBlockArray];
void (^blockObject)(void);
blockObject = [obj objectAtIndex:1];
blockObject();
}
這段代碼中,getBlockArray返回一個NSStackBlock數組,而ARC并沒有對數組中的block做任何處理,getBlockArray函數結束后,NSArray中的三個block都將被釋放。所以在test函數中,blockObject指向一塊已經被釋放的棧內存,運行時程序會報錯。
NSMallocBlock
默認的block是存放在棧上的(NSStackBlock),對于一個NSStackBlock copy一下即可copy到堆上,變成NSMallocBlock。