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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345