How to store Blocks in NSDictionary

Are you getting a crash trying to store your block in a NSDictionary instance?

From http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/_index.html:

Blocks “just work” when you pass blocks up the stack in ARC mode, such as in a return. You don’t have to call Block Copy any more. You still need to use [^{} copy] when passing “down” the stack into arrayWithObjects: and other methods that do a retain.

From http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html:

As an optimization, block storage starts out on the stack—just like blocks themselves do. If the block is copied using Block_copy (or in Objective-C when the block is sent a copy), variables are copied to the heap. Thus, the address of a __block variable can change over time.

This means that the block variables in your function are stored in the stack, and when the function returns, they get wiped out. If you’re using ARC, your crash is caused by block variables being over released. Calling copy on a block makes block variables stick around.

Here’s what it looks like:

        __weak id weakSelf = self;  // Weak block variable prevents retain cycle.
        void (^myBlock)() = ^() { 
            NSLog(@"Called from %@!", weakSelf);
        };
        
        // Copy the block so the block variable sticks around!
        NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:[myBlock copy], @"MyBlock", nil];    
Advertisements

The mysterious case of an unexpectedly changing NSString attribute

So I had a vanilla Core Data entity with a @property (nonatomic, retain) NSString *text property and a NSTextView *textView used to edit text that would be stored to this value. Initialization looked something like textView.string = model.text and saving looks like model.text = textView.string in textDidEndEditing:.

All good right? Wrong. The value wasn’t sticking. It was changed even though the model’s setter without ever being called. Can you guess why?

It took me wayy too long to debug, but finally… checking out NSText’s string getter method, we see:

For performance reasons, this method returns the current backing store of the text object. If you want to maintain a snapshot of this as you manipulate the text storage, you should make a copy of the appropriate substring.

Tracing NSLog(@"%@", [textView.string class]) we see it is actually an instance of NSBigMutableString. This means both the text editor and entity now hold a reference to the same mutable NSString. Basic CS101, duh! There are two possible fixes: Define the string property using copy vs retain, @property (nonatomic, copy) NSString *text or manually copy the string when setting the value like model.text = [textView.string copy]. See this Stack Overflow post for more info on when to use retain/copy on NSString.