How to enforce parameters of anonymous blocks to be unused in Objective-C?

673 views Asked by At

I've run into a situation while using a library called TransitionKit (helps you write state machines) where I am want to supply entry and exit actions in the form of a callback.

Sadly, the callbacks include two completely useless parameters. A typical block has to look like this:

^void (TKState *state, TKStateMachine *stateMachine) {
    // I TOTALLY don't want parameters `state` or `stateMachine` used here
};

(this is an anonymous code block. Read up on blocks here if you're unclear)

As I've noted in the comment, I really don't want those parameters even mentioned in the body there. I've tried simply removing the parameter names like suggested in this question like so:

^void (TKState *, TKStateMachine *) {
     // I foobar all I like here
};

but sadly the code won't compile then :(.

How can I enforce this non-usage of parameters in code?

4

There are 4 answers

0
e1985 On

AFAIK there is no way to do what you want when you are creating a block, you can only miss the parameter names when you are declaring a block variable(a reference to a block, to avoid misunderstandings)

So here you can miss the param names:

void (^myBlock)(SomeClass *);

But not when you create a block:

myBlock = ^(SomeClass *o)
{
};
1
AudioBubble On

This is what I could come up with. Quite a hack and relies on the GCC poison pragma, which is not standard but a GNU extension - although, given that you are probably compiling this with clang anyway, it should not be a problem.

#define _state state
#define _stateMachine stateMachine

#pragma GCC poison state stateMachine

Then this compiles:

^(TKState *_state, TKStateMachine *_stateMachine) {
    do_something();
}

But this doesn't:

^(TKState *_state, TKStateMachine *_stateMachine) {
    do_something(state, stateMachine);
}
0
gnasher729 On

I'd write

^void (TKState *unused_state, TKStateMachine *unused_stateMachine) {
    // Anyone using unused_state or unused_stateMachine gets what they deserve.
};

Of course someone can use the parameters. But then whatever you do, they can change the code. If someone is intent on shooting themselves in the foot, there is no stopping them.

0
Clay Bridges On

You could just have a function that took one kind of block, and returned another, like this:

@class TKState, TKStateMachine; // here so this will compile

typedef void (^LongStateBlock)(TKState *state, TKStateMachine *stateMachine);

static inline LongStateBlock Adapter(void(^block)()) {

    void(^heapBlock)() = [block copy]; // forces block to be on heap rather than stack, a one-time expense

    LongStateBlock longBlock = ^(TKState *s __unused, TKStateMachine *sm __unused) {
        heapBlock();
    };

    // this is the non-ARC, MRR version; I'll leave ARC for the interested observer
    [heapBlock release];
    return [[longBlock copy] autorelease];
}

And in practice:

// this represents a library method
- (void)takesLongStateBlock:(LongStateBlock)longBlock
{
    // which hopefully wouldn't look exactly like this
    if (longBlock) longBlock(nil, nil);
}

- (void)yourRandomMethod
{
    [self takesLongStateBlock:^(TKState *state, TKStateMachine *stateMachine) {
        NSLog(@"Gratuitous parameters, AAAAHHHH!");
    }];

    [self takesLongStateBlock:Adapter(^{
        NSLog(@"So, so clean.");
    })];

}

The whole thing is gisted, and should compile inside any class. It does what you expect when you call -yourRandomMethod.