
神器.png
23、通過委托與數(shù)據(jù)源協(xié)議進(jìn)行對(duì)象間通信
- 委托模式(Delegate pattern)
定義一套接口,某對(duì)象若想接受另一個(gè)對(duì)象的委托,則需遵從此接口,以便成為其“委托對(duì)象”。而“另一個(gè)對(duì)象”則可以給其委托對(duì)象回傳一些信息,也可以在發(fā)生相關(guān)事件時(shí)通知委托對(duì)象。 - 委托模式一般用于反向傳值。
協(xié)議定義:
@protocol FirstViewControllerDelegate <NSObject>
//必選
@required
- (void)firstReturnAge:(NSString *)age;
//可選
@optional
- (void)firstReturnName:(NSString *)name;
- (void)firstReturnNPhone:(NSString *)phone;
- (void)firstReturnUSerID:(NSString *)userID;
@end
- 協(xié)議一般分為 required (必選)和 optional (可選)
例子:UITableView:顧名思義,必選的就是必須實(shí)現(xiàn)的方法,不實(shí)現(xiàn)程序就會(huì)報(bào)錯(cuò)。
@protocol UITableViewDataSource<NSObject>
@required
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
@optional
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; // Default is 1 if not implemented
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section; // fixed font style. use custom view (UILabel) if you want something different
- (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section;
- 有了協(xié)議之后,類就可以用一個(gè)屬性存放其委托對(duì)象了。
@property (nonatomic, weak) id <FirstViewControllerDelegate> delegate;
這個(gè)屬性定義為weak,而非strong,是因?yàn)閮烧咧g必須為“非擁有關(guān)系”。 - 委托方法的實(shí)現(xiàn)
if ([self.delegate respondsToSelector:@selector(firstReturnName:)]) {
[self.delegate firstReturnName:@"哈哈"];
[self.delegate firstReturnAge:@"18"];
[self.navigationController popViewControllerAnimated:YES];
}
可以發(fā)現(xiàn):
- 可選實(shí)現(xiàn)的方法,首先需要通過 respondsToSelector 來判斷委托對(duì)象是否實(shí)現(xiàn)了相關(guān)方法。如果實(shí)現(xiàn)了就調(diào)用,沒有實(shí)現(xiàn)就不執(zhí)行任何操作。
- 必選方法,上面我就和可選寫在一起了,其實(shí)就是默認(rèn)委托對(duì)象一定實(shí)現(xiàn)了相關(guān)方法。如果沒實(shí)現(xiàn)就報(bào)錯(cuò)。
- 如果相關(guān)方法多次調(diào)用,要考慮的優(yōu)化。
多次調(diào)用,那么每次都會(huì)去檢測委托對(duì)象是否實(shí)現(xiàn)了方法。那么我們是不是可以把委托對(duì)象是否能夠響應(yīng)相關(guān)協(xié)議方法這一信息緩存起來?加快執(zhí)行速度!
我們可以用 位段
位段,C語言允許在一個(gè)結(jié)構(gòu)體中以位為單位來指定其成員所占內(nèi)存長度,這種以位為單位的成員稱為“位段”或稱“位域”( bit field) 。利用位段能夠用較少的位數(shù)存儲(chǔ)數(shù)據(jù)。
struct {
unsigned int didFirstReturnName :1;
unsigned int didFirstReturnNPhone :1;
unsigned int didFirstReturnUSerID :1;
} _delegateFlags;
這個(gè)結(jié)構(gòu)體用來緩存委托對(duì)象是否能夠響應(yīng)方法。實(shí)現(xiàn)緩存功能所用的代碼可以寫在 delegate 屬性所對(duì)應(yīng)的設(shè)置方法里:
- (void)setDelegate:(id<FirstViewControllerDelegate>)delegate {
_delegate = delegate;
_delegateFlags.didFirstReturnName = [delegate respondsToSelector:@selector(firstReturnName:)];
_delegateFlags.didFirstReturnNPhone = [delegate respondsToSelector:@selector(firstReturnNPhone:)];
_delegateFlags.didFirstReturnUSerID = [delegate respondsToSelector:@selector(firstReturnUSerID:)];
}
每次調(diào)用 delegate 的相關(guān)方法之前,就不用檢測委托對(duì)象是否實(shí)現(xiàn)了相關(guān)方法,而是直接查詢結(jié)構(gòu)體里的標(biāo)志:
if (_delegateFlags.didFirstReturnName) {
[self.delegate firstReturnName:@"哈哈"];
[self.navigationController popViewControllerAnimated:YES];
}
24、將類的實(shí)現(xiàn)代碼分散到便于管理的數(shù)個(gè)分類之中
- 類中經(jīng)常容易填滿各種方法,而這些方法的代碼則全部堆在一個(gè)巨大的實(shí)現(xiàn)文件里,可以通過“Objective-C”的“分類”機(jī)制,把類代碼按邏輯劃入幾個(gè)分區(qū)中,對(duì)開發(fā)與調(diào)試都有好處。

Xcode創(chuàng)建分類.png
25、總是為第三方類的分類名稱加前綴
- 因?yàn)榉诸悪C(jī)制通常用于向無源碼的既有類中新增功能。這個(gè)特性極為強(qiáng)大,使用時(shí)很容易忽視其中可能產(chǎn)生的問題:
如果分類中的方法覆蓋原來的那一份實(shí)現(xiàn)代碼,那么會(huì)以最后一個(gè)分類為準(zhǔn)。 - 所以我們一般的做法:以命名空間來區(qū)別各個(gè)分類的名稱和其所定義的方法。添加前綴。
26、勿在分類中聲明屬性
- 盡管在技術(shù)上說,分類也可以聲明屬性,但是這種做法還是盡量避免。原因:
除了“class-continuation分類(參看27條)”之外,其他分類都無法向類中新增實(shí)例變量,因此,它們無法把實(shí)現(xiàn)屬性所需的實(shí)例變量合成出來。 - 把封裝數(shù)據(jù)所用的全部屬性都定義在主接口里。
- 在除了“class-continuation分類”之外的其他分類中,可以定義存取方法,但盡量不要定義屬性。
27、使用“class-contunuation分類”隱藏實(shí)現(xiàn)細(xì)節(jié)
- 類中經(jīng)常會(huì)包含一些無須對(duì)外公布的方法及實(shí)例變量。怎么寫呢?,這個(gè)特殊的“class-continuation分類”就派上用場了。
“class-continuation分類”和普通分類不同,它必須定義在其所接續(xù)的那個(gè)類的實(shí)現(xiàn)文件里。其重要之處:這是唯一能聲明實(shí)例變量的分類,而且此分類沒有特定的實(shí)現(xiàn)文件,其中的方法都應(yīng)該定義在類的主實(shí)現(xiàn)文件里。
@interface ZSCManager () {
NSString *_name;
}
@end
這樣做有什么好處呢?
公共接口里本來就能定義實(shí)例變量。不過,把它們定義在“class-continuation分類”或者“實(shí)現(xiàn)塊”中可以將其隱藏起來,只供本類使用。
28、通過協(xié)議提供匿名對(duì)象
- 協(xié)議定義了一系列方法,遵從此協(xié)議的對(duì)象應(yīng)該實(shí)現(xiàn)它們(如果這些方法不是可選的,那么就必須實(shí)現(xiàn))。
我們可以用協(xié)議把自己所寫的API之中的實(shí)現(xiàn)細(xì)節(jié)隱藏起來,將返回的對(duì)象設(shè)計(jì)為遵從此協(xié)議的純 id 類型。這樣,想要隱藏的類名就不會(huì)出現(xiàn)在API之中了。若是接口背后有很多個(gè)不同的實(shí)現(xiàn)類,而你又不想指明具體使用哪個(gè)類,就可以考慮用這個(gè)方法。
因?yàn)橛袝r(shí)候這些類可能會(huì)變,有時(shí)候它們無法容納于標(biāo)準(zhǔn)的類繼承體系中,因而不能以某個(gè)公共基類來統(tǒng)一表示。
例子:
#import <Foundation/Foundation.h>
@interface ZSCShare : NSObject
+ (instancetype)shareInstance;
/**通過協(xié)議提供匿名對(duì)象,返回的具體不知道是什么類型,我們不關(guān)注,只要是分享的就行**/
- (id)createShareWithName:(NSString *)name;
@end
//.m的實(shí)現(xiàn)
#import "ZSCShare.h"
@implementation ZSCShare
static id instance;
+ (instancetype)shareInstance {
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
instance = [[ZSCShare alloc] init];
});
return instance;
}
- (id)createShareWithName:(NSString *)name {
return [NSString stringWithFormat:@"分享了 %@",name];
}
@end
- 調(diào)用時(shí)候就會(huì)顯示下面這樣,達(dá)到了我們的目的。

協(xié)議匿名返回.png
接下來也將會(huì)繼續(xù)整理。如果覺得有用請(qǐng)點(diǎn)個(gè)喜歡!
您的支持將是我繼續(xù)寫作的動(dòng)力!謝謝。
觀“編寫高質(zhì)量iOS與OC X代碼的52個(gè)有效方法”有感(一)· 熟悉Objective-C
觀“編寫高質(zhì)量iOS與OC X代碼的52個(gè)有效方法”有感(二)· 對(duì)象、消息、運(yùn)行時(shí)
觀“編寫高質(zhì)量iOS與OC X代碼的52個(gè)有效方法”有感(三)· 接口與API設(shè)計(jì)
觀“編寫高質(zhì)量iOS與OC X代碼的52個(gè)有效方法”有感(四)· 協(xié)議與分類