OMGPOP
OMGPOP

Reputation: 932

objective c pass block as parameter with OOP

i make a class called "Tile" which is a square, and the passed block will get called when touched.

-(id) initWithRect: (CGRect) r color: (ccColor4B) c block: (void (^) (void)) blk {
    if ((self = [super init])) {
        rect = r;
        color = c;
        block = blk;
        tile = [CCLayerColor layerWithColor:color width:rect.size.width height:rect.size.height];
        tile.position = ccp(rect.origin.x, rect.origin.y);
        [self addChild: tile];
        self.isTouchEnabled = YES;
    }
    return self;
}

//rect is the square, i use CCLayerColor to represent the square.

-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
    CGPoint touchLocation = [Helper locationFromTouch: touch];
    if (CGRectContainsPoint(rect, touchLocation)) {
        block();
        [tile setColor:ccGRAY];
        return YES;

    }
    else {
        return NO;
    }
}

//when touched, just call the block.

then i make a couple of Tiles as follows:

Tile* aTile = [Tile tileWithMidPos:ccp(512, 500) width:300 height:200 color:ccc4(250, 250, 250, 250) block:^{
            [Helper playEffectButtonClicked];
        }];

but all the tiles actually execute the block that is passed by the last tile. what is the problem here? (every tile is an object, so they should call their own block)

Upvotes: 0

Views: 1727

Answers (1)

Stuart Carnie
Stuart Carnie

Reputation: 5476

Blocks are allocated on the stack.

In this case the Tile class should make a copy of the blk argument:

-(id) initWithRect: (CGRect) r color: (ccColor4B) c block: (void (^) (void)) blk {
    if ((self = [super init])) {
        rect = r;
        color = c;
        block = [blk copy];
        tile = [CCLayerColor layerWithColor:color width:rect.size.width height:rect.size.height];
        tile.position = ccp(rect.origin.x, rect.origin.y);
        [self addChild: tile];
        self.isTouchEnabled = YES;
    }
    return self;
}

- (void)dealloc {
    [block release];
    [super dealloc];
}

If you are using ARC, you won't need to worry about memory management (copy and release) if they are passed to methods with block parameters. If you pass stack allocated blocks to objects with id parameters, you must still copy:

[myArray addObject:[^{ // some block } copy]];

Mike Ash has an article well worth reading regarding blocks and ARC.

Upvotes: 2

Related Questions