简体   繁体   中英

Objective-C - Storing block with parameters for callback

I have a general routine, which takes a few parameters. Something like:

-(id) doStuff:(int)A:(int)B:(int)C {
    //doStuff
    return object;
}

I have a UITableViewController , which houses a number of custom cells, each with their own ID. When 'Save' is hit, these cells are iterated and some cells need 'additional behaviour' when they are being saved.

Up to now, I've created a 'Callback' object, which stores an NSString * and a delegate in the custom cell. Upon being 'Saved', the cell looks, whether it has any callbacks to apply and uses

SEL sel = NSSelectorFromString(Sel);
if([Del respondsToSelector:sel])
    [Del performSelector:sel withObject:Cell];

Now that works somewhat well..., however, it requires the method I pass to do a switch/case on the ID of the Cell that's passed, and I'd like to avoid that.

That's why I'd like to use blocks instead, but I don't really know how to store a parameterized block in a variable.

What I'm trying to do:

Declare a function block doStuff .

id (^doStuff) (int, int, int) = ^(int A, int B, int C) {
    //does Stuff
};

And add the previously created block as callback

[Cell addCallback:(^doStuff)(1, 2, 3)];

The block must NOT be called at that moment, but stored in the cell and only called it when the time is right. How would I go about this correctly?

Thank you very much.

Edit: What I'd also like to avoid is storing the parameters for the block in the cell and pass them upon calling, because that would require me to further specialize the cells unnecessarily.

It sounds like what you want is a block that calls your block, something like this:

[cell addCallback:^{ doStuff(1, 2, 3); }];

But this is a rather odd and convoluted design. It seems like there is probably a way to write it with only one block, but it's hard to give a solution that specific without a better idea of what you're doing.

The most straight forward way is to create a typedef containing how the block parameters should look like, then use it to declare a new property/ivar. The following sample code is copied from the Sensible TableView framework SCCellActions class:

typedef void(^SCCellAction_Block)(SCTableViewCell *cell, NSIndexPath *indexPath);

@interface SCCellActions : NSObject
...
@property (nonatomic, copy) SCCellAction_Block willDisplay;
...
@end

You could then set the property as follows:

cellActions.willDisplay = ^(SCTableViewCell *cell, NSIndexPath *indexPath)
{
    cell.backgroundColor = [UIColor yellowColor];
};

Similarly, you could declare a parameter as follows:

...
- (void)callActionBlock:(SCCellAction_Block actionBlock)
{
    if(actionBlock)
    {
        actionBlock(self.cell, self.cellIndexPath);
    }
}
...

In which case the method should be called like this:

[myObject callActionBlock:^(SCTableViewCell *cell, NSIndexPath *indexPath {cell.backgroundColor = [UIColor yellowColor];}];

This answer is based on Chuck's suggestion and describes the pitfalls I encountered realizing it.

Creation:

Cell = [self CreateCell];
[Cell addCallback:^{ return doStuff(Cell, 1, 2, 3, 4) } At:ON_SAVE];

doStuff is a local block, declared before the cells. I was unable to add it directly to the cell, because I also needed a reference to the calling cell within the block.

Pitfall at this point: Class variables. A block will only retain...or rather 'copy'...local variables, but not class variables. Assuming that 'Cell' was a class variable and set by 'CreateCell', the block would work with the value of Cell at the time the block is executed.

As such, it is important to remember to declare a local variable, which assumes the value of the class variable if necessary.

Storage:

- (void) addCallback:(CallBlock_t)B At:(int)at {
    //Creates a Callback-Object and passes it the block and adds it to an Array.
}

- (id) initWithBlock:(CallBlock_t)B At:(int)at {
    self = [super init];
    if(self) {
        Block = [B copy];    //Yes, Copy. Not retain.
        When = at;
    }
    return self;
}

Pitfall at this point: If the block is merely retained, the local block from the calling function will go out of scope and the program will fail with 'Bad Access'. Copy resolves this problem.

Of course you need to release the Block once you're done using it (in the dealloc of the callback class), but that's a given.

I hope this little explanation will save someone some grief.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM