Blocks の nil チェックをスルーするマクロ
Blocksがnilの場合、実行時にクラッシュするため、nilチェックを行う必要がある。
マクロはBlocks の nil チェックを省略表記する。 Blocks が nil だった場合は実行せず、戻り値も0を返す。 NSObject が nil のものにメッセージを送ったときと似たような動きをする。
if (block) {
block(a, b);
}
#define FPB(block) !(block) ? 0 : (block)
FPB(block)(a, b); // OK
FPB(block); // NG
Blocksはコールバック目的で使うことが多いので、呼び出し元がnilを指定したならスルーするといった程度の緩さで良いと思われる。 値を返すような使い方をするときは、後述の既知の不具合もあるので、慎重に使うか、普通の分岐処理を書く方が良いと思われる。
全体を()で囲んでおらず、三項条件演算子の条件部分がむき出しなので、式の途中で使うと予期せぬ不具合が起こるので注意する。
int a = 1 + (FPB(block)(10)); // OK、式の途中で使う場合は()で囲む必要がある
int a = 1 + FPB(block)(10); // NG、(1 + !block) ? 0 : block(10); という意味になり、不具合になる
次のようなマクロも考えてみたが、コンパイルエラーを避けるために戻り値の型で使い分ける必要がある。 エラーになる分、こちらが安全か?
int __attribute__((overloadable)) FPSwamp(void) { return 0; }
int __attribute__((overloadable)) FPSwamp(int arg, ...) { return 0; }
int __attribute__((overloadable)) FPSwamp(id arg, ...) { return 0; }
id __attribute__((overloadable)) FPSwamp4o(void) { return 0; }
id __attribute__((overloadable)) FPSwamp4o(int arg, ...) { return 0; }
id __attribute__((overloadable)) FPSwamp4o(id arg, ...) { return 0; }
#define FPB2(block, ...) (block ? block(__VA_ARGS__) : FPSwamp(__VA_ARGS__))
#define FPB2O(block, ...) (block ? block(__VA_ARGS__) : FPSwamp4o(__VA_ARGS__))
typedef int (^FPIntBlocks)(int a);
typedef NSString *(^FPStringBlocks)(NSString *a, NSString *b);
FPIntBlocks intBlock = ^int (int a) { return a + 1; };
FPStringBlocks strBlocks = ^NSString * (NSString *a, NSString *b) { return a; };
// FPB2とFPB2Oを使い分ける必要がある
int b = 1 + FPB2(intBlock, 2);
NSString *c = FPB2O(strBlocks, @"a", @"b");