《52個有效方法》筆記5——協(xié)議與分類的一些細(xì)節(jié)


代理模式的兩種細(xì)分

代理模式也叫委托模式叙赚,我們在這統(tǒng)一一下稱呼老客。A類要委托B類來做某事,則A叫“委托者”震叮,因?yàn)锳委托胧砰、托付B幫它做事;把B叫“代理者”冤荆,因?yàn)锽幫助朴则、代理A完成該事。
在代理模式里钓简,代理者需要實(shí)現(xiàn)定義有關(guān)該“委托之事”規(guī)范的協(xié)議乌妒,說起協(xié)議⊥獾耍可以細(xì)分為“普通代理協(xié)議”和“數(shù)據(jù)源協(xié)議”撤蚊。這個在UITableVIew中體現(xiàn)得很明顯。
** UITableViewDelegate協(xié)議就是普通的代理協(xié)議损话。**
總得來說侦啸,在下面這個方法中,** 數(shù)據(jù)是從“委托者”(UITableView)通過參數(shù)流向“代理者”(一般為ViewController)的丧枪。**

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;

** 而UITableViewDataSource協(xié)議就是所謂的“數(shù)據(jù)源協(xié)議”光涂。**
顧名思義,它為“委托者”(UITableView)提供數(shù)據(jù)拧烦⊥牛總得來說,** 它的數(shù)據(jù)是從“代理者”(當(dāng)前ViewController)以返回值的形式流向“委托者”(UITableView)的恋博。**

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

代理模式里“普通代理協(xié)議”和“數(shù)據(jù)源協(xié)議”有以上數(shù)據(jù)流向不同的區(qū)別齐佳,望細(xì)辨之。


委托者的代理屬性

// YWAlertView.h

#import <UIKit/UIKit.h>
@class YWAlertView;
@protocol YWAlertViewDelegate <NSObject>

- (void)ywalertView:(YWAlertView *)alertView clickBtnIndex:(NSInteger)index;

@end


@interface YWAlertView : UIAlertView

@property (nonatomic, weak)id<YWAlertViewDelegate>      delegate;

@end

我們在自定義的YWAlertView中定義了它的代理者的屬性delegate來保存债沮、持有代理者炼吴。看上面的代碼疫衩,定義委托者的代理者屬性要注意兩點(diǎn):

  • 內(nèi)存管理語義要使用weak硅蹦。因?yàn)榇藭r委托者YWAlertView持有了代理者delegate,而在代理者類ViewController中,一般也要持有YWAlertView對象提针,這樣的話它們兩者互相持有命爬,互相保留,則會形成保留壞辐脖,互不相讓饲宛,都不釋放。這樣就造成了內(nèi)存泄露嗜价。
  • 委托者的代理屬性的類型是id<YWAlertViewDelegate>這在OC中叫 ** 匿名對象 ** 艇抠,沒有指定它的具體得是什么類,一定得是什么類久锥,它的語義是家淤,遵從YWAlertViewDelegate協(xié)議的任何對象。
    ** 注意:** OC中的匿名對象和概念和其他編程語言中的有所不同瑟由,其他編程的匿名對象一般指以內(nèi)聯(lián)方式所創(chuàng)建出來的無名類絮重。要注意區(qū)分,以免混淆歹苦。

細(xì)節(jié)優(yōu)化

若協(xié)議中的代理是可選實(shí)現(xiàn)的青伤,則我們在委托類中調(diào)用代理方法時,則需要判斷代理者是否已經(jīng)實(shí)現(xiàn)該協(xié)議里方法殴瘦,判斷代理者能否響應(yīng)此選擇子狠角。
我們知道有些方法在執(zhí)行過程中多次調(diào)用的,比如NSURLConnection網(wǎng)絡(luò)下載返回?cái)?shù)據(jù)的代理方法是多次調(diào)用蚪腋,網(wǎng)絡(luò)數(shù)據(jù)是一段一段下載而來的丰歌。又比如下面UITextFieldDelegate里的方法,也是多次調(diào)用的屉凯。

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;   

像下面我們自定義的YWTextfield立帖,它是基于UITextfield而建的,在YWTextField中UITextfieldDelegate的值發(fā)生改變時就調(diào)用我們協(xié)議定義的方法悠砚,所以該協(xié)議方法會被調(diào)用多次晓勇。

// YWTextfield.h

#import <UIKit/UIKit.h>
@class YWTextfield;
@protocol YWTextfieldDelegate <NSObject>

- (void)ywtextfield:(YWTextfield *)textfield textValueIsChanging:(NSString *)text;

@end

@interface YWTextfield : UITextField

@property (nonatomic, weak)id<YWTextfieldDelegate>      delegate;

@end
// YWTextfield.m

#import "YWTextfield.h"

@interface YWTextfield ()<UITextFieldDelegate>

@end

@implementation YWTextfield


- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    if([self.delegate respondsToSelector:@selector(ywtextfield:textValueIsChanging:)])
    {
        [self.delegate ywtextfield:self textValueIsChanging:string];
        return YES;
    }
    
    return NO;
}

@end

其實(shí)每次調(diào)用前通過respondsToSelector來檢測代理者能否響應(yīng),是完全沒必要的哩簿,因?yàn)槿舸碚弑旧頉]變,它不太可能突然不能響應(yīng)原本可以響應(yīng)的選擇子酝静。所以节榜,這兒有可優(yōu)化的可能性。比較好的方案是 ** 把代理者能否響應(yīng)該協(xié)議方法這一信息緩存起來别智,以后每次只通過該緩存判斷該協(xié)議方法是否被實(shí)現(xiàn)宗苍。**
像下面的例子,我們重寫代理者屬性ywDelegate的setter方法,在setter方法中通過``respondsToSelector```判斷一次代理者是否實(shí)現(xiàn)該協(xié)議方法讳窟,然后把結(jié)果緩存在“段位”變量_ywdelegateFlags中让歼。這樣當(dāng)我們設(shè)置某類為委托類的代理時,就已經(jīng)開始判斷它是否實(shí)現(xiàn)協(xié)議方法丽啡,且把結(jié)果緩存下來了谋右。

// YWTextfield.m

#import "YWTextfield.h"

// C中的“段位”
struct
{
    unsigned int ywtextfieldStartEdit   :1;
    unsigned int valueIsChanging        :1; 
// 表示占用一個字節(jié),因此可以代表1 or 0 兩個值
}_ywdelegateFlags;

@interface YWTextfield ()<YWTextfieldDelegate>

@end

@implementation YWTextfield


- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    if(_ywdelegateFlags.valueIsChanging)
    {
        [self.ywDelegate ywtextfield:self textValueIsChanging:string];
        return YES;
    }
    
    return NO;
}


#pragma mark ---- setter
// 重寫ywDelegate屬性的setter方法补箍,在內(nèi)把代理者是否實(shí)現(xiàn)該協(xié)議方法這一信息緩存下來
- (void)setYwDelegate:(id<YWTextfieldDelegate>)ywDelegate
{
    _ywDelegate = ywDelegate;
    _ywdelegateFlags.valueIsChanging = [ywDelegate respondsToSelector:@selector(ywtextfield:textValueIsChanging:)];
}

@end

** 注意:** 此處用來緩存代理者能否響應(yīng)該選擇子這一信息的數(shù)據(jù)結(jié)構(gòu)叫“段位”改执。


關(guān)于分類的一些細(xì)節(jié)

OC有分類,當(dāng)我們想給某類添加新方法時坑雅,我們可以為該類定義分類辈挂,將新方法定義在里面,而不需要定義一個繼承于該類的子類裹粤。
** 這么說來终蒂,OC中為某類添加新方法有兩種方案,其一為繼承該類定義子類遥诉,在子類添加拇泣;其二則為添加分類,在分類中定義新方法突那。**
當(dāng)一個類很龐大時挫酿,我們可以根據(jù)功能模塊的不同,而將方法分散到幾個分類中愕难,這樣便于管理早龟。比如,我們把可以像下面這樣拆分Person類

// Person主類

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, copy)NSString     *personId;
@property (nonatomic, copy)NSString     *name;
@property (nonatomic, strong)NSArray    *friends;

@end
// "工作"某塊
@interface Person (Work)

- (void)goToCode;
- (void)writeBlog;

@end
// "好友"模塊
@interface Person (FriendShip)

- (void)addFriend:(Person *)person;
- (void)removeFriend:(Person *)person;

@end

** 注意:我們把“好友”這一模塊拆分出來建立了FriendShip分類猫缭,指的是把有關(guān)好友的“方法們”拆分出來了葱弟,你可別把friends這個屬性也拆分進(jìn)FriendShip分類中。若把屬性聲明放在了分類中猜丹,編譯時會給出警告芝加。
要記住: 把所有的成員變量好屬性聲明都應(yīng)寫在主類中射窒。**

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末藏杖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子脉顿,更是在濱河造成了極大的恐慌蝌麸,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件艾疟,死亡現(xiàn)場離奇詭異来吩,居然都是意外死亡敢辩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門弟疆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來戚长,“玉大人,你說我怎么就攤上這事怠苔⊥” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵嘀略,是天一觀的道長恤溶。 經(jīng)常有香客問我,道長帜羊,這世上最難降的妖魔是什么咒程? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮讼育,結(jié)果婚禮上帐姻,老公的妹妹穿的比我還像新娘。我一直安慰自己奶段,他們只是感情好饥瓷,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著痹籍,像睡著了一般呢铆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蹲缠,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天棺克,我揣著相機(jī)與錄音,去河邊找鬼线定。 笑死娜谊,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的斤讥。 我是一名探鬼主播纱皆,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼芭商!你這毒婦竟也來了派草?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤铛楣,失蹤者是張志新(化名)和其女友劉穎近迁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛉艾,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钳踊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了勿侯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拓瞪。...
    茶點(diǎn)故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖助琐,靈堂內(nèi)的尸體忽然破棺而出祭埂,到底是詐尸還是另有隱情,我是刑警寧澤兵钮,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布蛆橡,位于F島的核電站,受9級特大地震影響掘譬,放射性物質(zhì)發(fā)生泄漏泰演。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一葱轩、第九天 我趴在偏房一處隱蔽的房頂上張望睦焕。 院中可真熱鬧,春花似錦靴拱、人聲如沸垃喊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽本谜。三九已至,卻和暖如春偎窘,著一層夾襖步出監(jiān)牢的瞬間乌助,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工评架, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留眷茁,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓纵诞,卻偏偏與公主長得像上祈,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子浙芙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容