我在 Objective-C中Block的類型 一文中說到MRC下有NSGlobalBlock、NSMallocBlock以及NSStackBlock三種類型的block,而ARC下是沒有NSStackBlock類型的,在這里對其進(jìn)行一個更正補(bǔ)充。
在RAC下,進(jìn)行如下測試:
CGFloat f = 1.1;
NSLog(@"%@", ^{NSLog(@"%lf",f);});
NSLog(@"%@",[^{NSLog(@"%lf",f);} copy]);
void(^deliveryBlock)(void) = ^{NSLog(@"%lf",f);};
NSLog(@"%@", deliveryBlock);
輸出日志為
2017-03-24 22:20:22.526 testdemo[48961:588668] <__NSStackBlock__: 0x7fff525a8c20>
2017-03-24 22:20:22.526 testdemo[48961:588668] <__NSMallocBlock__: 0x60000005e420>
2017-03-24 22:20:22.527 testdemo[48961:588668] <__NSMallocBlock__: 0x60000005e420>
我發(fā)現(xiàn),在直接打印block的時候,他的類型顯示的還是NSStackBlock。而我們將這個block進(jìn)行賦值之后,打印deliveryBlock的結(jié)果是NSMallocBlock類型。
也就是說,NSStackBlock類型在ARC下是存在,只是在對他進(jìn)行賦值的時候,編譯器將棧區(qū)的block拷貝到了堆區(qū)(賦值和copy在這里效果相同)。
至于原因
- (void)viewDidLoad {
[super viewDidLoad];
NSObject *obj = [[NSObject alloc]init];
void(^deliveryBlock)(void) = ^{
NSLog(@"%@",obj);
};
[self didBlock:deliveryBlock];
}
- (void)didBlock:(void(^)(void)) block {
NSLog(@"%@",block);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
block();
});
}
參照上方代碼,假如deliveryBlock是NSStackBlock類型,如果我們把deliveryBlock當(dāng)作方法參數(shù)傳遞到了另外一個方法,那么一旦deliveryBlock不在原來方法的調(diào)用棧,而新方法調(diào)用deliveryBlock的時機(jī)又不得而知,可能在新方法調(diào)用的時候deliveryBlock的時候堆區(qū)的obj已經(jīng)被釋放。即:ARC下編譯器在NSStackBlock類型的block傳遞過程中進(jìn)行了自動優(yōu)化。
為了進(jìn)一步驗證,我做了如下測試
- (void)viewDidLoad {
[super viewDidLoad];
[self test:^{
NSLog(@"%@",self.object);
}];
}
- (void)test:(void(^)(void))block {
NSLog(@"%@",block); //log: <__NSStackBlock__: 0x7fff5ef49b88>
}
當(dāng)object作為self的屬性時,object在整個vc的生命周期存在,故不會有脫離調(diào)用棧的問題,所以編譯器沒有將其自動拷貝到堆。