通常 block 可以是 3 种类型:NSGlobalBlock、NSStackBlock、NSMallocBlock。让我们看下面的例子:
void (^aBlock)(NSString *someString) = ^(NSString *someString){
NSLog(@"Block was executed. %@", someString);
};
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:aBlock forKey:@"aBlock"];
因为如果我执行 po dictionary 我得到 p>,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字典,我得到:
aBlock = <NSMallocBlock:0x165dde60> 这就是让我困惑的地方
这不应该是一个 NSStackBlock 并且只在我这样做时才变成一个 NSMallocBlock:
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:[aBlock copy] forKey:@"aBlock"];
我在 iOS 7.1 上使用 ARC,据我所知,当向下传递堆栈时,ARC 中的 block 默认情况下不应该被复制,只有当向上传递堆栈(从函数返回)时,它们才应该被复制。
我在这里错过了什么?
最佳答案
字典中 block 对象的类型在这些行上已经是 NSMallocBlock,而不是通过 NSDictionary +dictionaryWithObject:forKey: 方法复制的。
void (^aBlock)(NSString *someString) = ^(NSString *someString){
NSLog(@"Block was executed. %@ %@", someString, string);
};
这个aBlock变量在ARC编译环境下默认是__strong
__strong void (^aBlock)(NSString *someString) = ^(NSString *someString){
...
所以block对象被aBlock变量保留了下来。实际上,根据 LLVM 源代码,编译器发出了用于将对象存储到 __strong 变量中的 retain 代码。
- https://github.com/llvm-mirror/clang/blob/master/lib/CodeGen/CGObjC.cpp#L2091
- https://github.com/llvm-mirror/clang/blob/master/lib/CodeGen/CGObjC.cpp#L2109
- https://github.com/llvm-mirror/clang/blob/master/lib/CodeGen/CGObjC.cpp#L1920
- https://github.com/llvm-mirror/clang/blob/master/lib/CodeGen/CGObjC.cpp#L1944
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 对象被这个_Block_copy从栈复制到堆。
除此之外,您还可以使用 __weak 查看 block 对象的 __NSStackBlock__ 类型。
__weak void (^aBlock)(NSString *someString) = ^(NSString *someString){
NSLog(@"Block was executed. %@ %@", someString, string);
};
在这种情况下, block 对象并没有被aBlock变量保留, block 对象也不是普通的Objective-C对象,所以 block 对象可以存在于栈中。是的,它是 __NSStackBlock__ 对象。在存储到 NSMutableDictionary 之前,您可能需要调用 copy 或 Block_copy。
关于objective-c - 你能帮我理解添加到容器(NSDictionary、NSArray)时的 block 类型吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25794306/