一、通過委托與數(shù)據(jù)源協(xié)議進行對象間通信
1.委托模式
對象之間經(jīng)常需要相互 通信,而通信方式有很多種。OC開發(fā)者廣泛使用一種“委托模式”的編程設(shè)計模式來實現(xiàn)對象間的通信,該模式的主旨是:定義一套接口,某對象若想接收另一個對象的委托,則需遵從此接口,以便于成為其“委托對象”,而這“另一個對象”則可以給委托對象回傳一些信息,也可以在發(fā)生相關(guān)事件時通知委托對象。
此模式可將數(shù)據(jù)與業(yè)務(wù)邏輯解藕。比如說,用戶界面里有個現(xiàn)實一系列數(shù)據(jù)所用的視圖,那么,此視圖只應(yīng)包含顯示數(shù)據(jù)所需的邏輯代碼,而不應(yīng)決定要顯示和中數(shù)據(jù)以及數(shù)據(jù)之間如何交互等問題。視圖對象的屬性中,可以包含負責數(shù)據(jù)與事件處理的對象。這兩種對象分別稱為:“數(shù)據(jù)源”與“委托”。
@protocol EOCNetworkFetcherDelegate
- (void) networkFetcher:(EOCNetworkFetcher *)fetcher didReceiveData:(NSData *)data;
- (void) networkFetcher:(EOCNetworkFetcher *)fetcher didFailWithError:(NSError *)error;
@end
有了這個協(xié)議之后,類就可以用一個屬性來存放其委托對象了。在本例總,這個類就是EOCNetworkFetcher類。此類的接口可以寫成這樣:
@interface EOCNetworkFetcher : NSObject
@property (nonatomic, weak) id <EOCNetworkFetcherDelegate> delegate;
@end
這個id類型的協(xié)議屬性一定要定義成weak,而非strong,因為兩者之間必須為“非擁有關(guān)系”。一般情況下,扮演delegate的那個對象也要持有本對象,直到用完本對象之后,才會釋放。
假如聲明屬性的時候用strong將本對象與委托對象之間定為“擁有關(guān)系”,那么就會引入“保留環(huán)”。因此, 本類中存放委托對象的這個屬性要么定義為weak,要么定義為unsafe_unretained。如果需要在相關(guān)對象銷毀時自動清空,則定義為前者,若不需要自動清空,則定義為后者。
某類若要遵從委托協(xié)議,可以在其接口中聲明,也可以在“分類”中聲明。如果要象外界公布此類實現(xiàn)了某協(xié)議,那么就在接口中聲明,而如果這個協(xié)議是個委托協(xié)議的話,那么通常只會在類的內(nèi)部使用。所以說,這種情況一般都是在“分類”里聲明的:
@implementation EOCDataModel () <EOCNetworkFetcherDelegate>
@end
@implementation EOCDataModel
- (void) networkFetcher: (EOCNetworkFetcher *)fetcher didReceiveData: (NSData *) data {
/* Handle data */
}
- (void) networkFetcher: (EOCNetworkFetcher *)fetcehr didFailWithError: (NSError *)error {
/* Handle error */;
}
@end
委托協(xié)議中的方法一般都是“可選的”,因為扮演“受委托者”角色的這個對象未必關(guān)心其中的所有方法。這時我們就可以使用@optional關(guān)鍵字來標注其大部分或全部的方法:
@protocol EOCNetworkFetcherDelegate
@optional
- (void)networkFetcher:(EOCNetworkFetcher *)fetcher didReceiveData:(NSData *)data;
- (void)networkFetcher:(EOCNetworkFetcher *)fetcher didFailWithError: (NSError *)error;
@end
然而在委托對象上調(diào)用可選方法時,就必須提前使用類型信息查詢方法,來判斷這個委托對象能否響應(yīng)相關(guān)選擇子。以EOCNetworkFetcher為例,應(yīng)該這樣寫:
NSData *data = /* data obtained from network */;
if ([_delegate respondsToSelector: @selector(networkFetcher:didReceiveData:)]) {
[_delegate networkFetcher:self didReceiveData:data];
}
判斷委托對象是否實現(xiàn)了相關(guān)方法,如果實現(xiàn)了就調(diào)用,如果沒有實現(xiàn)就不執(zhí)行任何操作(因為給nil發(fā)送消息將使if語句的值成為false)。
也可以像下方,傳入發(fā)起委托的實例,然后在delegete對象在實現(xiàn)相關(guān)方法時,根據(jù)傳入的實例分別執(zhí)行不同的代碼子:
- (void)networkFetcher: (EOCNetworkFetcher *)fetcher didReceiveData: (NSData *)data {
if (fetcher == _myFetcherA) {
/* Handle data */
} else if (fetcher == _myFetcherB) {
/* Handle data */
}
}
上面這段代碼表明,委托對象有兩個不同的“網(wǎng)絡(luò)數(shù)據(jù)獲取器”,所以他們必須根據(jù)所傳的參數(shù)判斷到底是哪個EOCNetworkFetcher獲取到了數(shù)據(jù)。若沒有此信息,則委托對像在同一時間只能使用一個網(wǎng)絡(luò)請求獲取器,這么做不太好。
delegate里的方法也可以用于從獲取委托對象中獲取信息。比方說,EOCNetworkFetcher類也許想提供一種機制:在獲取數(shù)據(jù)的時候如果遇到了“重定向”,那么將詢問其委托對象是否應(yīng)該發(fā)生重定向。delegate對象中的相關(guān)方法也可以寫成這樣:
- (BOOL)networkFetcher: (EOCNetworkFetcher *)fetcher shouldFollowRedirectToURL: (NSURL *) url;
用結(jié)構(gòu)體緩存委托對象是否能響應(yīng)特定的選擇子。實現(xiàn)緩存功能所用的代碼可以寫在delegate屬性所對應(yīng)的設(shè)置方法里:
- (void) setDelegate:(id<EOCNetworkFetcher>) delegate {
_delegate = delegate;
_delegateFlags.didReceiveData = [delegate respondsToSelector: @selector(networkFetcher:didReceiveData:)];
_delegateFlags.didFailWithError = [delegate respondsToSelector: @selector(networkFetcher:didFailWithError:)];
_delegateFlags.didUpdateProgressTo = [delegate respondsToSelector:@selector(networkFetcher:didUpdateProgressTo:)];
}
2.要點
- 委托模式為對象提供了一套接口,使其可由此將相關(guān)事件告知其他對象。
- 將委托對象應(yīng)該支持的接口定義成協(xié)議,在協(xié)議中把可能需要處理的事件定義成方法。
- 當某對象需要從另外一個對象中獲取數(shù)據(jù)時,可以使用委托模式。這種情況下,該模式亦稱“數(shù)據(jù)源協(xié)議”
- 若有必要,可實現(xiàn)含有位段的結(jié)構(gòu)體,將委托對象是否能響應(yīng)相關(guān)協(xié)議方法這一信息緩存至其中。
二、將類的實現(xiàn)代碼分散到便于管理的數(shù)個分類之中
1.如何實現(xiàn)
OC中有一個分類機制,但是通常是使用它來補充一個需要的類,但其實還有更好的用途,那就是用來規(guī)劃我們的代碼,我們可以通過OC的“分類”機制,把類代碼按邏輯劃入幾個分區(qū)中。就像這樣:
#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
@property (nonatomic, strong, readonly) NSArray *friends;
- (id)initWithFirstName: (NSString *)firstName andLastName:(NSString *)lastName;
/ * Friendship methods * /
- (void) addFriend:(EOCPerson *)person;
- (void) removeFriend:(EOCPerson *)person;
- (BOOL) isFriendsWith:(EOCPerson *)person;
/ * Work methods * /
- (void) performDaysWork;
- (void) takeVacationFromWork;
/ * Play methods * /
- (void) goToTheCinema;
- (void) goToSportsGame;
@end
現(xiàn)在,類的實現(xiàn)代碼按照方法分成了好幾個部分。所以說,這項語言特性就叫做“分類”。本例中,類的基本要素(諸如屬性與初始化方法等)都聲明在“主實現(xiàn)”里??墒牵S著分類數(shù)量增加,當前這份實現(xiàn)文件很快就膨脹得無法管理了,此時就可以把每個分類提取到各自的文件中去,以EOCPerson為例,可以按照其分類拆分成下列幾個文件:
EOCPerson + Friendship(.h/.m)
EOCPerson + Work(.h/.m)
EOCPerson + Play(.h/.m)
//EOCPerson + Friendship.h
#import "EOCPerson.h"
@interface EOCPerson (Friendship)
- (void) addFriend:(EOCPerson *)person;
- (void) removeFriend:(EOCPerson *)person;
- (void) isFriendsWith:(EOCPerson *)person;
@end
//EOCPerson + Friendship.m
#import "EOCPerson + Friendship.h"
@implementation EOCPerson (Friendship)
- (void) addFriend: (EOCPerson *)person {
/* ... */
}
- (void) removeFriend:(EOCPerson *)person {
/* ... */
}
- (BOOL) isFriendsWith:(EOCPerson *)person {
/* ... */
}
@end
并且我們之前有說過私有方法的命名,通過特殊的前綴將私有方法指示出來,那么我們學了分類規(guī)劃之后,我們還可以通過創(chuàng)建一個分類,這個分類其中全是私有方法,通過這種方法將這些私有方法都規(guī)劃到一個類中,當然其還是的遵循之前的命名規(guī)則。
2.要點
- 使用分類機制把類的實現(xiàn)代碼劃分成易于管理的小塊。
- 將應(yīng)該視為“私有”的方法歸入名叫Private的分類中,以隱藏實現(xiàn)細節(jié)。
三、總是為第三方類的分類名稱加前綴
1.為什么總是為第三方類的分類名稱加前綴
分類機制通常用 于向無源碼的既有類中新增功能。這個特性極為強大,但在使用時也很 容易忽視其中可能產(chǎn)生的問題。這個問題在于:分類中的方法是直接添加在類里面的,它們 就好比這個類中的固有方法。將分類方法加人類中這一操作是在運行期系統(tǒng)加載分類時完成 的。運行期系統(tǒng)會把分類中所實現(xiàn)的每個方法都加人類的方法列表中。如果類中本來就有此 方法,而分類又實現(xiàn)了一次,那么分類中的方法會覆蓋原來那一份實現(xiàn)代碼。實際上可能會 發(fā)生很多次覆蓋,比如某個分類中的方法覆蓋了“ 主實現(xiàn)” 中的相關(guān)方法,而另外一個分類 中的方法又覆蓋了這個分類中的方法。多次覆蓋的結(jié)果以最后 一個分類為準。
比方說,要給NSString 添加分類,并在其中提供一些輔助方法,用于處理與HTTP URL 有關(guān)的字符串。你可能會把分類寫成這樣:
@interface NSString (HTTP)
//Encode a string with URL encoding
- (NSString *)urlEncodeString;
//Decode a URL encoded string
- (NSString *)urlDecodedString;
@end
現(xiàn)在看起來沒什么問題,可是,如果還有 一個分類也往NSString里添加方法,那會如何呢?那個分類里可能也有個名叫urIEncodedstring的方法,其代碼與你所添加的大同小 異,但卻不能正確實現(xiàn)你所需的功能。那個分類的加載時機如果晚于你所寫的這個分類,那 么其代碼就會把你的那 一份覆蓋掉,這樣的話,你在代碼中調(diào)用urlEncodedstring 方法時,實際執(zhí)行的是那個分類里的實現(xiàn)代碼。由于其執(zhí)行結(jié)果和你預(yù)期的值不同所以自己所寫 的那些代碼也許就無法正常運行了。這種bug很難追查因為你可能意識不到實際執(zhí)行的 urlEncodedString代碼并不是自己實現(xiàn)的那一份。
要解決此問題,**一般的做法是:以命名空間來區(qū)別各個分類的名稱與其中所定義的方法。 想在Objective-C中實現(xiàn)命名空問功能,只有 一個辦法,就是給相關(guān)名稱都加上某個共用的 前綴。與給類名加前級時所應(yīng)考慮的因素相似,給分類所加的前綴也要選 得怡當才行。**一般來說,這個前級應(yīng)該與應(yīng)用程序或程序庫中其他地方所用的前綴相同。 于 是,我們可以給剛才那個NSString分類加上ABC前綴:
@interface NSString (ABC_HTTP)
//Encode a string with URL encoding
- (NSString *)abc_urlEncodedString;
//Decode a URL encoded string
- (NSString *)abc_urlDecodedString;
@end
2.要點
- 向第三方類中添加分類時,總應(yīng)給其名稱加上你專用的前綴
- 向第三方類中添加分類時,總應(yīng)給其中的方法名加上你專用的前綴
三、勿在分類中聲明屬性
1.勿在分類中聲明屬性的原因
屬性是封裝數(shù)據(jù)的方式。盡管從技術(shù)上說,分類里也可以聲明屬性,但這種做法還是要盡量避免。原因在于,除了“class-continuation分類 ” 之 外 , 其 他分類都無法向類中新增實例變量,因此,它們無法把實現(xiàn)屬性所需的實例變量合成出來。
所以你要是想在分類中實現(xiàn)屬性就得自己進行設(shè)置該屬性的存取方法。你就可以用@dynamic或者消息轉(zhuǎn)發(fā)機制來實現(xiàn)其存取方法,但是還是不太提倡那樣做的。
當然,除了上述的方法可以實現(xiàn)設(shè)置分類中屬性的存取方法之外,還有關(guān)聯(lián)對象也可以解決在分類中不能合成實例變量的問題。就像這樣:
這樣做可行,但是不太理想,要把相似的代碼寫很多遍,而且內(nèi)存管理問題上容易出錯,因為我們在為屬性實現(xiàn)存取方法時,經(jīng)常會忘記遵從其內(nèi)存管理語義。
正確的做法應(yīng)該是:把所有屬性定義在主接口里。類所封裝的全部數(shù)據(jù)都應(yīng)該定義在主接口中,這是唯一能夠?qū)崿F(xiàn)實例變量的地方。并且分類機制,它的目的就是用于擴展類的功能,而非封裝數(shù)據(jù)。
有時屬性可讀也可以定義在分類中,我們可以用其get方法獲取相關(guān)的信息,但是這樣的話還不如直接定義一個方法多好,用處還多,所以說,分類還是好好使用其擴展功能吧。
2.要點
- 把封裝數(shù)據(jù)所用的全部屬性都定義在主接又里。
- 在“class-continuation分類”之外的其他分類中,可以定義存取方法,但盡量不要定義屬性。
四、使用“class-continuation分類”隱藏實現(xiàn)細節(jié)
1.什么是class-continuation分類
OC動態(tài)消息系統(tǒng)的工作方式?jīng)Q定了其不可能實現(xiàn)真正的私有方法或者私有實例變量。那么怎么實現(xiàn)私有變量和私有方法呢?這就要用到特殊的“class-continuation分類”了。
“class-continuation分類”和普通的分類不同,他必須定義在其所接續(xù)的那個類的實現(xiàn)文件里,并且這個類沒有名字。
@interface EOCPerson ()
// Methods here
@end
這樣你就可以在其中定義你的私有方法和私有變量了,這樣有什么好處呢?公共接口里本來就能定義實例變量。不過,把它們定義在“class-continuation分類”或“實現(xiàn)塊”中可以將其隱藏起來,只供本類使用。這些實例變量也并非真的私有,因為在運行期總可以調(diào)用某些方法繞過此限制,不過,從一般意義上來說,他們還是私有的。此外,由于沒有聲明在公共頭文件里,所以將代碼作為程序庫的一部分來發(fā)行時,其隱藏程度更好。
2.“class-continuation分類”的合理用法
“class-continuation分類”還有一種合理用法,就是將public接口中聲明為“只讀”的屬性擴展為“可讀寫”,以便在類的內(nèi)部設(shè)置其值。
就是說,你在外部.h文件中定義一個“只讀”的屬性,然后你又在“class-continuation分類”將其的“只讀”屬性改為“可讀寫”的,那么這樣下來,在外部看來他就是一個“只讀”的屬性,但是你可以在其內(nèi)部自定義的設(shè)置其值了,他在內(nèi)部來說就是“可讀寫”的了。
這樣做很有用,既能令外界無法修改對象,又能在其內(nèi)部按照需要管理其數(shù)據(jù)。這樣,封裝在類中的數(shù)據(jù)就由實例本身來控制,而外部代碼則無法修改其值。
還有一種用法:若對象所遵從的協(xié)議只應(yīng)視為私有,則可在“class-continuation分類”中聲明名,這樣就不會泄漏我們所遵從的協(xié)議
#import "EOCPerson.h"
#import "EOCSecretDelegate.h"
@interface EOCPerson () <EOCSecretDelegate>
@end
@implementation EOCPerson
/* ... */
@end
3.要點
- 通過“class-continuation分類”向類中新增實例變量。
- 如果某屬性在主接口中聲明為“只讀”,而類的內(nèi)部又要用設(shè)置方法修改此屬性,那么就在“class-continuation分類”中將其擴展為“可讀寫”。
- 把私有方法的原型聲明在“class-continuation分類”里面。
- 若想使類所遵循的協(xié)議不為人所知,則可于“class-continuation分類”中聲明。
五、通過協(xié)議提供匿名對象
1.什么是匿名對象
若是接口背后有多個不同的實現(xiàn)類,而你又不想指明具體使用哪個類,那么可以考慮用這個方法——因為有時候這些類可能會變,有時候他們又無法容納于標準的類繼承體系中,因而不能以某個公共基類來統(tǒng)一表示。此概念通常稱為“匿名對象”。
@property (nonatomic, weak) id<EOCDelegete> delegate;
這個delegate就是“匿名的”,因為當你調(diào)用這個delegate的時候你并不知道它指的是那個類,而你卻又能使用它所指代類的方法,這就把那個類給隱藏起來了,匿名對象也是同樣的原理。
因為你可能定義很多的類,但是我們不能將它們都繼承于同一個類,并且在OC中只有id類型可以將這些類的隨便一個類都返回,所以我們在使用匿名對象的時候一定是返回的id類型。比如:我們將所有數(shù)據(jù)庫都具備的那些方法放到協(xié)議中,令返回的對象遵從此協(xié)議。
2.如何使用
先定義一個協(xié)議其中包括數(shù)據(jù)庫都有的方法:
@protocol EOCDatabaseConnection
- (void)connect;
- (void)disconnect;
- (BOOL)isConneceted;
- (NSArray *)performQuery:(NSString *)query;
@end
提供一個單例接口:文章來源:http://www.zghlxwxcb.cn/news/detail-836550.html
#import <Foundation/Foundation.h>
@protocol EOCDatabaseConnection;
@interface EOCDatabaseConnection;
+ (id)sharedInstance;
- (id<EOCDatabaseConnection>)connectionWithIdentifier: (NSString *)identifier;
@end
這樣的話,處理數(shù)據(jù)庫連接的類名稱就不會暴露了,來自不同框架的那些類限制就都可以使用同一個方法來返回了,而不用對每個類都寫一個這種協(xié)議。文章來源地址http://www.zghlxwxcb.cn/news/detail-836550.html
3.要點
- 協(xié)議可在某種程度上提供匿名類型。具體的對象類型可以淡化成遵從某協(xié)議的id類型,協(xié)議里規(guī)定了對象所實現(xiàn)的方法。
- 使用匿名對象來隱藏類型名稱(或類名 )。
- 如果具體類型不重要,重要的是對象能夠相應(yīng)(定義在協(xié)議里的)特定方法,那么可使用匿名對象來表示。
到了這里,關(guān)于【學習iOS高質(zhì)量開發(fā)】——協(xié)議與分類的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!