前言
我们都知道,回调的方案有Block、代理、通知。要想实现一对多就要用通知。而且很方便的在多个地方进行回调,实现我们任意的方法。
然而是否考虑过用Block去实现一对多的回调。注意:不是无聊的想象哦,可以让我们看到另一面。
原理
一般情况下,Block的回调确实是一对一的。要想实现一对多,我们能想到什么?缓存block,循环调用。没错,本质上是这样。现在我们需要考虑一下几个问题:
1、用什么缓存来保证不会循环引用?
2、Block的一对一?
3、如何遍历调用?
4、还有不想用的Block如何快捷移除?
下面我们来一一解决以上问题。
实现方案
1、第一个问题:
首先我们可以考虑使用 NSMapTable 来缓存Block。因为这种缓存方式可以设置KEY和Value的缓存策略,并且可以把KEY设置成Object。设置Value的缓存策略,可以让我们把绑定的Block的Target也就是我们Value设置成 NSPointerFunctionsWeakMemory 这样就可以在Target释放的时候,自动移除我们的此条数据。
2、第二个问题
我们可以使用 runtime 的关联对象,关联当前监听对象 observer 和Block。
3、第三个问题
遍历的时候,遍历所有的存储的 observer,找出其相对应的KEY,再用KEY找到所关联的Block。从而实现回调。
4、第四个问题
其实第四个问题,在第一个问题的基础上也已经得到了解决。但是我们有可能在一个类里,也就是同一个Observer中有多个回调,此时我们需要的KeyTable中的数据无法移除。此时单独Remove即可
具体实现如下:
@interface Manger ()
@property (nonatomic, strong) NSMapTable *valueTable;
@property (nonatomic, strong) NSMapTable *keyTable;
@end
@implementation Manger
+ (instancetype)sharedInstance
{
static Manger *gInteractor = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!gInteractor) {
gInteractor = [Manger new];
gInteractor.valueTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory];
gInteractor.keyTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory];
}
});
return gInteractor;
}
- (void)clickSelector:(id)obsever callBack:(void(^)(NSString *callStr))callBack {
// 使用内存地址保证一个对象置监听一次
NSString *key = [NSString stringWithFormat:@"%@-key", [NSString stringWithFormat:@"%p", obsever]];
NSString *keyValue = [key stringByAppendingString:@"-KeyBlock"];
[self.valueTable setObject:obsever forKey:key];
[self.keyTable setObject:keyValue forKey:key];
// keyValue 和 block 进行关联
objc_setAssociatedObject(obsever, CFBridgingRetain(keyValue), callBack, OBJC_ASSOCIATION_COPY);
}
- (void)clickSelector {
NSArray <NSString *>*keyArr = [[self.valueTable keyEnumerator] allObjects];
[keyArr enumerateObjectsUsingBlock:^(NSString * _Nonnull key, NSUInteger idx, BOOL * _Nonnull stop) {
id observe = [self.valueTable objectForKey:key];
NSString *valueKey = [self.keyTable objectForKey:key];
void(^block)(NSString *callStr) = objc_getAssociatedObject(observe, (__bridge const void * _Nonnull)(valueKey));
if (block) {
block([NSString stringWithFormat:@"%@", NSStringFromClass([self class])]);
}
}];
NSLog(@"%@\n%@", self.valueTable, self.keyTable);
}
/// 移除的时候,仅仅只是移除的KeyTable数据,因为ValueTable中的数据,随着Observer的释放而自动移除了
- (void)remove:(id)obj {
NSString *key = [NSString stringWithFormat:@"%@-key", [NSString stringWithFormat:@"%p", obj]];
[self.keyTable removeObjectForKey:key];
}
@end
