Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Executing Blocks From NSArray?

I was just thinking, as you can treat Blocks like objects if I create two of them and then add them to an NSArray is there a way to execute them from the array?

int (^Block_001)(void) = ^{ return 101; }; int (^Block_002)(void) = ^{ return 202; }; NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil]; 

EDIT: Update for clarity Per @davedelong's excellent answer

int (^Block_001)(void) = [^{ return 101; } copy]; int (^Block_002)(void) = [^{ return 202; } copy]; NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil];  [Block_001 release]; [Block_002 release]; 
like image 645
fuzzygoat Avatar asked Jul 15 '10 16:07

fuzzygoat


2 Answers

@KennyTM and @David are correct, but your code is potentially wrong. Here's why:

When creating an NSArray with objects, it will retain the objects put into it. In the case of blocks, it's using the Block_retain function. This means that the array has retained the blocks that you created, but that live on the stack (blocks are one of the very rare examples of Objective-C objects that can be created on the stack without delving into absurd tricks). That means that as soon as this method exits, your array now points to garbage, because the blocks it was pointing to no longer exist. To do this properly, you should do:

int (^Block_001)(void) = [^{ return 101; } copy]; int (^Block_002)(void) = [^{ return 202; } copy]; NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil];  [Block_001 release]; [Block_002 release]; 

By invoking copy on the block, you're explicitly moving the block off of the stack and onto the heap, where it can safely remain after the method/function exits. Then after you've added the blocks to the array, you have to balance your copy (because of the NARC rule) with a subsequent call to release. Make sense?

like image 66
Dave DeLong Avatar answered Sep 21 '22 05:09

Dave DeLong


Sure, you just invoke it with () like any other block, but you need to typecast the value you retrieve from NSArray. Here's an example (with an added typedef, because otherwise my head hurts):

typedef int (^IntBlock)(void); IntBlock Block_001 = ^{ return 101; }; IntBlock Block_002 = ^{ return 202; }; NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil]; int x = ((IntBlock)[array objectAtIndex:0]) (); // now x == 101 
like image 45
David Gelhar Avatar answered Sep 22 '22 05:09

David Gelhar