Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copying blocks (ie: copying them to instance variables) in Objective-C

I'm trying to understand blocks. I get how to use them normally, when passed directly to a method. I'm interested now in taking a block, storing it (say) in an instance variable and calling it later.

The blocks programming guide makes it sound like I can do this, by using Block_copy / retain to copy the block away, but when I try to run it I crash my program.

- (void) setupStoredBlock
{
    int salt = 42;
    m_storedBlock = ^(int incoming){ return 2 + incoming + salt; };
    [m_storedBlock retain];
}

I try to call it later:

- (void) runStoredBlock
{
    int outputValue = m_storedBlock(5);
    NSLog(@"When we ran our stored blockwe got back: %d", outputValue);
    [m_storedBlock release];
}

Anyone have any insights? (Or, is there something I'm not getting with blocks?)

Thank you very much!

like image 690
RyanWilcox Avatar asked Apr 17 '10 16:04

RyanWilcox


3 Answers

You'll want to do this instead:

- (void) setupStoredBlock
{
    int salt = 42;
    m_storedBlock = Block_copy(^(int incoming){ return 2 + incoming + salt; });
}
like image 66
Dave DeLong Avatar answered Dec 11 '22 06:12

Dave DeLong


Copy a block when you want it to stay around. Autorelease or release it when you're through with it. Retain it if you need a long way to spell /* NOP */.

@interface Foo : FooSuper {}
@property(copy) int (^storedBlock)(int);
@end

@implementation Foo
@synthesize storedBlock = mStoredBlock;

- (void)setupStoredBlock {
    self.storedBlock = ^{/*...*/};
    // or: mStoredBlock = [^{/*...*/} copy];
    // but this simple implementation violates the atomicity contract
}

- (void)runStoredBlock {
    int result = self.storedBlock(5);
    NSLog(@"%s: result = %d", __func__, result);
}
@end
like image 43
Jeremy W. Sherman Avatar answered Dec 11 '22 08:12

Jeremy W. Sherman


• Like all local variables, a non-static block exists on the stack and will be popped from the stack, like any other local variable which has not been declared static.

• Block_copy() copies the block from the stack onto the heap, where all malloc instances exist. And like all new/copy methods, Block_copy() returns a heap allocated object with a retain count of 1. A block is an objectiveC object but doesNot conform like a normal object. Therefore, there should be no difference between Block_Release() and the objective release method.

• This example uses the copy method of a block instance. Because assigning the result of a Block_copy() to an id requires a type cast that I doNot want to get wrong. The copy method allows the block variable to be assigned directly to an id.

 - (void) setupStoredBlock
{
    int zStackLocalVariable = 42;
    iHeapAllocatedVariable = [^int(int aMore){ return zStackLocalVariable + aMore; } copy];
}

• To declare an object static is to require it to be physically allocated with the code itself. A block which is declared static is compiler prohibited from accessing variables outside of its own scope. Due to the requirements of a static block declaration, I assume that the block on the stack is somehow different from the block which is in the heap.

• A block is an objective c object whose class whose class name and other associated information I have not yet attempted to retrieve, but, like Protocol, Object and other hidden objectiveC classes, it does not conform to NSObject. Like all objectiveC objects, however, it must conform to retain/release. ARC extends retain/release equivalencies into Core Foundation objects as well, and probably, if not now, then eventually, into malloc/free allocations.

• I await the true motivation for a thorough exploration of mikeash.com, as apple likes to keep us all on some hyper-theoritical plane of little physical significance, even though all that is significant is physical.

ARC and blocks also discussed here

like image 45
carmin Avatar answered Dec 11 '22 08:12

carmin