前言
我們都知道,回調(diào)的方案有Block杯瞻、代理镐牺、通知。要想實(shí)現(xiàn)一對(duì)多就要用通知又兵。而且很方便的在多個(gè)地方進(jìn)行回調(diào)任柜,實(shí)現(xiàn)我們?nèi)我獾姆椒ā?br> 然而是否考慮過(guò)用Block去實(shí)現(xiàn)一對(duì)多的回調(diào)卒废。注意:不是無(wú)聊的想象哦沛厨,可以讓我們看到另一面。
原理
一般情況下摔认,Block的回調(diào)確實(shí)是一對(duì)一的逆皮。要想實(shí)現(xiàn)一對(duì)多,我們能想到什么参袱?緩存block电谣,循環(huán)調(diào)用秽梅。沒(méi)錯(cuò),本質(zhì)上是這樣〗宋現(xiàn)在我們需要考慮一下幾個(gè)問(wèn)題:
1企垦、用什么緩存來(lái)保證不會(huì)循環(huán)引用?
2晒来、Block的一對(duì)一钞诡?
3、如何遍歷調(diào)用湃崩?
4荧降、還有不想用的Block如何快捷移除?
下面我們來(lái)一一解決以上問(wèn)題攒读。
實(shí)現(xiàn)方案
1朵诫、第一個(gè)問(wèn)題:
首先我們可以考慮使用 NSMapTable
來(lái)緩存Block。因?yàn)檫@種緩存方式可以設(shè)置KEY和Value的緩存策略薄扁,并且可以把KEY設(shè)置成Object剪返。設(shè)置Value的緩存策略,可以讓我們把綁定的Block的Target也就是我們Value設(shè)置成 NSPointerFunctionsWeakMemory
這樣就可以在Target釋放的時(shí)候邓梅,自動(dòng)移除我們的此條數(shù)據(jù)随夸。
2、第二個(gè)問(wèn)題
我們可以使用 runtime
的關(guān)聯(lián)對(duì)象震放,關(guān)聯(lián)當(dāng)前監(jiān)聽(tīng)對(duì)象 observer
和Block宾毒。
3、第三個(gè)問(wèn)題
遍歷的時(shí)候殿遂,遍歷所有的存儲(chǔ)的 observer
诈铛,找出其相對(duì)應(yīng)的KEY,再用KEY找到所關(guān)聯(lián)的Block墨礁。從而實(shí)現(xiàn)回調(diào)幢竹。
4、第四個(gè)問(wèn)題
其實(shí)第四個(gè)問(wèn)題恩静,在第一個(gè)問(wèn)題的基礎(chǔ)上也已經(jīng)得到了解決焕毫。但是我們有可能在一個(gè)類里,也就是同一個(gè)Observer中有多個(gè)回調(diào)驶乾,此時(shí)我們需要的KeyTable中的數(shù)據(jù)無(wú)法移除邑飒。此時(shí)單獨(dú)Remove即可
具體實(shí)現(xiàn)如下:
@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 {
// 使用內(nèi)存地址保證一個(gè)對(duì)象置監(jiān)聽(tīng)一次
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 進(jìn)行關(guān)聯(lián)
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);
}
/// 移除的時(shí)候,僅僅只是移除的KeyTable數(shù)據(jù)级乐,因?yàn)閂alueTable中的數(shù)據(jù)疙咸,隨著Observer的釋放而自動(dòng)移除了
- (void)remove:(id)obj {
NSString *key = [NSString stringWithFormat:@"%@-key", [NSString stringWithFormat:@"%p", obj]];
[self.keyTable removeObjectForKey:key];
}
@end