1

ゴール

ブロックをプラグインして特定のイベントを受け取るために使用できるさまざまなプロパティを持つクラスがあります。

@interface SomeClass

@property (copy, nonatomic) void (^handler)(int arg1, int arg2);

@end

クライアント コードでは、C# の MulticastDelegate と同様に、このプロパティにハンドラー ブロックを動的に追加/削除したいと考えています。

self.logger = ^(int arg1, int arg2){
    NSLog(@"arg1 = %d, arg2 = %d", arg1, arg2);
};

void (^doSomething)(int, int) = ^(int arg1, int arg2){
    if (arg1 == 42) {
        // Do something.
    }
};

loggerたとえば、プラグインしたいのですが、特定のメソッドの実行中に-(id)initのみ使用します。が接続されてdoSomethingいる間は、実行する必要があります。doSomethinglogger

現在の実装

NSMutableArrayブロックを維持するために、ブロックのコピーを保存し、登録されているすべてのブロックにイベントをブロードキャストする を使用することを考えました(オブザーバー パターン)。

- (id)init

self.handlerBlocks = [NSMutableArray array];
__weak typeof(self) weakSelf = self;
self.object.handler = ^(int x, int y){
    typeof(self) strongSelf = weakSelf;
    if (!strongSelf) {
        return;
    }
    for (void (^item)(int x, int y) in strongSelf.handlerBlocks) {
        item(x, y);
    }
};

[self.handlerBlocks addObject:[self.logger copy]];

- (void)someOtherMethod

void (^doSomething)(int, int) = [^(int arg1, int arg2){
    if (arg1 == 42) {
        // Do something.
    }
} copy];
[self.handlerBlocks addObject:doSomething copy];
// Do something.
[self.handlerBlocks removeObject:doSomething];

未解決の質問

メソッドは、任意の引数のカウント/タイプを持つブロックに一般化できますか? 私はこのようにそれを使うことができるように:

MulticastBlock *b = [[MulticastBlock alloc] init];
self.object.handler = b;

[b addBlock:self.logger];

ここでの問題は、 の型が であるということself.object.handlerですvoid (^)(int, int)。したがって、MulticastBlockブロックを模倣して、受信した呼び出しを配列に転送する必要があります。

ここで説明されている手法を使用できますか?

すべての呼び出しをインターセプトし、配列要素ごとにコピーして、新しい呼び出しターゲットを割り当てているのでしょうか?

4

1 に答える 1

1

mikeash.com に提供したリンクから、これをコードで行うのは困難であり、実稼働コードに含めるものではないことがわかります。同様の理由で、C# はランタイムによって提供されるため機能しますが、C# で自分で簡単に作成することはできませんでした。パラメトリック ポリモーフィズムでさえ、ここでは役に立ちません。さまざまな数の引数を使用してブロック呼び出しを取得することはできません。

必要なのは、文字列展開による「パラメトリック ポリモーフィズム」、つまりマクロです。

サンプルの "MulticastBlock.h" ファイルを次に示します。

#define MULTICAST(name, typelist, arglist) \
\
@interface name : NSObject \
\
@property (readonly) void (^block)typelist; \
\
- (id) addBlock:(void (^)typelist)aBlock; \
\
- (void) removeBlock:(id)token; \
\
@end

MULTICAST(MulticastBlock, (int arg1, int arg2), (arg1, arg2))
MULTICAST(MulticastBlock2, (NSString *arg1, NSString *arg2), (arg1, arg2))

#undef MULTICAST

これは、 に展開され、@interfaceそれを 2 回使用し、不要になったマクロを削除するマクロを定義します。

実装はコードに従い、同様にマクロを使用して行われます。arglistループ内の呼び出しにマクロ引数を使用します。使用されていませんが、一貫性を保つためにここに含めています。

私があなたのコードに加えた唯一の重要な変更はNSMutableDictionary、自動生成されたキーで を使用することでした (数字が増えるだけです) addBlock:removeBlock:同じブロック

正確にはあなたが望むものではありませんが、うまくいきます。

補遺

OK、これを使用する方法が明確ではありませんでした。すべてを説明するテストコードを次に示します。

MulticastBlock *multicast = MulticastBlock.new;

id tokenAdd = [multicast addBlock:^(int arg1, int arg2) {
   NSLog(@"%d + %d -> %d", arg1, arg2, arg1 + arg2);
}];

multicast.block(3, 4);

id tokenMul = [multicast addBlock:^(int arg1, int arg2) {
   NSLog(@"%d * %d -> %d", arg1, arg2, arg1 * arg2);
}];

multicast.block(4, 5);

[multicast removeBlock:tokenAdd];

multicast.block(5, 6);

[multicast removeBlock:tokenMul];

multicast.block(6, 7);

MulticastBlock2 *two = MulticastBlock2.new;

[two addBlock:^(NSString *arg1, NSString *arg2) {
   NSLog(@"%@ | %@", arg1, arg2);
}];

two.block(@"asda", @"tesco");
于 2014-02-09T21:33:32.837 に答える