Crash storing block in mutable array.

Discussion in 'Mac Programming' started by tronthomas, Feb 18, 2012.

  1. macrumors newbie

    Joined:
    Sep 21, 2008
    #1
    What can people tell me about why this program is crashing:

    Code:
    #import <Foundation/Foundation.h>
    
    @interface Action : NSObject 
    + (void)execute;
    @end
    
    @implementation Action
    + (void)execute
    {
    	NSLog(@"The action was executed.");
    }
    @end
    
    @interface ActionPerformer : NSObject {
    @private
    	NSMutableDictionary* _namedActions;
    }
    
    + (ActionPerformer*) performer;
    - (void)addAction:(NSString*)action withClass:(Class)class andSelector:(SEL)selector;
    - (void)performAction:(NSString*)action;
    @end
    
    @implementation ActionPerformer
    
    typedef void(^ActionBlock)();
    
    + (ActionPerformer*) performer
    {
    	ActionPerformer* performer = [[ActionPerformer alloc] init];
    	performer->_namedActions = [NSMutableDictionary dictionary];
    	return performer;
    }
    
    - (void)addAction:(NSString*)actionName withClass:(Class)class andSelector:(SEL)selector
    {
    	ActionBlock block = ^(){ [class performSelector:selector]; };
    	// Verify things work
    	block();
    	[_namedActions setObject:block forKey:actionName];
    }
    
    - (void)performAction:(NSString*)actionName
    {
    	ActionBlock block = [_namedActions objectForKey:actionName];
    	// Crash happens here!!!
    	block();
    }
    
    @end
    
    int main()
    {
    	@autoreleasepool {
    		ActionPerformer* performer = [ActionPerformer performer];
    		
    		[performer addAction:@"action" withClass:[Action class]
    			andSelector:@selector(execute)];
    			
    		[performer performAction:@"action"];	    
    	}
        return 0;
    }
    
    I am building it on Mac OS X 10.6.8 using Xcode 4.2 with Apple LLVM compiler 3.0
     
  2. macrumors 603

    Joined:
    Aug 9, 2009
    #2
    Since you didn't post the crash log or stack trace, I'm not going to bother guessing.

    Instead, I'm just going to say "Use NSInvocation". Seriously, trying to do what you're doing with blocks is silly. Especially when NSInvocation already exists and is well-tested (hint: it's instrumental in Distributed Objects).

    And I'm also going to suggest NOT using class-methods. Use instance methods, even if it means targeting an instance of the Action class instead of targeting the Action class itself. So:
    Code:
    @interface Action : NSObject 
    - (void)execute;
    @end
    
    @implementation Action
    - (void)execute
    {
    	NSLog(@"The action was executed.");
    }
    @end
    
     
  3. macrumors 6502

    Joined:
    Jul 25, 2006
    #3
    You need to explicitly copy the block to allow it to live beyond the scope it's declared in.

    Blocks live on the stack by default, unlike every other object that starts off in the heap. Adding a stack-based block to an array will call -retain on it, sure, but that doesn't actually have any effect on its lifetime. The only way to get the block to the heap is to -copy it. Once there, it will respond properly to -retain and -release. (don't forget to balance the -copy too!)
     

Share This Page