功能介绍:
用于解决项目中,代理只能指向一个对象的问题,在某些封装性高的类中,会使用self.delegate = self;来实现功能细节,但是给外部使用后就不能再监听到代理事件,代理在这种场景就会变得很难用。
实现思路
初步思路
- 继承宿主(UITextView),并增加delegates容器属性,用来储存需要监听的代理对象
- 将需要监听的对象,加入delegates容器
- 将宿主的delegate指向自身,每个代理事件内部都遍历delegates,通知事件到每个监听对象
- 宿主释放时,将delegates容器清空。
优化
- delegates用NSPointArray,实现弱引用,避免对其它对象造成影响。
缺点
使用方式上需要使用[textView.delegates addObject:obj]; 的方式,无法做到无感知使用。
QMUIMultipleDelegates
类目介绍.png两个实现类,不到350行代码。
NSObject+QMUIMultipleDelegates
@interface NSObject (QMUIMultipleDelegates)
/// 当你需要当前的 class 支持多个 delegate,请将此属性置为 YES。默认为 NO。
@property(nonatomic, assign) BOOL qmui_multipleDelegatesEnabled;
/// 让某个 delegate 属性也支持多 delegate 模式(默认只帮你加了 @selector(delegate) 的支持,如果有其他命名的 property 就需要自己用这个方法添加)
- (void)qmui_registerDelegateSelector:(SEL)getter;
/// 移除某个特定的 delegate 对象,例如假设你把 delegate 同时赋值给 objA 和 objB,而你只要移除 objB,则可:[self qmui_removeDelegate:objB]。但如果你想同时移除 objA 和 objB(也即全部 delegate),则像往常一样直接 self.delegate = nil 即可。
- (void)qmui_removeDelegate:(id)delegate;
@end
为避免侵入性,使用了qmui_multipleDelegatesEnabled开关来控制模式开启,另外考虑delegate的命名各异,增加了- (void)qmui_registerDelegateSelector:(SEL)getter;方便用户主动注册代理,比如tableView的dateSource代理就需要主动注册,此处QMUI自动注册了@selector(delegate) ,所以就不需要手动再注册了。
逻辑
- setQmui_multipleDelegatesEnabled 用于初始化一个字典容器,其中key为当前代理属性的变量名,value为QMUIMultipleDelegates对象,再往下看还会看到已经帮我们注册了delegate方法,并且如果是TableView或者CollectionView的话还会注册DataSource方法。
- 当执行self.delegate = obj 时,会用delegate为key寻找QMUIMultipleDelegates对象,如果对象不存在那么初始化QMUIMultipleDelegates对象,其内部是一个数组容器,此处先忽略,后续继续介绍。
以下为字典容器的添加对象逻辑
// 为这个 selector 创建一个 QMUIMultipleDelegates 容器
NSString *delegateGetterKey = NSStringFromSelector(getter);//此处的getter就是self.delegate里的delegate。
if (!self.qmuimd_delegates[delegateGetterKey]) { //字典容器未获取到value
objc_property_t prop = class_getProperty(self.class, delegateGetterKey.UTF8String);
QMUIPropertyDescriptor *property = [QMUIPropertyDescriptor descriptorWithProperty:prop];//获取属性详情(如:self.delegate)
if (property.isStrong) { //如果self.delegate是强引用,那么创建一个强引用数组来保存对象。此处不知道应用场景,代理一般都写成weak形式的。
// strong property
QMUIMultipleDelegates *strongDelegates = [QMUIMultipleDelegates strongDelegates];
strongDelegates.parentObject = self;
self.qmuimd_delegates[delegateGetterKey] = strongDelegates;
} else {
// weak property
QMUIMultipleDelegates *weakDelegates = [QMUIMultipleDelegates weakDelegates];
weakDelegates.parentObject = self;
self.qmuimd_delegates[delegateGetterKey] = weakDelegates;
}
}
网友评论