淺談iOS代理

相信提起代理(delegate),無論你是否剛步入iOS的編程世界,應(yīng)該一定都會聽說過它,我們經(jīng)常會使用到代理(delegate)的設(shè)計模式楚里,這是iOS中一種常用的消息傳遞的方式归露,也可以通過這種方式來傳遞一些參數(shù)。這篇文章會涵蓋代理的使用技巧和原理性置,以及代理的內(nèi)存管理等方面的知識。

那么究竟什么是代理模式呢? 舉個在我們iOS行業(yè)中經(jīng)典形象的例子來,以便大家能夠更好的理解代理模式的含義:
有個嬰兒(是男是女就不要去糾結(jié)了~~~)魂奥,baby不會自己吃飯和洗澡等等做一些事情终抽,于是baby的Mommy就請了一個保姆势木,于是baby和保姆之間有了一個協(xié)議合同蛛倦,協(xié)議合同中寫明了保姆需要做什么事情, 而保姆就是要去完成這個協(xié)議中規(guī)定要做的事的代理人
即:baby和保姆之間有個協(xié)議跟压,保姆遵守該協(xié)議胰蝠,于是保姆就需要實現(xiàn)該協(xié)議中的條款成為baby代理人。

說白了,代理的作用大家可以簡單粗暴的理解為:"自己做不了的事情,就去雇傭一個可以做這些事的人,交給他去做!"

<h2>iOS中消息傳遞方式</h2>

在iOS中有很多種消息傳遞方式震蒋,首先簡單了解一下常見的消息傳遞的幾種方式茸塞。

1.通知(notification):在iOS中由通知中心進行消息接收和消息廣播,是一種一對多的消息傳遞方式查剖。(使用后需要移除通知)
2.代理(delegate):是一種通用的設(shè)計模式钾虐,iOS中對代理支持的很好,由代理對象笋庄、委托者效扫、協(xié)議三部分組成。
3.block:在iOS 4.0中開始引入的一種回調(diào)方法直砂,可以將回調(diào)處理代碼直接寫在block代碼塊中菌仁,看起來邏輯清晰代碼整齊。
4.target action:通過將對象傳遞到另一個類中静暂,在另一個類中將該對象當(dāng)做target的方式济丘,來調(diào)用該對象方法,從內(nèi)存角度來說和代理類似洽蛀。
5.KVO:NSObject的Category(分類)-NSKeyValueObserving摹迷,通過對屬性進行監(jiān)聽的方式來監(jiān)測某個值的變化,當(dāng)值發(fā)生變化時調(diào)用KVO的回調(diào)方法郊供。

<h2>代理的基本使用</h2>
代理是一種通用的設(shè)計模式峡碉,在iOS中有特定的語法來實現(xiàn)代理模式,OC語言可以通過@Protocol實現(xiàn)協(xié)議驮审。
代理主要由三部分組成:
協(xié)議:用來指定代理雙方可以做什么鲫寄,必須做什么。
代理:根據(jù)協(xié)議头岔,完成委托方需要實現(xiàn)的功能(方法)塔拳。
委托:根據(jù)協(xié)議,指定代理去完成什么功能峡竣。

下面讓我用一張圖來闡述一下三方之間的關(guān)系(光看文字太無聊了,別睡!配圖來了~~~~~~):

<h2>協(xié)議(Protocol)的概念</h2>
從上圖中我們可以看到三方之間的關(guān)系靠抑,在實際應(yīng)用中通過協(xié)議來規(guī)定代理雙方的行為,協(xié)議中的內(nèi)容通常情況都是方法列表适掰,當(dāng)然也可以定義屬性.(后面會介紹協(xié)議屬性)
協(xié)議是公共的颂碧,如果只是單單某個類去使用荠列,我們常做的就是寫在某個類中。
如果多個類都是使用同一個協(xié)議载城,這里建議大家創(chuàng)建一個Protocol文件肌似,在這個文件中制定協(xié)議。遵循的協(xié)議可以被繼承诉瓦,

例如:UITableView川队,繼承自UIScrollView,所以也將UIScrollViewDelegate繼承了過來睬澡,這樣我們就可以通過代理方法獲取UITableView偏移量等狀態(tài)參數(shù)固额。
協(xié)議只能定義公用的一套接口,類似于一個約束代理雙方的作用煞聪。但不能提供具體的實現(xiàn)方法斗躏,實現(xiàn)方法需要代理對象(可以理解為接受協(xié)議遵守協(xié)議的代理人)去實現(xiàn)。協(xié)議可以繼承其他協(xié)議昔脯,也可以繼承多個協(xié)議啄糙,在iOS中對象是不支持多繼承的,而協(xié)議是可以多繼承云稚。

協(xié)議有兩個修飾符@optional和@required隧饼,創(chuàng)建一個協(xié)議如果沒有聲明修飾符,默認(rèn)是@required狀態(tài)的静陈。這兩個修飾符只是約定代理是否強制需要遵守協(xié)議桑李,如果@required狀態(tài)的方法代理沒有遵守,Xcode會報一個黃色的警告窿给,@required是需要我們必須實現(xiàn)的。@optional是可以選擇實現(xiàn)的.

下面我們將用一個小例子來講解一下這個問題:

我想給我的愛車清理一下,不過上海的天氣太熱了,不想自己動手.于是去洗車店,委托洗車行幫我把車子清理干凈(指定協(xié)議).然后洗車店的人會幫助我將我的車子洗干凈.

在這個過程當(dāng)中,洗車行就是我的代理.而我就是委托方,我要求洗車行將我的車子清理干凈,這就相當(dāng)于制定了協(xié)議.至于洗車的過程是洗車行去處理,不需要我來操作,我只要付錢給車行,等待車行將我的車子清洗完就好率拒。

這個過程中我付的錢就相當(dāng)于是傳遞的參數(shù),最后洗干凈的車子就是處理的結(jié)果.

在等待車行洗車的過程中,突然間我覺得肚子餓了,于是打給了某家餐館進行委托訂餐,那么我就是委托方,而上面的洗車行無法提供給我,我要的東西.所以這個餐館成為了我的又一個代理.也就是說一個委托方可以有多個代理對象

在iOS中一個代理可以有多個委托方崩泡,而一個委托方也可以有多個代理。我指定了洗車行和餐館兩個代理猬膨,也可以再指定其他等多個代理角撞,委托方也可以為多個代理服務(wù)。

代理對象在很多情況下其實是可以復(fù)用的勃痴,可以創(chuàng)建多個代理對象為多個委托方服務(wù)谒所,在下面將會通過引用一個小例子介紹一下控制器代理的復(fù)用。(因為沒有時間有限,這里直接引用一個網(wǎng)絡(luò)上例子)
下面是一個簡單的代理:
首先定義一個協(xié)議類沛申,來定義公共協(xié)議


定義委托類劣领,這里簡單實現(xiàn)了一個用戶登錄功能,將用戶登錄后的賬號密碼傳遞出去铁材,有代理來處理具體登錄細節(jié)尖淘。

<h2>代理使用原理</h2>
代理實現(xiàn)流程
在iOS中代理的本質(zhì)就是代理對象內(nèi)存的傳遞和操作奕锌,我們在委托類設(shè)置代理對象后,實際上只是用一個id類型的指針將代理對象進行了一個弱引用村生。委托方讓代理方執(zhí)行操作惊暴,實際上是在委托類中向這個id類型指針指向的對象發(fā)送消息,而這個id類型指針指向的對象趁桃,就是代理對象辽话。

代理原理
通過上面這張圖我們發(fā)現(xiàn),其實委托方的代理屬性本質(zhì)上就是代理對象自身卫病,設(shè)置委托代理就是代理屬性指針指向代理對象油啤,相當(dāng)于代理對象只是在委托方中調(diào)用自己的方法,如果方法沒有實現(xiàn)就會導(dǎo)致崩潰忽肛。從崩潰的信息上來看村砂,就可以看出來是代理方?jīng)]有實現(xiàn)協(xié)議中的方法導(dǎo)致的崩潰。
而協(xié)議只是一種語法屹逛,是聲明委托方中的代理屬性可以調(diào)用協(xié)議中聲明的方法础废,而協(xié)議中方法的實現(xiàn)還是有代理方完成,而協(xié)議方和委托方都不知道代理方有沒有完成罕模,也不需要知道怎么完成评腺。
代理內(nèi)存管理
為什么我們在設(shè)置代理屬性要使用weak呢?
我們定義的指針默認(rèn)都是__strong類型的淑掌,而屬性本質(zhì)上也是一個成員變量和set蒿讥、get方法構(gòu)成的,strong類型的指針會造成強引用抛腕,會造成循環(huán)引用芋绸。

由于代理對象使用強引用指針,引用創(chuàng)建的委托方LoginVC對象担敌,并且成為LoginVC的代理摔敛。這就會導(dǎo)致LoginVC的delegate屬性強引用代理對象,導(dǎo)致循環(huán)引用的問題全封,最終兩個對象都無法正常釋放马昙。

簡單理解就是:A引用B,B引用A.

<h2>弱引用</h2>

我們將LoginVC對象的delegate屬性,設(shè)置為弱引用屬性刹悴。這樣在代理對象生命周期存在時行楞,可以正常為我們工作,如果代理對象被釋放土匀,委托方和代理對象都不會因為內(nèi)存釋放導(dǎo)致的Crash子房。
但是,這樣還有點問題恒削,真的不會崩潰嗎池颈?
下面兩種方式都是弱引用代理對象尾序,但是第一種在代理對象被釋放后不會導(dǎo)致崩潰,而第二種會導(dǎo)致崩潰躯砰。

@property (nonatomic, weak) iddelegate;
@property (nonatomic, assign) iddelegate;

weak和assign是一種“非擁有關(guān)系”的指針每币,通過這兩種修飾符修飾的指針變量,都不會改變被引用對象的引用計數(shù)琢歇。但是在一個對象被釋放后兰怠,weak會自動將指針指向nil,而assign則不會李茫。在iOS中揭保,向nil發(fā)送消息時不會導(dǎo)致崩潰的,所以assign就會導(dǎo)致野指針的錯誤unrecognized selector sent to instance魄宏。
所以我們?nèi)绻揎棿韺傩越章拢€是用weak修飾吧,比較安全宠互。

為什么要使用代理對象味榛?
隨著項目越來越復(fù)雜,控制器也隨著業(yè)務(wù)的增加而變得越來越臃腫予跌。對于這種情況搏色,很多人都想到了最近比較火的MVVM設(shè)計模式。但是這種模式學(xué)習(xí)曲線很大不好掌握券册,對于新項目來說可以使用频轿,對于一個已經(jīng)很復(fù)雜的大中型項目,就不太好動框架這層的東西了烁焙。
在項目中用到比較多的控件應(yīng)該就有UITableView了摩窃,有的頁面往往UITableView的處理邏輯很多享郊,這就是導(dǎo)致控制器臃腫的一個很大的原因节榜。對于這種問題粘姜,我們可以考慮給控制器瘦身,通過代理對象的方式給控制器瘦身乞榨。

代理對象
從上面兩張圖可以看出,我們將UITableView的delegate和DataSource單獨拿出來当娱,由一個代理對象類進行控制吃既,只將必須控制器處理的邏輯傳遞給控制器處理。
UITableView的數(shù)據(jù)處理跨细、展示邏輯和簡單的邏輯交互都由代理對象去處理鹦倚,和控制器相關(guān)的邏輯處理傳遞出來,交由控制器來處理冀惭,這樣控制器的工作少了很多震叙,而且耦合度也大大降低了掀鹅。這樣一來,我們只需要將需要處理的工作交由代理對象處理媒楼,并傳入一些參數(shù)即可乐尊。

<h2>代理和block的選擇</h2>
在iOS中的回調(diào)方法有很多,而代理和block功能更加相似划址,都是直接進行回調(diào)扔嵌,那我們應(yīng)該用哪個呢,或者說哪個更好呢夺颤?
其實這兩種消息傳遞的方式痢缎,沒有哪個更好、哪個不好代理更加面相過程世澜,block則更面向結(jié)果独旷。從設(shè)計模式的角度來說,代理更佳面向過程寥裂,而block更佳面向結(jié)果嵌洼。
單從性能上來說,block的性能消耗要大于delegate抚恒,因為block會涉及到棧區(qū)向堆區(qū)拷貝等操作咱台,。而代理只是定義了一個方法列表俭驮,在遵守協(xié)議對象的objc_protocol_list中添加一個節(jié)點回溺,在運行時向遵守協(xié)議的對象發(fā)送消息即可。如何選擇要看情景,和你自己的習(xí)慣了.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末混萝,一起剝皮案震驚了整個濱河市遗遵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逸嘀,老刑警劉巖车要,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異崭倘,居然都是意外死亡翼岁,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門司光,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琅坡,“玉大人,你說我怎么就攤上這事残家∮馨常” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長茴晋。 經(jīng)常有香客問我陪捷,道長,這世上最難降的妖魔是什么诺擅? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任市袖,我火速辦了婚禮,結(jié)果婚禮上掀虎,老公的妹妹穿的比我還像新娘凌盯。我一直安慰自己,他們只是感情好烹玉,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布驰怎。 她就那樣靜靜地躺著,像睡著了一般二打。 火紅的嫁衣襯著肌膚如雪县忌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天继效,我揣著相機與錄音症杏,去河邊找鬼。 笑死瑞信,一個胖子當(dāng)著我的面吹牛厉颤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播凡简,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼逼友,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了秤涩?” 一聲冷哼從身側(cè)響起帜乞,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎筐眷,沒想到半個月后黎烈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡匀谣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年照棋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片武翎。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡必怜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出后频,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布卑惜,位于F島的核電站膏执,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏露久。R本人自食惡果不足惜更米,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望毫痕。 院中可真熱鬧征峦,春花似錦、人聲如沸消请。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽臊泰。三九已至蛉加,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缸逃,已是汗流浹背针饥。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留需频,地道東北人丁眼。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像昭殉,于是被迫代替她去往敵國和親苞七。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

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