1

目標組播座:如何概括

我有可以使用的塊插上接收特定事件的各種屬性的類。

@interface SomeClass 

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

@end 

在客戶端代碼,我想動態添加/刪除處理程序塊,以該屬性,類似於MulticastDelegate在C#。

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)初始

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]]; 

- (無效)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需要模仿一個塊,將其收到的任何調用轉發給數組。

這裏描述的技術是否可以使用?

也許攔截所有的調用,複製他們爲每個數組元素,並賦予新的調用目標?

+0

這有什麼意義調用轉發到具有不同簽名的塊?或者到具有不同簽名的塊列表? –

+0

這些塊共享相同的簽名。但是,我想重複使用該模式,而不必爲每個回調類型手動創建數組。假設SomeClass有第二個處理程序塊'void(^ otherHandler)(int arg1,NSString * arg2,id arg3)'。用我目前的方法,我需要翻一番。 我列在最後的'MulticastBlock'將匹配任何簽名,因此可以重複使用。我想知道這樣一個普通的塊是否可能。 當然,您仍然需要爲每個處理程序都有不同的MulticastBlock實例,但它是2行而不是11個 – Etan

回答

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生成的密鑰(只是越來越多的數字) - 密鑰由返回和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"); 
+0

不過,問題在於將MulticastBlock分配給處理程序^^,因爲處理程序是某些塊類型,但MulticastBlock不是塊。 – Etan

+1

@Etan - 添加了我的測試代碼以顯示它的工作原理,您只需將'block'屬性作爲您的處理程序。在您的代碼中,您將代碼直接分配給處理程序,在該代碼塊中,該代碼塊將分配給該屬性,然後將該屬性分配給調用代碼中的處理程序。 – CRD