Normally blocks can be of 3 types: NSGlobalBlock, NSStackBlock, NSMallocBlock. Lets take the following example:
void (^aBlock)(NSString *someString) = ^(NSString *someString){
NSLog(@"Block was executed. %@", someString);
};
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:aBlock forKey:@"aBlock"];
Because aBlock doesn't capture surrounding scope if I do po dictionary I get
aBlock = <NSGlobalBlock:0x165dde60> and this is correct
If I then do a:
NSString *string = @"Test";
void (^aBlock)(NSString *someString) = ^(NSString *someString){
NSLog(@"Block was executed. %@ %@", someString, string);
};
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:aBlock forKey:@"aBlock"];
and then po dictionary, I get:
aBlock = <NSMallocBlock:0x165dde60> and this is what confuses me
Shouldn't this be a NSStackBlock and only become a NSMallocBlock when I do:
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:[aBlock copy] forKey:@"aBlock"];
I am on iOS 7.1 using ARC and as far as I know blocks should not be copied by default in ARC when passed down the stack and they should be copied only when passed up the stack (returning from a function).
What am I missing here?
The type of the block object in the dictionary was already NSMallocBlock on these lines, not from copied by NSDictionary +dictionaryWithObject:forKey: method.
void (^aBlock)(NSString *someString) = ^(NSString *someString){
NSLog(@"Block was executed. %@ %@", someString, string);
};
This aBlock variable is __strong by the default under ARC compilation environment.
__strong void (^aBlock)(NSString *someString) = ^(NSString *someString){
...
So the block object was retained by the aBlock variable. Actually, according to LLVM source code, the compiler emitted retain code for storing the object into __strong variable on the line.
- 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");
This objc_retainBlock is a runtime function in objc4.
http://opensource.apple.com/source/objc4/objc4-551.1/runtime/NSObject.mm
id objc_retainBlock(id x) {
return (id)_Block_copy(x);
}
Thus, the block object was copied from stack to heap by this _Block_copy.
In addition to this, you can see __NSStackBlock__ type for the block object using __weak.
__weak void (^aBlock)(NSString *someString) = ^(NSString *someString){
NSLog(@"Block was executed. %@ %@", someString, string);
};
In this case, the block object was not retained by the aBlock variable, and the block object is not an ordinary Objective-C object, so the block object can exist on stack. Yes, it is __NSStackBlock__ object. You may need to call copy or Block_copy for it ahead of storing into NSMutableDictionary.
来源:https://stackoverflow.com/questions/25794306/could-you-help-me-to-understand-block-types-when-added-to-containers-nsdictiona