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的分离,如UITableView
、UIApplicationDelegate
等,其主要有三部分组成:
- 协议,用来指定代理双方可以做什么,必须做什么;
- 代理方,根据指定的协议,完成委托方需要实现的功能;
- 委托方,根据指定的协议,指定代理去完成什么功能;
直观的关系图如下(借楼):
代理关系图
使用
下面将重点阐述“协议”的概念及使用;
Protocol协议
Protocol
协议类似java
中的接口或者c++
中的纯虚函数,只提供接口不提供实现,Î不同于c++
中的纯虚函数,其不存在类继承关系,但遵循协议继承。
@protocol FSSubDelegate <NSObject, FSDelegate>
@required
//methodList or protertyList
@optional
//methodList or protertyList
@end
协议中存在两个修饰符@required
和@optional
,默认为@required
,修饰符指示遵循协议下的方法或者属性是否必须要实现.
协议需要继承“基协议”NSObject
(不同于NSObject
基类),其中规定了一些所有基类都需要实现的基本方法和属性,比如isEqual:
、isKindOfClass
、respondsToSelector
等,内存管理的方法retain
、release
、autorelease
、retainCount
等,这样就定义了一个相对统一的接口和 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
基于代理模式的关系图解释,其中协议为UITableViewDelegate
及UITableViewDataSource
分别提出了
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