1. 程式人生 > IOS開發 >iOS delegate使用及原理實現

iOS delegate使用及原理實現

概述

蘋果官方的解釋:

Delegation is a simple and powerful pattern in which one object in a program acts on behalf of,or in coordination with,another object. The delegating object keeps a reference to the other object—the delegate—and at the appropriate time sends a message to it. The message informs the delegate of an event that the delegating object is about to handle or has just handled. The delegate may respond to the message by updating the appearance or state of itself or other objects in the application,and in some cases it can return a value that affects how an impending event is handled. The main value of delegation is that it allows you to easily customize the behavior of several objects in one central object.

其大意是:代理是一種簡單而強大的模式,委託方持有代理物件的引用,並對其傳送訊息,代理方接收訊息並處理返回結果。其主要價值在於,能實現一個物件中實現多個物件的功能,類似“多繼承”。

代理模式是通用的設計模式,Cocoa框架中大量使用了這種模式來實現資料和UI的分離,如UITableViewUIApplicationDelegate等,其主要有三部分組成:

  • 協議,用來指定代理雙方可以做什麼,必須做什麼;
  • 代理方,根據指定的協議,完成委託方需要實現的功能;
  • 委託方,根據指定的協議,指定代理去完成什麼功能;

直觀的關係圖如下(借樓):

代理關係圖

使用

下面將重點闡述“協議”的概念及使用;

Protocol協議

Protocol協議類似java中的介面或者c++中的純虛擬函式,只提供介面不提供實現,Î不同於c++中的純虛擬函式,其不存在類繼承關係,但遵循協議繼承。

@protocol FSSubDelegate <NSObject,FSDelegate>

@required
//methodList or protertyList
@optional
//methodList or protertyList

@end
複製程式碼

協議中存在兩個修飾符@required@optional,預設為@required,修飾符指示遵循協議下的方法或者屬性是否必須要實現.

協議需要繼承“基協議”NSObject

(不同於NSObject基類),其中規定了一些所有基類都需要實現的基本方法和屬性,比如isEqual:isKindOfClassrespondsToSelector等,記憶體管理的方法retainreleaseautoreleaseretainCount等,這樣就定義了一個相對統一的介面和 OC 物件都可以響應的方法。OC物件是不支援多繼承的,但協議可以多繼承。

注意:協議中是可以新增屬性,只是在代理模式中很少使用,但類別category是無法新增屬性,除非使用關聯物件。

協議中常用的繼承自NSObject基協議的方法如下:

- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
- (BOOL)respondsToSelector:(SEL)aSelector;
複製程式碼

由於協議是“類無關”的,任何類都可以實現定義好的協議,因此可以通過conformsToProtocol:方法來判別某個類是否實現了特定協議;即使協議中指定了@required,代理物件也可以不遵循協議規定不實現協議必須實現的方法(只是編譯警告),可以通過respondsToSelector來判別代理物件是否實現了特定方法,避免執行期崩潰。

對於協議定義的位置,可根據使用情況來決定,如果只是使用在某個類中,可直接定義在類檔案中;若多個類都是用同一個協議,可定義在統一的檔案中。

代理基本使用

以開放中常見的UITableView為例說明,一般的使用如下:

@protocol UITableViewDelegate <NSObject,UIScrollViewDelegate>
...
@end

@protocol UITableViewDataSource <NSObject>
...
@end
  
@interface UITableView
@property (nonatomic,weak,nullable) id <UITableViewDelegate> delegate;
@end

@implementation UIViewController <UITableViewDelegate,UITableViewDataSource>

- (void) viewDidLoad {
	self.tableView.delegate = self;
	self.tableView.dataSource = self;
}

#pragma mark -- UITableViewDelegate
//需要遵循實現的協議方法
#pragma mark -- UITableViewDataSource
////需要遵循實現的協議方法

@end
複製程式碼

基於代理模式的關係圖解釋,其中協議為UITableViewDelegateUITableViewDataSource分別提出了

UITableVeiwCell的顯示、編輯、選擇及其內容、索引、數目等需求;委託方為UITableView持有弱引用的代理物件delegate,通過delegate呼叫協議中的方法並傳遞引數;代理方UIViewController需要實現協議中的方法來完成UITableView中的需求,最終實現UITableView控制元件的內容顯示、編輯、滑動、跳轉等動作。其中委託方中的代理屬性需要為弱引用,避免與代理方迴圈引用而兩個物件無法釋放。

通過上述通用案例說明:委託方可以存在多個代理物件,一個代理物件也可以有多個委託方(如資料來源代理物件可通過分離物件實現,而不指定為UIViewController,以實現鬆耦合)。

原理實現

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;	
  	...
    struct objc_protocol_list *protocols 	OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE;

struct objc_protocol_list {
    struct objc_protocol_list * _Nullable next;
    long count;
    __unsafe_unretained Protocol * _Nullable list[1];
};

struct protocol_t : objc_object {
    const char *mangledName;
    struct protocol_list_t *protocols;
    method_list_t *instanceMethods;
    method_list_t *classMethods;
    method_list_t *optionalInstanceMethods;
    method_list_t *optionalClassMethods;
    property_list_t *instanceProperties;
    uint32_t size;   // sizeof(protocol_t)
    uint32_t flags;
    // Fields below this point are not always present on disk.
    const char **_extendedMethodTypes;
    const char *_demangledName;
    property_list_t *_classProperties;
		//省略一些封裝的便捷 get 方法
  	....
};
複製程式碼

觀察類物件的結構可發現存在struct objc_protocol_list型別的協議連結串列,並且協議結構體中存在屬性資訊(也說明協議中可定義屬性),因此代理物件實現協議方法,即將協議方法新增至類物件中,可通過如下runtime方法獲取協議方法: 具體的類結構圖資訊如下圖所示(借樓):

代理實現原理圖
委託方利用協議修飾代理屬性並指向代理方物件,代理物件實現協議中的方法並新增至類物件協議方法中,委託方利用代理屬性指向的物件相代理方傳送訊息(若存在頻繁的呼叫代理物件協議方法,可直接呼叫代理物件方法method來避免訊息轉發)。

Reference

Cocoa Core Competencies -- Delegation

你真的瞭解iOS代理設計模式嗎?

iOS學習筆記之“偽”多繼承

Objective-C:代理

iOS 編寫高質量Objective-C程式碼(四)—— 協議與分類

iOS開發 - protocol中定義屬性?

Foundation: NSObject Protocol