通常,块可以是3种类型:NSGlobalBlock,NSStackBlock,NSMallocBlock。让我们看下面的例子:
void (^aBlock)(NSString *someString) = ^(NSString *someString){
NSLog(@"Block was executed. %@", someString);
};
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:aBlock forKey:@"aBlock"];
因为如果我执行 po dictionary ,aBlock不会捕获周围的范围,我得到
aBlock = < NSGlobalBlock :0x165dde60>,这是正确的
如果我那么做:
NSString *string = @"Test";
void (^aBlock)(NSString *someString) = ^(NSString *someString){
NSLog(@"Block was executed. %@ %@", someString, string);
};
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:aBlock forKey:@"aBlock"];
然后 po dictionary ,我得到:
aBlock = < NSMallocBlock :0x165dde60> ,这让我感到困惑
这不应该是 NSStackBlock ,并且在执行以下操作时仅成为 NSMallocBlock :
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:[aBlock copy] forKey:@"aBlock"];
我在使用ARC的iOS 7.1上,据我所知,在向下传递到堆栈时,默认情况下不应在ARC中复制块,而应该仅在向上传递到堆栈(从函数返回)时才复制它们。
我在这里想念什么?
最佳答案
在这些行中,字典中的块对象的类型已经是NSMallocBlock,而不是通过NSDictionary + dictionaryWithObject:forKey:方法复制的。
void (^aBlock)(NSString *someString) = ^(NSString *someString){
NSLog(@"Block was executed. %@ %@", someString, string);
};
在ARC编译环境下,默认情况下,此aBlock变量为__strong。
__strong void (^aBlock)(NSString *someString) = ^(NSString *someString){
...
因此,块对象由aBlock变量保留。实际上,根据LLVM源代码,编译器发出了保留代码,用于将对象存储到该行的__strong变量中。
EmitARCRetainBlock:
llvm::Value *CodeGenFunction::EmitARCRetainBlock(llvm::Value *value, bool mandatory) {
llvm::Value *result = emitARCValueOperation(*this, value,
CGM.getARCEntrypoints().objc_retainBlock, "objc_retainBlock");
此objc_retainBlock是objc4中的运行时函数。
http://opensource.apple.com/source/objc4/objc4-551.1/runtime/NSObject.mm
id objc_retainBlock(id x) {
return (id)_Block_copy(x);
}
因此,此_Block_copy将块对象从堆栈复制到堆。
除此之外,您还可以使用 __weak 看到块对象的 __NSStackBlock__ 类型。
__weak void (^aBlock)(NSString *someString) = ^(NSString *someString){
NSLog(@"Block was executed. %@ %@", someString, string);
};
在这种情况下,aBlock变量不会保留该块对象,并且该块对象不是普通的Objective-C对象,因此该块对象可以存在于堆栈中。是的,它是 __NSStackBlock__ 对象。在将其存储到NSMutableDictionary中之前,可能需要调用复制或 Block_copy 。