ARCはNSStackBlockを降りてどこに行ったの?
2072 ワード
私はObjective-CのBlockのタイプについて、MRCの下にNSGlobalBlock、NSMallocBlock、NSStackBlockの3種類のblockがありますが、ARCの下にはNSStackBlockのタイプはありません.
RACでは、次のテストを行います.
出力ログは
blockを直接印刷したとき、彼のタイプはNSStackBlockだったことに気づきました.このblockを割り当てた後、deliveryBlockを印刷した結果、NSMallocBlockタイプになりました.すなわち、NSStackBlockタイプはARCの下に存在するが、コンパイラはスタック領域のblockをスタック領域にコピーした(付与とcopyはここで効果は同じ).
原因について
上のコードを参照して、deliveryBlockがNSStackBlockタイプである場合、deliveryBlockをメソッドパラメータとして別のメソッドに渡すと、deliveryBlockが元のメソッドの呼び出しスタックに存在せず、新しいメソッドがdeliveryBlockを呼び出すタイミングが分からず、新しいメソッドが呼び出されたときにdeliveryBlockがスタック領域のobjが解放される可能性がある.すなわち、ARC下コンパイラは、NSStackBlockタイプのblock転送中に自動的に最適化される.
さらに検証するために、以下のテストを行いました.
objectがselfのプロパティである場合、objectはvc全体のライフサイクルに存在するため、呼び出しスタックから離れる問題はないため、コンパイラはスタックに自動的にコピーしません.
RACでは、次のテストを行います.
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__:>
2017-03-24 22:20:22.526 testdemo[48961:588668] <__nsmallocblock__:>
2017-03-24 22:20:22.527 testdemo[48961:588668] <__nsmallocblock__:>
blockを直接印刷したとき、彼のタイプはNSStackBlockだったことに気づきました.このblockを割り当てた後、deliveryBlockを印刷した結果、NSMallocBlockタイプになりました.すなわち、NSStackBlockタイプはARCの下に存在するが、コンパイラはスタック領域のblockをスタック領域にコピーした(付与と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をメソッドパラメータとして別のメソッドに渡すと、deliveryBlockが元のメソッドの呼び出しスタックに存在せず、新しいメソッドがdeliveryBlockを呼び出すタイミングが分からず、新しいメソッドが呼び出されたときにdeliveryBlockがスタック領域のobjが解放される可能性がある.すなわち、ARC下コンパイラは、NSStackBlockタイプのblock転送中に自動的に最適化される.
さらに検証するために、以下のテストを行いました.
- (void)viewDidLoad {
[super viewDidLoad];
[self test:^{
NSLog(@"%@",self.object);
}];
}
- (void)test:(void(^)(void))block {
NSLog(@"%@",block); //log: <__nsstackblock__:>
}
objectがselfのプロパティである場合、objectはvc全体のライフサイクルに存在するため、呼び出しスタックから離れる問題はないため、コンパイラはスタックに自動的にコピーしません.