iOS開發(fā)基礎(chǔ):協(xié)議、代理阱表、block

一殿如、協(xié)議和代理

首先如果要學(xué)習(xí)協(xié)議和代理,要先了解一下什么是協(xié)議和代理

協(xié)議(protocol)

協(xié)議就是定義一個(gè)需要完成任務(wù)(函數(shù))的公用接口最爬,因?yàn)镺bjective - C語言沒有多繼承涉馁,所以很多時(shí)候都是用Protocol(協(xié)議)來代替。比如:你要寫一個(gè)程序里面包括優(yōu)秀學(xué)生爱致,優(yōu)秀三好學(xué)生烤送,普通學(xué)生三個(gè)類。他們都應(yīng)該繼承學(xué)生類糠悯,但是優(yōu)秀學(xué)生和優(yōu)秀三好學(xué)生 都有一個(gè)相似的部分都是優(yōu)秀學(xué)生帮坚,但是因?yàn)镺bjective - C 沒有多繼承,所以就會(huì)用到Protocol(協(xié)議)來定義一套公用的接口互艾,代替多繼承试和。當(dāng)然協(xié)議還有另一種用法,接下來我們將會(huì)講到

定義一套公用的接口(Public)

@required:必須實(shí)現(xiàn)的方法忘朝,默認(rèn)在@protocol里的方法都要求實(shí)現(xiàn)灰署。

@optional:可選實(shí)現(xiàn)的方法(可以全部都不實(shí)現(xiàn))

比如常用的 UITableViewDelegate 就是一個(gè)協(xié)議判帮,遵守這個(gè)協(xié)議必須實(shí)現(xiàn)UI TabViewDelegate的方法

代理
委托代理是指給一個(gè)對象提供一個(gè)機(jī)會(huì)局嘁,對另一個(gè)對象中的變化做出反應(yīng)溉箕,或者響應(yīng)另一個(gè)對象的行為。它的基本思想是兩個(gè)對象協(xié)同解決問題悦昵。
一般在代碼開發(fā)過程中肴茄,View層一本不會(huì)直接操作函數(shù),但會(huì)接受一些時(shí)間但指,比如點(diǎn)擊事件寡痰,這種事件需要拿到controller里去處理,而完成這個(gè)讓Controller響應(yīng)View事件的操作就可以用代理來完成棋凳。

一般代理和協(xié)議都是聯(lián)合使用的:代理讓‘別人去替你做事情 ’拦坠,‘事情’用協(xié)議聲明

接下來講解一下代理的用法:

先聲明一個(gè)協(xié)議(事情)

@protocol ProtocolDelegate <NSObject>

// 必須實(shí)現(xiàn)的方法
@required
- (void)error;

// 可選實(shí)現(xiàn)的方法
@optional
- (void)other;
- (void)other2;
- (void)other3;

@end

@interface ViewControllerB : UIViewController

// 委托代理人,代理一般需使用弱引用(weak)
@property (weak, nonatomic) id<ProtocolDelegate> delegate;

@end

接下來剩岳,作為當(dāng)事人贞滨,在讓別人去替你做事的時(shí)候,你需要告訴別人什么時(shí)候做:

// 在ViewControllerB的.m文件里
- (void)backAction:(id)sender
{
// 協(xié)議是否響應(yīng)了error方法 (看看代理人是不是準(zhǔn)備好要做事情了)
    if ([_delegate respondsToSelector:@selector(error)]) { 
        [_delegate error]; // 告訴別人 你可以做事情了
    }}

@end

然后代理人需要做什么準(zhǔn)備呢拍棕?

#import "ViewController.h"
#import "ViewControllerB.h"

@interface ViewController () <ProtocolDelegate> //  聲明協(xié)議(如果你想替別人做事晓铆,需要先遵守這個(gè)合同)

@end

@implementation ViewController

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    ViewControllerB *vc = segue.destinationViewController;
    [vc setDelegate:self]; // 確定自己(ViewControllerB)的代理人(self / ViewController);
}

// 當(dāng)自己自己(ViewControllerB)需要實(shí)現(xiàn)error方法時(shí) 調(diào)用此方法
- (void)error
{
    
}
@end

二绰播、block:

block的代理的要完成目標(biāo)相似骄噪,都是讓別人去替完成任務(wù)。那就直接上代碼

//BViewController.h
#import <UIKit/UIKit.h>

typedef void(^CallBackBlcok) (NSString *text);//1

@interface BViewController : UIViewController

// 在自己頁面聲明一個(gè)block
@property (nonatomic,copy) CallBackBlcok callBackBlock;//2
@end

在這里蠢箩,代碼 1 用 typedef 定義了void(^) (NSString *text)的別名為 CallBackBlcok链蕊。這樣我們就可以在代碼 2 中,使用這個(gè)別名定義一個(gè) Block 類型的變量callBackBlock谬泌。

在定義了callBackBlock之后示弓,我們可以在 B 中的點(diǎn)擊事件中添加callBackBlock的傳參操作

//BViewController.m

// 然后在.m 決定什么時(shí)候替你完成‘事’
- (IBAction)click:(id)sender {
    self.callBackBlock(_textField.text); //1
}

這樣我們就可以在想要獲取數(shù)據(jù)回調(diào)的地方,也就 A 的視圖中調(diào)用 block:

// AViewController.m
- (IBAction)push:(id)sender {
    BViewController *bVC = [self.storyboard instantiateViewControllerWithIdentifier:@"BViewController"];
    
    bVC.callBackBlock = ^(NSString *text){   // 1
        
        NSLog(@"text is %@",text);
        
        self.label.text = text;
        
    };
    [self.navigationController pushViewController:bVC animated:YES];
}

代碼 1 中呵萨,通過對回調(diào)將 B 中的數(shù)據(jù)傳遞到代碼塊中奏属,并賦值給 A中的 label,實(shí)現(xiàn)了整個(gè)回調(diào)過程潮峦。

上例是通過將 block 直接賦值給 block 屬性囱皿,也可以通過方法參數(shù)的方式傳遞 block 塊。

不過上面的代碼有問題:

有人問了忱嘹,兄弟嘱腥,有錯(cuò)誤的代碼還粘?我要回答了拘悦,兄弟 我之前也是被這個(gè)代碼騙慘了齿兔,拿出來就是讓大家以你想讓大家深刻記住block的缺點(diǎn),就是block會(huì)循環(huán)引用。

把代碼中的self改成弱引用就可以了:

__weak AViewController *weakSelf = self;
bVC.callBackBlock = ^(NSString *text){

       NSLog(@"text is %@",text);
      // self.label.text = text;
      weakSelf.label.text = text;

};

因?yàn)樯厦娴拇a self.label.text = text;分苇,在 Block 中引用 self 添诉,也就是 A ,而 A 創(chuàng)建并引用了 B 医寿,而 B 引用callBackBlock栏赴,此時(shí)就形成了一個(gè)循環(huán)引用。改成弱引用就好了原因靖秩。

協(xié)議须眷、代理與block基本就到這了

三、區(qū)別

很多同學(xué)可能還會(huì)迷糊代理與block沟突,兩者實(shí)現(xiàn)的功能差不多那么區(qū)別是什么呢花颗?接下來我給把兩者區(qū)別列出來一下:

block 和 delegate 都可以通知外面。block 更輕型惠拭,使用更簡單捎稚,能夠直接訪問上下文,這樣類中不需要存儲(chǔ)臨時(shí)數(shù)據(jù)求橄,使用 block 的代碼通常會(huì)在同一個(gè)地方今野,這樣讀代碼也連貫。delegate 更重一些罐农,需要實(shí)現(xiàn)接口条霜,它的方法分離開來,很多時(shí)候需要存儲(chǔ)一些臨時(shí)數(shù)據(jù)涵亏,另外相關(guān)的代碼會(huì)被分離到各處宰睡,沒有 block 好讀。

應(yīng)該優(yōu)先使用 block气筋。而有兩個(gè)情況可以考慮 delegate拆内。

1.有多個(gè)相關(guān)方法。假如每個(gè)方法都設(shè)置一個(gè) block, 這樣會(huì)更麻煩宠默。而 delegate 讓多個(gè)方法分成一組麸恍,只需要設(shè)置一次,就可以多次回調(diào)。當(dāng)多于 3 個(gè)方法時(shí)就應(yīng)該優(yōu)先采用 delegate。

比如一個(gè)網(wǎng)絡(luò)類洋闽,假如只有成功和失敗兩種情況,每個(gè)方法可以設(shè)計(jì)成單獨(dú) block融欧。但假如存在多個(gè)方法,比如有成功卦羡、失敗噪馏、緩存麦到、https 驗(yàn)證,網(wǎng)絡(luò)進(jìn)度等等欠肾,這種情況下瓶颠,delegate 就要比 block 要好。

在 swift 中董济,利用 enum, 多個(gè)方法也可以合并成一個(gè) block 接口。swift 中的枚舉根據(jù)情況不同要门,可以關(guān)聯(lián)不同數(shù)據(jù)類型虏肾。而在 objc 就不建議這樣做,objc 這種情況下欢搜,額外數(shù)據(jù)需要使用 NSObject 或者 字典進(jìn)行強(qiáng)轉(zhuǎn)封豪,接口就不夠安全。

2.為了避免循環(huán)引用炒瘟,也可以使用 delegate吹埠。使用 block 時(shí)稍微不注意就形成循環(huán)引用,導(dǎo)致對象釋放不了疮装。這種循環(huán)引用缘琅,一旦出現(xiàn)就比較難檢查出來。而 delegate 的方法是分離開的廓推,并不會(huì)引用上下文刷袍,因此會(huì)更安全些。

假如寫一個(gè)庫供他人使用樊展,不清楚使用者的水平如何呻纹。這時(shí)為防止誤用,寧愿麻煩一些专缠,笨一些雷酪,使用 delegate 來替代 block。

將 block 簡單分類涝婉,有三種情形哥力。

  • 臨時(shí)性的,只用在棧當(dāng)中墩弯,不會(huì)存儲(chǔ)起來省骂。

比如數(shù)組的 foreach 遍歷,這個(gè)遍歷用到的 block 是臨時(shí)的最住,不會(huì)存儲(chǔ)起來钞澳。

  • 需要存儲(chǔ)起來,但只會(huì)調(diào)用一次涨缚,或者有一個(gè)完成時(shí)期轧粟。

比如一個(gè) UIView 的動(dòng)畫策治,動(dòng)畫完成之后,需要使用 block 通知外面兰吟,一旦調(diào)用 block 之后通惫,這個(gè) block 就可以刪掉。

  • 需要存儲(chǔ)起來混蔼,可能會(huì)調(diào)用多次履腋。

比如按鈕的點(diǎn)擊事件,假如采用 block 實(shí)現(xiàn)惭嚣,這種 block 就需要長期存儲(chǔ)遵湖,并且會(huì)調(diào)用多次。調(diào)用之后晚吞,block 也不可以刪除延旧,可能還有下一次按鈕的點(diǎn)擊。

對于臨時(shí)性的槽地,只在棧中使用的 block, 沒有循環(huán)引用問題迁沫,block 會(huì)自動(dòng)釋放。而只調(diào)用一次的 block捌蚊,需要看內(nèi)部的實(shí)現(xiàn)集畅,正確的實(shí)現(xiàn)應(yīng)該是 block 調(diào)用之后,馬上賦值為空缅糟,這樣 block 也會(huì)釋放牡整,同樣不會(huì)循環(huán)引用。

而多次調(diào)用時(shí)溺拱,block 需要長期存儲(chǔ)逃贝,就很容易出現(xiàn)循環(huán)引用問題。

Cocoa 中的 API 設(shè)計(jì)也是這樣的迫摔,臨時(shí)性的沐扳,只會(huì)調(diào)用一次的,采用 block句占。而多次調(diào)用的沪摄,并不會(huì)使用 block。比如按鈕事件纱烘,就使用 target-action杨拐。有些庫將按鈕事件從 target-action 封裝成 block 接口, 反而容易出問題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末擂啥,一起剝皮案震驚了整個(gè)濱河市哄陶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哺壶,老刑警劉巖屋吨,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜒谤,死亡現(xiàn)場離奇詭異,居然都是意外死亡至扰,警方通過查閱死者的電腦和手機(jī)鳍徽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來敢课,“玉大人阶祭,你說我怎么就攤上這事≈备眩” “怎么了濒募?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長切厘。 經(jīng)常有香客問我萨咳,道長懊缺,這世上最難降的妖魔是什么疫稿? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮鹃两,結(jié)果婚禮上遗座,老公的妹妹穿的比我還像新娘。我一直安慰自己俊扳,他們只是感情好途蒋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著馋记,像睡著了一般号坡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上梯醒,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天宽堆,我揣著相機(jī)與錄音,去河邊找鬼茸习。 笑死畜隶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的号胚。 我是一名探鬼主播籽慢,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼猫胁!你這毒婦竟也來了箱亿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤弃秆,失蹤者是張志新(化名)和其女友劉穎极景,沒想到半個(gè)月后察净,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盼樟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年氢卡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晨缴。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡译秦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出击碗,到底是詐尸還是另有隱情筑悴,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布稍途,位于F島的核電站阁吝,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏械拍。R本人自食惡果不足惜突勇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坷虑。 院中可真熱鬧甲馋,春花似錦、人聲如沸迄损。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽芹敌。三九已至痊远,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間氏捞,已是汗流浹背碧聪。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留幌衣,地道東北人矾削。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像豁护,于是被迫代替她去往敵國和親哼凯。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355