淺談iOS代理

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

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

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

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

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

1.通知(notification):在iOS中由通知中心進行消息接收和消息廣播,是一種一對多的消息傳遞方式。(使用后需要移除通知)
2.代理(delegate):是一種通用的設計模式,iOS中對代理支持的很好,由代理對象、委托者、協議三部分組成。
3.block:在iOS 4.0中開始引入的一種回調方法,可以將回調處理代碼直接寫在block代碼塊中,看起來邏輯清晰代碼整齊。
4.target action:通過將對象傳遞到另一個類中,在另一個類中將該對象當做target的方式,來調用該對象方法,從內存角度來說和代理類似。
5.KVO:NSObject的Category(分類)-NSKeyValueObserving,通過對屬性進行監聽的方式來監測某個值的變化,當值發生變化時調用KVO的回調方法。

<h2>代理的基本使用</h2>
代理是一種通用的設計模式,在iOS中有特定的語法來實現代理模式,OC語言可以通過@Protocol實現協議。
代理主要由三部分組成:
協議:用來指定代理雙方可以做什么,必須做什么。
代理:根據協議,完成委托方需要實現的功能(方法)。
委托:根據協議,指定代理去完成什么功能。

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

<h2>協議(Protocol)的概念</h2>
從上圖中我們可以看到三方之間的關系,在實際應用中通過協議來規定代理雙方的行為,協議中的內容通常情況都是方法列表,當然也可以定義屬性.(后面會介紹協議屬性)
協議是公共的,如果只是單單某個類去使用,我們常做的就是寫在某個類中。
如果多個類都是使用同一個協議,這里建議大家創建一個Protocol文件,在這個文件中制定協議。遵循的協議可以被繼承,

例如:UITableView,繼承自UIScrollView,所以也將UIScrollViewDelegate繼承了過來,這樣我們就可以通過代理方法獲取UITableView偏移量等狀態參數。
協議只能定義公用的一套接口,類似于一個約束代理雙方的作用。但不能提供具體的實現方法,實現方法需要代理對象(可以理解為接受協議遵守協議的代理人)去實現。協議可以繼承其他協議,也可以繼承多個協議,在iOS中對象是不支持多繼承的,而協議是可以多繼承。

協議有兩個修飾符@optional和@required,創建一個協議如果沒有聲明修飾符,默認是@required狀態的。這兩個修飾符只是約定代理是否強制需要遵守協議,如果@required狀態的方法代理沒有遵守,Xcode會報一個黃色的警告,@required是需要我們必須實現的。@optional是可以選擇實現的.

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

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

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

這個過程中我付的錢就相當于是傳遞的參數,最后洗干凈的車子就是處理的結果.

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

在iOS中一個代理可以有多個委托方,而一個委托方也可以有多個代理。我指定了洗車行和餐館兩個代理,也可以再指定其他等多個代理,委托方也可以為多個代理服務。

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


定義委托類,這里簡單實現了一個用戶登錄功能,將用戶登錄后的賬號密碼傳遞出去,有代理來處理具體登錄細節。

<h2>代理使用原理</h2>
代理實現流程
在iOS中代理的本質就是代理對象內存的傳遞和操作,我們在委托類設置代理對象后,實際上只是用一個id類型的指針將代理對象進行了一個弱引用。委托方讓代理方執行操作,實際上是在委托類中向這個id類型指針指向的對象發送消息,而這個id類型指針指向的對象,就是代理對象。

代理原理
通過上面這張圖我們發現,其實委托方的代理屬性本質上就是代理對象自身,設置委托代理就是代理屬性指針指向代理對象,相當于代理對象只是在委托方中調用自己的方法,如果方法沒有實現就會導致崩潰。從崩潰的信息上來看,就可以看出來是代理方沒有實現協議中的方法導致的崩潰。
而協議只是一種語法,是聲明委托方中的代理屬性可以調用協議中聲明的方法,而協議中方法的實現還是有代理方完成,而協議方和委托方都不知道代理方有沒有完成,也不需要知道怎么完成。
代理內存管理
為什么我們在設置代理屬性要使用weak呢?
我們定義的指針默認都是__strong類型的,而屬性本質上也是一個成員變量和set、get方法構成的,strong類型的指針會造成強引用,會造成循環引用。

由于代理對象使用強引用指針,引用創建的委托方LoginVC對象,并且成為LoginVC的代理。這就會導致LoginVC的delegate屬性強引用代理對象,導致循環引用的問題,最終兩個對象都無法正常釋放。

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

<h2>弱引用</h2>

我們將LoginVC對象的delegate屬性,設置為弱引用屬性。這樣在代理對象生命周期存在時,可以正常為我們工作,如果代理對象被釋放,委托方和代理對象都不會因為內存釋放導致的Crash。
但是,這樣還有點問題,真的不會崩潰嗎?
下面兩種方式都是弱引用代理對象,但是第一種在代理對象被釋放后不會導致崩潰,而第二種會導致崩潰。

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

weak和assign是一種“非擁有關系”的指針,通過這兩種修飾符修飾的指針變量,都不會改變被引用對象的引用計數。但是在一個對象被釋放后,weak會自動將指針指向nil,而assign則不會。在iOS中,向nil發送消息時不會導致崩潰的,所以assign就會導致野指針的錯誤unrecognized selector sent to instance。
所以我們如果修飾代理屬性,還是用weak修飾吧,比較安全。

為什么要使用代理對象?
隨著項目越來越復雜,控制器也隨著業務的增加而變得越來越臃腫。對于這種情況,很多人都想到了最近比較火的MVVM設計模式。但是這種模式學習曲線很大不好掌握,對于新項目來說可以使用,對于一個已經很復雜的大中型項目,就不太好動框架這層的東西了。
在項目中用到比較多的控件應該就有UITableView了,有的頁面往往UITableView的處理邏輯很多,這就是導致控制器臃腫的一個很大的原因。對于這種問題,我們可以考慮給控制器瘦身,通過代理對象的方式給控制器瘦身。

代理對象
從上面兩張圖可以看出,我們將UITableView的delegate和DataSource單獨拿出來,由一個代理對象類進行控制,只將必須控制器處理的邏輯傳遞給控制器處理。
UITableView的數據處理、展示邏輯和簡單的邏輯交互都由代理對象去處理,和控制器相關的邏輯處理傳遞出來,交由控制器來處理,這樣控制器的工作少了很多,而且耦合度也大大降低了。這樣一來,我們只需要將需要處理的工作交由代理對象處理,并傳入一些參數即可。

<h2>代理和block的選擇</h2>
在iOS中的回調方法有很多,而代理和block功能更加相似,都是直接進行回調,那我們應該用哪個呢,或者說哪個更好呢?
其實這兩種消息傳遞的方式,沒有哪個更好、哪個不好代理更加面相過程,block則更面向結果。從設計模式的角度來說,代理更佳面向過程,而block更佳面向結果。
單從性能上來說,block的性能消耗要大于delegate,因為block會涉及到棧區向堆區拷貝等操作,。而代理只是定義了一個方法列表,在遵守協議對象的objc_protocol_list中添加一個節點,在運行時向遵守協議的對象發送消息即可。如何選擇要看情景,和你自己的習慣了.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容