iOS 多播者:一对多模式封装
2021-02-05 本文已影响0人
某非著名程序员
描述:git仓库
iOS delegate、block属于一对一的模式。
有时候需要实现一对多的需求,通知可以实现。但有时候业务关联是很紧密的,通知的可读性不是很好。
项目中目前使用的是GCDMulticastDelegate,发现其有几个缺点:
- GCDMulticastDelegate中都是在消息转发中进行消息调用,消息转发在iOS中代价比较高。
- 其消息调用都是异步提交到队列,导致堆栈信息无法跟踪。
- 库也比较老了,偶现闪退。体验不是很好。
- 使用起来不太方便,需要先定义multiDelegate,添加代理,不用需要移除,使用时还需要判断方法是否存在。
@property(nonatomic, strong) GCDMulticastDelegate <BLEnterpriseManagerDelegate> *multiDelegate;
- (instancetype)init {
if (self = [super init]) {
self.multiDelegate = (GCDMulticastDelegate <BLEnterpriseManagerDelegate> *) [[GCDMulticastDelegate alloc] init];
}
return self;
}
- (void)addDelegate:(id <BLEnterpriseManagerDelegate>)delegate delegateQueue:(dispatch_queue_t)queue {
[_multiDelegate addDelegate:delegate delegateQueue:queue];
}
- (void)removeDelegate:(id <BLEnterpriseManagerDelegate>)delegate {
[_multiDelegate removeDelegate:delegate];
}
if ([self.multiDelegate hasDelegateThatRespondsToSelector:@selector(enterpriseManager:didClearAllOrgs:)]) {
[self.multiDelegate enterpriseManager:self didClearAllOrgs:YES];
}
思考
一对多就是观察者模式的使用,完全可以自己实现。
- 观察者使用封装在了NSObject扩展中,任何对象都可以添加观察者对象。不再需要在被观察者中声明什么。
- 消息调用使用了NSInvocation,不走消息转发。
- 如果不设置队列,默认都回到主线程;也可指定队列。
- 方法不存在,不需要外部判断。内部打印错误日志,注意观察。
方案一
使用
- 导入头文件
#import "NSObject+BLObserver.h" - 添加观察者
[_customView wp_addObserver:self];
_observer1 = [WPViewObserver new];
_observer1.title = @"observer1 ";
[_customView wp_addObserver:_observer1];
_observer2 = [WPViewObserver new];
_observer2.title = @"observer2 ";
[_customView wp_addObserver:_observer2 delegateQueue:dispatch_queue_create("observer2", DISPATCH_QUEUE_SERIAL)];//指定队列
- 被观察者通知观察者:带参数调用
[self wp_notifyObserverWithAction:@selector(update), nil];
[self wp_notifyObserverWithAction:@selector(update:), 1,nil];
[self wp_notifyObserverWithAction:@selector(update:count2:), 1,2,nil];
- 观察者监听
- (void)update:(NSInteger)count{
NSLog(@"detail update %ld",count);
}
- (void)update:(NSInteger)count count2:(NSInteger)count2{
NSLog(@"detail update %ld,%ld",count,count2);
}
- 退出页面后内存自动释放
2021-02-06 23:18:16.655618+0800 WPObserver_Example[58471:640255] WPDetailViewController dealloc
2021-02-06 23:18:16.656013+0800 WPObserver_Example[58471:640255] WPViewObserver dealloc observer2
2021-02-06 23:18:16.656214+0800 WPObserver_Example[58471:640255] WPViewObserver dealloc observer1
2021-02-06 23:18:16.656621+0800 WPObserver_Example[58471:640255] WPView dealloc
小结
- 多数监听的或移除的时候都是在主线程,如果觉得线程不安全,也可以加个信号量。
- 观察者模式使用简单,要考虑到通用性,多参问题。
- 多参要考虑到int,double等基本类型。
- weak不持有观察者,不需要释放。只管监听,和通知观察者。如果有特殊情况,不需要监听了,可以调用[wp_removeObserver],移除监听;单例的监听者,需要手动移除。
方案二
以上方案可以实现通知,调用wp_notifyObserverWithAction:,不支持泛型。
方案二去掉多参,加上线程安全,支持泛型。
使用
- 添加观察者
- (void)addObserver2{
[_customView.proxyObserver addObserver:self];
[_customView.proxyObserver addObserver:_observer1];
[_customView.proxyObserver addObserver:_observer2 delegateQueue:dispatch_queue_create("observer2", DISPATCH_QUEUE_SERIAL)];//指定队列
}
- 通知观察者,支持泛型
[self.proxyObserver notifyObserver:^(id<WPViewObserver> _Nonnull target) {
[target update];
} selector:@selector(update)];
[self.proxyObserver notifyObserver:^(id<WPViewObserver> _Nonnull target) {
[target update:1];
} selector:@selector(update:)];
[self.proxyObserver notifyObserver:^(id<WPViewObserver> _Nonnull target) {
[target update:1 count2:2];
} selector:@selector(update:count2:)];
[self.proxyObserver notifyObserver:^(id<WPViewObserver> _Nonnull target) {
[target updateTitle:@"标题" count:4];
} selector:@selector(updateTitle:count:)];