组播块:如何概括

时间:2014-02-09 12:59:18

标签: objective-c delegates objective-c-blocks nsinvocation multicastdelegate

目标

我有一个具有各种属性的类,可以用来插入一个块来接收某些事件。

@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。插入doSomething后,logger仍应运行。

当前实施

为了维护块,我考虑使用存储块副本的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需要模仿一个块,将它收到的任何调用转发给数组。

这里介绍的技术可以用吗?

可能拦截所有调用,为每个数组元素复制它们并分配新的调用目标?

1 个答案:

答案 0 :(得分:1)

从您提供给mikeash.com的链接中,您会看到在代码中执行此操作是一项挑战,而不是要包含在生产代码中。出于类似的原因,C#stuff的工作原理是因为它是由运行时提供的,所以你不能轻易地在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,使用它两次,然后删除不再需要的宏。

实现遵循您的代码,并且同样使用宏 - 它在循环中使用arglist宏参数进行调用,我只是在此处包含它以保持一致性,尽管它没有被使用。

我对您的代码进行的唯一重大更改是使用带有自动生成密钥的NSMutableDictionary(只是一个递增的数字) - 密钥由addBlock:返回并由{{1}接受并且避免了复制块的任何问题(如果它们是相同的块,则两个块只相等)

不完全是你想要的但它有效。

<强>附录

好的,目前还不清楚如何使用它,这是我的测试代码,应该解释所有:

removeBlock: