Obj-C __block saves the behavior of a variable

I ran into some strange problem when trying to access the __block variable (mutable block) from outside the block in which it was changed. This is a very toy example that I use to better understand the blocks as a whole, but I currently have a controller with this method that creates an NSDictionary content NSDictionary that uses NSDictionary enumerateKeysAndObjectsUsingBlock:

 - (NSString*) contentsOfDictionary:(NSDictionary*)dictionary { __block NSString *content = @""; [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){ NSString* contentToAppend = [NSString stringWithFormat:@"Object:%@ for key:%@\n", obj, key]; content = [content stringByAppendingString:contentToAppend]; NSLog(@"Content in block:\n%@", content); }]; NSLog(@"Content out of block:\n%@", content); return content; } 

When I run this method with a dictionary containing the contents:

 Value Key "Queen" "card" "Hearts" "suit" "10" "value" 

The content variable changes correctly inside the block, and I get this output with each iteration:

... Content in a block:

 Object:Queen for key:card 

... Content in a block:

 Object:Queen for key:card Object:Hearts for key:suit 

... Content in a block:

 Object:Queen for key:card Object:Hearts for key:suit Object:10 for key:value 

As soon as the code exits the block, although access to the content line calls EXC_BAD_ACCESS and at the same time it starts, it seems that it printed some garbage memory (cannot play) ...

What causes the release of this variable earlier? I got the impression that the definition of __block means that it is saved when used in a block and freed when it leaves the block. But the variable is saved and auto-implemented to start with the fact that it is a string literal, so I expect it to not be canceled until this method exits as early as possible.

+8
memory-management objective-c objective-c-blocks
source share
2 answers

It's your problem:

 content = [content stringByAppendingString:contentToAppend]; 

-stringByAppendingString: returns a new, auto-implemented object. The address of this object is stored in content . Everyone goes through this (implicit) loop, i.e. Each call to the provided block creates an entirely new object, and then assigns the address of this new content object. None of these objects survive its containing pool of auto resources.

What you should do is use NSMutableString and directly attach contentToAppend to the mutable string. For example:

 - (NSString*) contentsOfDictionary:(NSDictionary*)dictionary { NSMutableString *content = [NSMutableString string]; [dictionary enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop){ NSString* contentToAppend = [NSString stringWithFormat: @"Object:%@ for key:%@\n", obj, key]; [content appendString:contentToAppend]; NSLog(@"Content in block:\n%@", content); }]; NSLog(@"Content out of block:\n%@", content); return content; } 

Note that __block no longer required as you do not assign content anywhere in the block.

+15
source share

Internally, -enumerateKeysAndObjectsUsingBlock: uses a resource pool. Objects with the __block region __block not saved beyond the end of the block's life cycle, so you get an object created in the block region, which is then freed up when the dictionary autoresist pool merges, which happens before you try to print the content value.

+5
source share

All Articles