OC 中多代理的实现

背景

iOS 中有个设计模式叫单例模式,还有个模式叫代理模式,
如果想在单例中添加代理就不能像之前那样直接赋值,
那样只有最后一个代理生效,所以就有了多代理的需求

实现方式

多代理实现的关键就在于如何管理多个代理,代理也是对象,我们可以用数组装载起来,但是直接放在数组会影响代理对象的销毁。
所以这就需要一个弱引用的容器,将多个代理加入到弱引用容器中,在需要通知代理的地方遍历容器,分别去通知各代理。
(关于弱引用容器可以参考我的上篇文章OC中的弱引用容器)

1
Talk is cheap, show me the code. ——Linus Torvalds
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

// ------------ .h文件 ----------

#import <Foundation/Foundation.h>

// 协议
@protocol TestClassProtocol <NSObject>

// 测试协议方法
- (void)testProtocolMethod;

@end

@interface TestClass : NSObject

/**
初始化单例
*/
+ (instancetype)shareCenter;


/**
添加代理

@param delegate 遵循<TestClassProtocol>协议的代理
支持多代理模式,但是要记得移除,否则会造成多次调用
*/
- (void)addDelegate:(id<TestClassProtocol>_Nonnull)delegate;

/**
移除代理

@param delegate 遵循<TestClassProtocol>协议的代理
*/
- (void)removeDelegate:(id<TestClassProtocol>_Nonnull)delegate;

@end


// --------- .m 文件 -------

#import "TestClass.h"

@interface TestClass ()

/**
代理容器
*/
@property (strong, nonatomic) NSPointerArray *delegateContainer;

@end

@implementation TestClass

#pragma mark - 单例相关 -----begin---
/*
创建静态对象 防止外部访问
*/
static TestClass *_center;
/**
重写初始化方法
*/
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (_center == nil) {
_center = [super allocWithZone:zone];
_center.decimalsLimit = -1;
}
});
return _center;
}

/**
初始化单例

@return 数据中心单例
*/
+ (instancetype)shareCenter
{
return [[self alloc]init];
}

/**
重写copyWithZone
*/
-(id)copyWithZone:(NSZone *)zone
{
return _center;
}

/**
重写mutableCopyWithZone
*/
-(id)mutableCopyWithZone:(NSZone *)zone
{
return _center;
}

#pragma mark - 单例相关 ----- end ---

#pragma mark - 代理相关 ----- begin ---

/**
添加代理

@param delegate 遵循<TestClassProtocol>协议的代理
支持多代理模式,但是要记得移除,否则会造成多次调用
*/
- (void)addDelegate:(id<TestClassProtocol>)delegate {
if(delegate) {
for (id tempDelegate in self.delegateContainer) {
if ([tempDelegate isEqual:delegate]) {
// 已经有了这个代理直接return
return;
}
}
// 将代理添加到弱引用容器中
[self.delegateContainer addPointer:(__bridge void * _Nullable)(delegate)];
}

// 自动检测并移除失效的代理
[self.delegateContainer compact];
}

/**
移除代理

@param delegate 遵循<TestClassProtocol>协议的代理
*/
- (void)removeDelegate:(id<TestClassProtocol>)delegate {

if (delegate) {
for (int a = 0 ; a < self.delegateContainer.count ; a ++) {

id tempDelegate = [self.delegateContainer pointerAtIndex:a];

if (tempDelegate && [tempDelegate isEqual:delegate]) {
[self.delegateContainer removePointerAtIndex:a];
break;
}
}
}
// 自动检测并移除失效的代理
[self.delegateContainer compact];
}


/**
代理弱引用容器的懒加载
*/
- (NSPointerArray *)delegateContainer {

if(!_delegateContainer) {
_delegateContainer = [NSPointerArray weakObjectsPointerArray];
}
return _delegateContainer;
}
#pragma mark - 代理相关 ----- end ---

// TODO: 其他业务代码
@end

理论上来说,不用的代理不移除也可以,但是如果有些代理由于一些错误未释放的话,就会造成多次调用的问题。如果能保证释放掉的话是没问题的。