Protocol協(xié)議代理在開發(fā)中應(yīng)用頻繁阶牍,開發(fā)者經(jīng)常會遇到一個問題——事件的連續(xù)傳遞喷面。比如,為了隔離封裝走孽,開發(fā)者可能經(jīng)常會把tableview的delegate或者datesource抽離出獨(dú)立的對象惧辈,而其它對象(比如VC)需要獲取某些delegate事件時,只能通過事件的二次傳遞磕瓷。有沒有更簡單的方法了盒齿?協(xié)議分發(fā)器正好可以派上用場
話不多說,先上干貨:HJProtocolDispatcher是一個協(xié)議實(shí)現(xiàn)分發(fā)器困食,通過該工具能夠輕易實(shí)現(xiàn)將協(xié)議事件分發(fā)給多個實(shí)現(xiàn)者边翁。比如最常見的tableview的delegate協(xié)議,通過HJProtocolDispatcher硕盹,能夠非常容易的分發(fā)給多個對象符匾,具體可參考Demo
self.tableView.delegate = AOProtocolDispatcher(UITableViewDelegate, self, self.delegateSource);
原理解析
原理并不復(fù)雜, 協(xié)議分發(fā)器Dispatcher并不實(shí)現(xiàn)Protocol協(xié)議莱睁,其只需將對應(yīng)的Protocol事件分發(fā)給不同的實(shí)現(xiàn)者Implemertor待讳。如何實(shí)現(xiàn)分發(fā)?
熟悉類Class響應(yīng)鏈的童鞋都知道仰剿,NSObject對象主要通過以下函數(shù)響應(yīng)未實(shí)現(xiàn)的Selector函數(shù)調(diào)用
- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
因此创淡,協(xié)議分發(fā)器Dispatcher可以在該函數(shù)中將Protocol中Selector的調(diào)用傳遞給實(shí)現(xiàn)者Implemertor,由實(shí)現(xiàn)者Implemertor實(shí)現(xiàn)具體的Selector函數(shù)即可
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL aSelector = anInvocation.selector;
if (!ProtocolContainSel(self.prococol, aSelector)) {
[super forwardInvocation:anInvocation];
return;
}
for (ImplemertorContext *implemertorContext in self.implemertors) {
if ([implemertorContext.implemertor respondsToSelector:aSelector]) {
[anInvocation invokeWithTarget:implemertorContext.implemertor];
}
}
}
設(shè)計關(guān)鍵
如何做到只對Protocol中Selector函數(shù)的調(diào)用做分發(fā)是設(shè)計的關(guān)鍵南吮,系統(tǒng)提供有函數(shù)
objc_method_description protocol_getMethodDescription(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod)
通過以下方法即可判斷Selector是否屬于某一Protocol
struct objc_method_description MethodDescriptionForSELInProtocol(Protocol *protocol, SEL sel) {
struct objc_method_description description = protocol_getMethodDescription(protocol, sel, YES, YES);
if (description.types) {
return description;
}
description = protocol_getMethodDescription(protocol, sel, NO, YES);
if (description.types) {
return description;
}
return (struct objc_method_description){NULL, NULL};
}
BOOL ProtocolContainSel(Protocol *protocol, SEL sel) {
return MethodDescriptionForSELInProtocol(protocol, sel).types ? YES: NO;
}
注意事項
協(xié)議分發(fā)器使用需要了解如何處理帶有返回值的函數(shù) 琳彩,比如
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
我們知道,iOS中,函數(shù)執(zhí)行返回的結(jié)果存在于寄存器R0中露乏,后執(zhí)行的會覆蓋先執(zhí)行的結(jié)果碧浊。因此,當(dāng)遇到有返回結(jié)果的函數(shù)時瘟仿,返回結(jié)果以后執(zhí)行的函數(shù)返回結(jié)果為最終值箱锐,以Demo為例
self.tableView.delegate = AOProtocolDispatcher(UITableViewDelegate, self, self.delegateSource);
TableView的DataSource以后面的self.delegateSource中實(shí)現(xiàn)函數(shù)返回的結(jié)果為準(zhǔn)
備注
開發(fā)完本項目后發(fā)現(xiàn)網(wǎng)上已有朋友實(shí)現(xiàn)了協(xié)議分發(fā)器AOMultiproxier,因此劳较,技術(shù)版權(quán)屬于原作者驹止,本文只做宣傳,特此說明观蜗!
注:二次傳遞臊恋,在demo里面就是viewController中會響應(yīng)這個代理方法,在delegateSource中也會響應(yīng)這個代理方法墓捻。也就是兩個地方都實(shí)現(xiàn)代理方法抖仅,會依次響應(yīng)。
轉(zhuǎn)載:http://www.olinone.com/