Как сделать так, чтобы параметры анонимных блоков не использовались в Objective-C?
Я столкнулся с ситуацией, когда использовал библиотеку TransitionKit (помогает писать конечные автоматы), где я хочу предоставить действия входа и выхода в форме обратного вызова.
К сожалению, обратные вызовы включают два совершенно бесполезных параметра. Типичный блок должен выглядеть так:
^void (TKState *state, TKStateMachine *stateMachine) {
// I TOTALLY don't want parameters `state` or `stateMachine` used here
};
(это блок анонимного кода. Читайте о блоках здесь, если вам неясно)
Как я отметил в комментарии, я действительно не хочу, чтобы эти параметры даже упоминались в теле. Я попытался просто удалить имена параметров, как предложено в этом вопросе, вот так:
^void (TKState *, TKStateMachine *) {
// I foobar all I like here
};
но, к сожалению, код не скомпилируется тогда:(.
Как я могу принудить это неиспользование параметров в коде?
4 ответа
Это то, что я мог придумать. Довольно взломать и опирается на GCC poison
Прагма, которая не является стандартной, но расширение GNU - хотя, учитывая, что вы, вероятно, компилируете это с clang
во всяком случае, это не должно быть проблемой.
#define _state state
#define _stateMachine stateMachine
#pragma GCC poison state stateMachine
Затем это компилируется:
^(TKState *_state, TKStateMachine *_stateMachine) {
do_something();
}
Но это не так:
^(TKState *_state, TKStateMachine *_stateMachine) {
do_something(state, stateMachine);
}
Вы можете просто иметь функцию, которая принимает один вид блока и возвращает другой, например так:
@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];
}
И на практике:
// 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.");
})];
}
Все это суть и должно компилироваться внутри любого класса. Он делает то, что вы ожидаете, когда вы звоните -yourRandomMethod
,
AFAIK нет способа сделать то, что вы хотите, когда вы создаете блок, вы можете пропустить имена параметров только тогда, когда вы объявляете переменную блока (ссылка на блок, чтобы избежать недопонимания)
Так что здесь вы можете пропустить названия параметров:
void (^myBlock)(SomeClass *);
Но не когда вы создаете блок:
myBlock = ^(SomeClass *o)
{
};
Я бы написал
^void (TKState *unused_state, TKStateMachine *unused_stateMachine) {
// Anyone using unused_state or unused_stateMachine gets what they deserve.
};
Конечно, кто-то может использовать параметры. Но тогда, что бы вы ни делали, они могут изменить код. Если кто-то намеревается выстрелить себе в ногу, его нельзя остановить.