Hor*_*hiv 2 memory-management objective-c nsdictionary objective-c-blocks automatic-ref-counting
通常块可以有3种类型:NSGlobalBlock,NSStackBlock,NSMallocBlock.让我们举个例子:
void (^aBlock)(NSString *someString) = ^(NSString *someString){
NSLog(@"Block was executed. %@", someString);
};
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:aBlock forKey:@"aBlock"];
Run Code Online (Sandbox Code Playgroud)
由于ABLOCK不捕获周围的范围,如果我做PO字典,我得到
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"];
Run Code Online (Sandbox Code Playgroud)
然后po字典,我得到:
aBlock = < NSMallocBlock:0x165dde60> 这就是我的困惑
这不应该是一个NSStackBlock,只有当我这样做时才成为NSMallocBlock:
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:[aBlock copy] forKey:@"aBlock"];
Run Code Online (Sandbox Code Playgroud)
我使用ARC在iOS 7.1上,据我所知,在向下传递堆栈时,不应该在ARC中默认复制块,并且只有在向上传递堆栈(从函数返回)时才应复制它们.
我在这里错过了什么?
字典中块对象的类型在这些行上已经是NSMallocBlock,而不是由NSDictionary + dictionaryWithObject:forKey:方法复制.
void (^aBlock)(NSString *someString) = ^(NSString *someString){
NSLog(@"Block was executed. %@ %@", someString, string);
};
Run Code Online (Sandbox Code Playgroud)
在ARC编译环境下,此aBlock变量是__strong默认值.
__strong void (^aBlock)(NSString *someString) = ^(NSString *someString){
...
Run Code Online (Sandbox Code Playgroud)
所以块对象由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");
Run Code Online (Sandbox Code Playgroud)
这个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);
}
Run Code Online (Sandbox Code Playgroud)
因此,通过此_Block_copy将块对象从堆栈复制到堆.
除此之外,您还可以使用__weak查看块对象的__NSStackBlock__类型.
__weak void (^aBlock)(NSString *someString) = ^(NSString *someString){
NSLog(@"Block was executed. %@ %@", someString, string);
};
Run Code Online (Sandbox Code Playgroud)
在这种情况下,aBlock变量不保留块对象,并且块对象不是普通的Objective-C对象,因此块对象可以存在于堆栈中.是的,它是__NSStackBlock__对象.在存储到NSMutableDictionary之前,您可能需要为它调用copy或Block_copy.