单例使用多代理
问题描述:
tableview有很多cell, 其中有两个cell, 每个cell上有一条音频.
播放音频使用的单例,这个单例有代理方法,在两个cell上都设置代理,但是只有一个代理方法会调用
bug表现就是:
我点击开始播放第一个音频,进度条开始变化, 滑动tableview,上面的进度条不动了,
bug产生的原因是,单例设置多个代理,只有一个代理方法会被调用,代理是一对一的, 如果我想要多个代理方法都执行怎么办呢?
将代理放入数组,遍历这些代理,执行代理函数
具体代码:
.h文件:
@property (nonatomic, strong) NSMutableArray *delegates; - (void)addDelegate:(NSObject *)foo;
.m文件
- (void)changeStatus:(GXAVPlayStatus)status {if (self.status == status) { return; } __weak typeof(self) weakSelf = self; dispatch_async(dispatch_get_main_queue(), ^{ weakSelf.status = status; if (status == GXPlayerPlaying) { [weakSelf.progressTimer startTimerWithTime:0.1 target:weakSelf selector:@selector(progressChange) userInfo:nil repeats:true]; } else if (status != GXPlayerLoading) { [weakSelf.progressTimer stopTimer]; } if ([weakSelf.delegate respondsToSelector:@selector(currPlayStatus:)]) { [weakSelf.delegate currPlayStatus:status]; } [weakSelf sv_dealStatus:status]; }); } - (void)progressChange { if (CMTimeGetSeconds(self.currItem.duration) <= 0 || isnan(CMTimeGetSeconds(self.currItem.duration))) { return; } __weak typeof(self) weakSelf = self; dispatch_async(dispatch_get_main_queue(), ^{ if ([weakSelf.delegate respondsToSelector:@selector(progressChange)] && !weakSelf.isSeeking) { [weakSelf.delegate progressChange]; } [weakSelf signalDelegateEvent]; }); } - (void)addDelegate:(NSObject * )foo { [self.delegates addObject:foo]; } - (void)removeDelegate:(NSObject *)foo { [self.delegates removeObject:foo]; } - (void)signalDelegateEvent { [self.delegates enumerateObjectsUsingBlock:^(id<SV_GXAVPlayerDelegate> obj, NSUInteger idx, BOOL *stop) { // call delegate method `foo` on each delegate if ( [obj respondsToSelector:@selector(sv_progressChange)]) { [obj sv_progressChange]; } }]; } - (void)sv_dealStatus:(GXAVPlayStatus)status; { [self.delegates enumerateObjectsUsingBlock:^(id<SV_GXAVPlayerDelegate> obj, NSUInteger idx, BOOL *stop) { // call delegate method `foo` on each delegate if ( [obj respondsToSelector:@selector(sv_currPlayStatus:)]) { [obj sv_currPlayStatus:status]; } }]; } -(NSMutableArray *)delegates{ if(!_delegates){ _delegates = [NSMutableArray array]; } return _delegates; }
这样使用多代理,解决了上面提到的bug.
不足之处是,需要恰当的时候释放代理,考虑使用 NSPointerArray; 更好的解决办法是使用runtime。待探索。。。
update: runtime添加多代理:
https://www.jianshu.com/p/fed580fa45eb
https://github.com/FlameGrace/MultiDelegateOC/
https://blog.scottlogic.com/2012/11/19/a-multicast-delegate-pattern-for-ios-controls.html
整体的思路是:
写个NSObject的分类,添加
multiDelegate 属性, 添加addDelegate方法。 这样其他类只要引入这个分类,就可以添加多个代理了
把其他类的代理,设置为multiDelegate
代理调用的时候,在multiDelegate 处理
正常的代理,A声明代理,B成为A的代理, A调用代理,其实是在A类中调用B
这里multiDelegate相当于B, 但是multiDelegate没有实现代理方法啊,可以转发,
respondsToSelector
不明白了,拿过来书看看怎么说的
主要是 这两个方法,不知道里面啥意思
methodSignatureForSelector
forwardInvocation
- (void)forwardInvocation:(NSInvocation *)anInvocation { NSLog(@"22222222"); for(id delegate in self.delegateArr){ if ([delegate respondsToSelector:anInvocation.selector]) { [anInvocation invokeWithTarget:delegate]; } } } - (BOOL)respondsToSelector:(SEL)aSelector{ if ([super respondsToSelector:aSelector]) { return YES; } for (id target in self.delegateArr) { if ([target respondsToSelector:aSelector]) { return YES; } } return NO; }
参考下面,可以实现
https://blog.csdn.net/kingjxust/article/details/49559091
TODD:1. 学习下面的; 2. 再写一遍上面的三个方法
https://zhangbuhuai.com/post/message-forwarding.html
2022.3.4更新:
helper类:.m文件
#import "MutipleDelegate3_4_2022.h" @implementation MutipleDelegate3_4_2022 -(BOOL)respondsToSelector:(SEL)aSelector { // 如果父类能响应, 直接返回yes if ([super respondsToSelector:aSelector]) { return YES; } // 遍历,让代理响应 for (id delegate in self.delegates) { if ([delegate respondsToSelector:aSelector]) { return YES; } } return NO; } -(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSMethodSignature *sign = [super methodSignatureForSelector:aSelector]; if (sign) { return sign; }else { // 每个代理都生成sign for(id delegate in self.delegates){ if ([delegate respondsToSelector:aSelector]) { // 下面这行没写对,写成了 sign = [delegate methodSignatureForSelector:aSelector]; return [delegate methodSignatureForSelector:aSelector]; } } return sign; } } - (void)forwardInvocation:(NSInvocation *)anInvocation { for(id delegate in self.delegates){ if ([delegate respondsToSelector:anInvocation.selector]) { // [delegate forwardInvocation:anInvocation]; // 下面这句没抄对. 意思是, anInvocation调用delegate [anInvocation invokeWithTarget:delegate]; } } } -(NSMutableArray *)delegates { if (!_delegates) { _delegates = [NSMutableArray array]; } return _delegates; } @end
.h文件
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface MutipleDelegate3_4_2022 : NSObject @property (nonatomic, strong) NSMutableArray *delegates; @end NS_ASSUME_NONNULL_END
控制器:
#import "MDViewController2022_3_4.h" #import "HandleDelegateObj.h" #import "MutipleDelegate3_4_2022.h" @interface MDViewController2022_3_4 ()<UIScrollViewDelegate> @property (nonatomic, strong) HandleDelegateObj *obj; @property (nonatomic, strong) MutipleDelegate3_4_2022 *delegateHelp; @end @implementation MDViewController2022_3_4 - (void)viewDidLoad { [super viewDidLoad]; UIScrollView *scrl = [[UIScrollView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; [self.view addSubview:scrl]; scrl.contentSize = CGSizeMake(0, 200); // scrl.delegate = self; scrl.backgroundColor = [UIColor brownColor]; _obj = [HandleDelegateObj new]; // scrl.delegate = _obj; self.delegateHelp = [MutipleDelegate3_4_2022 new]; self.delegateHelp.delegates = @[self, self.obj]; scrl.delegate = (id<UIScrollViewDelegate>)self.delegateHelp; } -(void)scrollViewDidScroll:(UIScrollView *)scrollView { NSLog(@"控制器滚动"); }
HandleDelegateObj .h文件
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface HandleDelegateObj : NSObject <UIScrollViewDelegate> @end NS_ASSUME_NONNULL_END
.m文件
#import "HandleDelegateObj.h" @implementation HandleDelegateObj -(void)scrollViewDidScroll:(UIScrollView *)scrollView { NSLog(@"obj滚动了%@",NSStringFromClass([self class])); } @end
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
2020-03-01 XWG工作成绩
2020-03-01 父容器flex布局,子控件absult布局,会影响父容器的宽度
2019-03-01 xcode代码提示没了