當(dāng)單繼承不夠用,很難為問題域建模時(shí)馋艺,我們通常都會(huì)直接想到多繼承嚎卫。多繼承是從多余一個(gè)直接基類派生類的能力嘉栓,可以更加直接地為應(yīng)用程序建模。但是Objective-C不支持多繼承拓诸,由于消息機(jī)制名字查找發(fā)生在運(yùn)行時(shí)而非編譯時(shí)侵佃,很難解決多個(gè)基類可能導(dǎo)致的二義性問題。不過其實(shí) Objective-C 也無需支持多繼承奠支,我們可以找到如下幾種間接實(shí)現(xiàn)多繼承目的的方法:
消息轉(zhuǎn)發(fā)??
delegate和protocol ?
類別? ?
當(dāng)向someObject發(fā)送某消息馋辈,但runtime system在當(dāng)前類和父類中都找不到對應(yīng)方法的實(shí)現(xiàn)時(shí),runtime system并不會(huì)立即報(bào)錯(cuò)使程序崩潰倍谜,而是依次執(zhí)行下列步驟:
分別簡述一下流程:
1.動(dòng)態(tài)方法解析:向當(dāng)前類發(fā)送 resolveInstanceMethod: 信號首有,檢查是否動(dòng)態(tài)向該類添加了方法燕垃。(迷茫請搜索:@dynamic)
2.快速消息轉(zhuǎn)發(fā):檢查該類是否實(shí)現(xiàn)了 forwardingTargetForSelector: 方法,若實(shí)現(xiàn)了則調(diào)用這個(gè)方法井联。若該方法返回值對象非nil或非self卜壕,則向該返回對象重新發(fā)送消息。
3.標(biāo)準(zhǔn)消息轉(zhuǎn)發(fā):runtime發(fā)送methodSignatureForSelector:消息獲取Selector對應(yīng)的方法簽名烙常。返回值非空則通過forwardInvocation:轉(zhuǎn)發(fā)消息轴捎,返回值為空則向當(dāng)前對象發(fā)送doesNotRecognizeSelector:消息,程序崩潰退出蚕脏。
顧名思義侦副,我們可以利用上述過程中的2、3兩種方式來完成消息轉(zhuǎn)發(fā)驼鞭。
快速消息轉(zhuǎn)發(fā)
快速消息轉(zhuǎn)發(fā)的實(shí)現(xiàn)方法很簡單秦驯,只需要重寫 - (id)forwardingTargetForSelector:(SEL)aSelector ?方法即可。
我來舉個(gè)簡單的例子挣棕,比如現(xiàn)有2個(gè)類:Teacher 和 Doctor译隘,Doctor可以做手術(shù)(operate方法)。
@interface?Teacher?:?NSObject? ?
@end? ?
@interface?Doctor?:?NSObject? ?
-?(void)operate;???
@end???
通過快速消息轉(zhuǎn)發(fā)洛心,可以很輕松的讓teacher調(diào)用doctor的方法做手術(shù)固耘。
Teacher類需要實(shí)現(xiàn)將消息轉(zhuǎn)發(fā)給Doctor:
-?(id)forwardingTargetForSelector:(SEL)aSelector???
{???
????Doctor?*doctor?=?[[Doctor?alloc]init];???
if?([doctor?respondsToSelector:aSelector])?{???
return?doctor;???
????}???
return?nil;???
}???
雖然消息可以動(dòng)態(tài)轉(zhuǎn)發(fā)傳遞,但是編輯器的靜態(tài)檢查是繞不過的词身,那么問題來了厅目,既然Teacher類沒有實(shí)現(xiàn)operate方法又該如何聲明呢?
到目前為止法严,我只想到下面2種方法:
聲明方法1 ———— 類別
@interface?Teacher?(DoctorMethod)???
-?(void)operate;? ?
@end? ?
聲明方法2 ———— 導(dǎo)入頭文件损敷、調(diào)用時(shí)強(qiáng)轉(zhuǎn)類型
Teacher類頭文件需要包含Doctor頭文件,告訴編譯器去Doctor.h中可以找到operator方法的聲明深啤,并且在調(diào)用時(shí)強(qiáng)轉(zhuǎn)類型嗤锉。
Teacher?*teacher?=?[[Teacher?alloc]init];???
[(Doctor?*)teacher?operate];???
有興趣可以思考一個(gè)問題:如果將其類型轉(zhuǎn)成 id ,也可以編譯通過墓塌,并實(shí)現(xiàn)轉(zhuǎn)發(fā)“露睿可是會(huì)帶來什么隱患呢苫幢?
方法1使用類別足夠清晰簡便,為什么還要提出辦法2呢 垫挨? 我的想法是韩肝,方法1的弊端是拋出來的方法是定死的,而且在.h里露著九榔;方法2就相對靈活哀峻,而且隱藏了我要轉(zhuǎn)發(fā)的消息涡相。
標(biāo)準(zhǔn)消息轉(zhuǎn)發(fā)
標(biāo)準(zhǔn)消息轉(zhuǎn)發(fā)需要重寫 methodSignatureForSelector: 和 forwardInvocation: 兩個(gè)方法即可。
轉(zhuǎn)發(fā)重寫方法:
-?(NSMethodSignature?*)methodSignatureForSelector:(SEL)aSelector???
{???
????NSMethodSignature*?signature?=?[super?methodSignatureForSelector:aSelector];???
if?(signature==nil)?{???
????????signature?=?[someObj?methodSignatureForSelector:aSelector];???
????}???
????NSUInteger?argCount?=?[signature?numberOfArguments];???
for(NSInteger?i=0?;?i
????}? ?
return?signature;???
}? ?
-?(void)forwardInvocation:(NSInvocation?*)anInvocation???
{???
????SEL?seletor?=?[anInvocation?selector];???
if?([someObj?respondsToSelector:seletor])?{???
????????[anInvocation?invokeWithTarget:someObj];???
????}? ?
}???
兩種消息轉(zhuǎn)發(fā)方式的比較
快速消息轉(zhuǎn)發(fā):簡單剩蟀、快速催蝗、但僅能轉(zhuǎn)發(fā)給一個(gè)對象。
標(biāo)準(zhǔn)消息轉(zhuǎn)發(fā):稍復(fù)雜育特、較慢丙号、但轉(zhuǎn)發(fā)操作實(shí)現(xiàn)可控,可以實(shí)現(xiàn)多對象轉(zhuǎn)發(fā)缰冤。
delegate和protocol
委托是Objective-C中最常用的一種回調(diào)機(jī)制犬缨。用法我覺得沒什么好說的,總結(jié)一下該機(jī)制特點(diǎn):
委托協(xié)助主體完成操作任務(wù)棉浸,將需要定制化的操作預(yù)留給委托對象來自定義實(shí)現(xiàn)怀薛,類似子類化主體。
除此之外迷郑,可以用作事件監(jiān)聽枝恋。
一時(shí)還真想不出來了…
類別
個(gè)人認(rèn)為類別是Objective-C設(shè)計(jì)的一大精髓,也是我愛上Objective-C的最大理由三热。
類別是個(gè)強(qiáng)大的東西鼓择,它既可以為類添加方法,也可以添加實(shí)例就漾。一定有不少人不認(rèn)同呐能,想提醒我:類別的局限性之一就是無法向類中添加新的實(shí)例變量。 背書真心毀人抑堡,聽我舉個(gè)例子慢慢說摆出。
重新再來個(gè)Teacher類:
@interface?Teacher?:?NSObject???
{???
????NSUInteger?age;???
}? ?
@end???
光有個(gè)年齡還不能滿足對teacher的描述,我想加個(gè)profession實(shí)例來存teacher的專業(yè)首妖。直觀的想法是子類化Teacher偎漫,其實(shí)也可以用類別。
你需要了解一下 runtime 編程知識有缆,關(guān)注一下 objc_setAssociatedObject 和 objc_getAssociatedObject 象踊。
//??Teacher+Profession.m? ?
#import?"Teacher+Profession.h"???
#import? ?
const?char?*ProfessionType?=?"NSString?*";???
@implementation?Teacher?(Profession)? ?
-(void)setProf:(NSString*)prof???
{???
????objc_setAssociatedObject(self,?ProfessionType,?prof,?OBJC_ASSOCIATION_RETAIN_NONATOMIC);???
}? ?
-(NSString?*)prof???
{???
????NSString?*pro?=?objc_getAssociatedObject(self,?ProfessionType);???
return?pro;???
}? ?
@end???
現(xiàn)在就可以通過setProf: 和 prof 來存取 teacher 的 profession 值了。