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)initdoSomethingdoSomethinglogger

当前实施

为了维护块,我考虑使用NSMutableArray存储块的副本并将事件广播到所有已注册的块(观察者模式)。

- (id)初始化

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.handlervoid (^)(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,使用它两次,然后删除不再需要的宏。

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

我对您的代码所做的唯一重大更改是使用NSMutableDictionary带有自动生成的密钥(只是增加的数字) - 密钥被返回并被addBlock:接受,removeBlock:并避免了复制块的任何问题(两个块只有在它们相等时才相等同一块

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

附录

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

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 回答