音视频优化二:控制器瘦身及响应式实现

BLMeetingViewControll瘦身

音视频优化一做完之后,性能问题没有了。基本可堪重用,但BLMeetingViewControll类的代码量在2000多行,后面又增加了横屏功能。太过臃肿。

音视频优化二:控制器瘦身及响应式实现_第1张图片
拆完结构.png

遵循单一职责原则,BLMeetingVCDataSource为底层,所有Control均可使用。
BLMeetingVCBase为Control父类,有一些共用方法。

@interface BLMeetingVCBase : NSObject
@property (nonatomic,weak) BLMeetingViewController * meetingVC;
@property (nonatomic,weak) BLMeetingVCDataSource * dataSource;

- (instancetype)initWithMeetingVC:(BLMeetingViewController *)meetingVC dataSource:(BLMeetingVCDataSource *)dataSource;

- (BLMeetingVCRequest *)request;
- (BLMeetingVCRotateControl *)rotateControl;
- (BLMeetingVCShareControl *)shareControl;
- (BLMeetingVCMenuControl *)menuControl;
- (BLMeetingVCLectureControl *)lectureControl;
- (BLMeetingVCLifeControl *)lifeControl;
- (BLMeetingVCFloatControl *)floatControl;
- (UIView *)view;

#pragma mark - 数据源
- (DolpConferenceProfileDto *)profileDto;
@end
  1. 各Control统一在BLMeetingViewController中创建,可相互调用。
  2. 每个类最多的也就400行,各职责清晰,分享、菜单、主讲、浮窗、旋转等功能。
  3. 拆分也不是一步到位,先拆分到各个分类中,再提取到各control。

响应式实现

什么是响应式,一个简单例子a+b=c,当a或b发生变化时,c会自动刷新。这样c就需要监听a和b的属性了。KVO可以胜任,但直接使用系统KVO,如果在dealloc中移除,忘了或者添加多次,或者移除多次都有可能会闪退。

KVOController第三方库,底层对KVO做了一层封装,不需要管理释放,在观察者对象释放时,会自动移除KVO,一个对象的同一个属性观察者也不会添加多次。

  1. 下面是视频view绑定开关视频、开关摄像头及主持人状态的绑定。
- (void)bind:(BLMeetingItemModel *)model{
    @weakify(self);
    [self.KVOController observe:model keyPath:@"isOpenMicro" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary * _Nonnull change) {
        @strongify(self);
        BOOL isOpenMicro = [change[@"new"] boolValue];
        NSLog(@"%@-%@麦克风",model.nickName,isOpenMicro?@"开启":@"关闭");
        [self.bottomView updateMicroStatus];
    }];
    
    [self.KVOController observe:model keyPath:@"isOpenVideo" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary * _Nonnull change) {
        @strongify(self);
        BOOL isOpenMicro = [change[@"new"] boolValue];
        NSLog(@"%@-%@视频-%@",model.nickName,isOpenMicro?@"开启":@"关闭",NSStringFromClass(self.class));
        [self loadVideoSource];
    }];
    
    [self.KVOController observe:model keyPath:@"isHost" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary * _Nonnull change) {
        @strongify(self);
        [self.bottomView updateView:model];//更新主持人
    }];
}

- (void)unbind:(BLMeetingItemModel *)model{
    [self.KVOController unobserve:model keyPath:@"isOpenMicro"];
    [self.KVOController unobserve:model keyPath:@"isOpenVideo"];
    [self.KVOController unobserve:model keyPath:@"isHost"];
}
  1. 主讲模式绑定了远程视频状态,如果有其他人共享,则显示旋转按钮。及按钮旋转状态绑定横竖屏刷新。
- (void)bind{
    @weakify(self);
    [self.KVOController observe:self.lectureView keyPath:@"showType" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary * _Nonnull change) {
        @strongify(self)
        if (self.lectureView.showType == BLShowTypeRemoteShare) {
            [self.rotateControl rotateBtnHidden:NO];
        }else{
            [self.rotateControl rotateBtnHidden:YES];
        }
    }];

    [self.KVOController observe:self.rotateControl keyPath:@"orientation" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary * _Nonnull change) {
        @strongify(self)
        if (self.rotateControl.orientation == UIInterfaceOrientationPortrait) {
            [self hiddenBottomView:NO];
        }else{
            [self hiddenBottomView:YES];
        }
    }];
}
  1. 美颜与翻转摄像头显示与隐藏绑定开关摄像头;菜单栏绑定横竖屏状态;翻转和主讲按钮绑定分享按钮状态。
- (void)bind{
    @weakify(self);
    [self.KVOController observe:self.bottomMenuView.cameraButton keyPath:@"selected" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary * _Nonnull change) {
        @strongify(self)
        id newValue = change[@"new"];
        BOOL selected = [(NSNumber *)newValue boolValue];
        [self hideCameraReverseAndBeautiful:selected];
    }];
    
    [self.KVOController observe:self.rotateControl keyPath:@"orientation" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary * _Nonnull change) {
        @strongify(self)
        self.isHiddenMenu = !self.isHiddenMenu;
        [self menuShowHiddenAnimation];
        
        [self.meetingVC.hostCloseView setFrame:BLKeyWindow.bounds];
        [self.meetingVC.hostCloseView addContentView];
    }];
    
    [self.KVOController observe:self.shareControl keyPath:@"shareType" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary * _Nonnull change) {
        @strongify(self)
        BLMeetingShareType shareType = (BLMeetingShareType)[change[@"new"] intValue];
        [self.bottomMenuView shareButtonSelected:shareType == BLMeetingShareTypeLocal];
        [self.bottomMenuView cameraButtonEnable:shareType != BLMeetingShareTypeLocal];//自己分享摄像头置灰,不可点击
        
        if (shareType != BLMeetingShareTypeNone){
            [self.menuControl.topTitleView setRightMenuButtonHidden:YES];//收到屏幕共享:切换摄像头与主讲按钮隐藏
        }else{
            [self.menuControl.topTitleView setRightMenuButtonHidden:NO];
        }
    }];
}
  1. 一个最重要的功能,如何监听数组的变化。
    [self mutableArrayValueForKeyPath:@"dataArray"]可以监听到数组的变化,但存在的问题是监听到的操作不明确。
    removeObjects会调用多次到观察者,removeObject会调用一次,区分不开。如在网络重连时,可能会先remove再addObject,但可能想要的结果只是在所有操作完成后进行一次回调。

如何很好的监听数组的变化

一个很好的方式,自定义。
继承NSMutableArray会有问题,官方文档说明:


音视频优化二:控制器瘦身及响应式实现_第2张图片
继承NSMutableArray.png

看了YYThreadSafeArray,发现YY重写了所有的数组方法,内部定义了NSMutableArray *_arr;如果有一个方法漏掉的,程序会直接闪退。有一定风险。

自定义状态,实现观察:

typedef enum {
    BLArrayInitType,
    BLArrayAddType,
    BLArrayAddAllType,
    BLArrayRemoveType,
}BLArrayChangeType;

@property (nonatomic, assign) BLArrayChangeType arrayChangeType;

@weakify(self);
    [self.KVOController observe:self.dataSource keyPath:@"arrayChangeType" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld block:^(id  _Nullable observer, BLMeetingVCDataSource * object, NSDictionary * _Nonnull change) {
        @strongify(self);
        if (object.arrayChangeType == BLArrayAddType) {
            NSLog(@"会议:添加数据");
        }else if (object.arrayChangeType == BLArrayAddAllType) {
            NSLog(@"会议:添加数据完成");
        }else if (object.arrayChangeType == BLArrayRemoveType){
            NSLog(@"会议:移除单个数据");
        }
    }];

数组的状态能准确监听。

响应式的实现,解决了很多刷新问题,如开关摄像头,不再关注联动属性:美颜和翻转按钮的展示了;别人分享,显示旋转按钮;开关摄像头,能够实现局部精准刷新。

小结

  1. 绑定model,在model需要特殊处理下,如果存在的从缓存中拿,更新对应属性,否则产生新的对象。确保绑定过的对象不会变成另一个。
  2. 数组的监听,自定义更能达到想要的效果。
  3. 绑定遵循原则:谁观察者谁处理,如按钮的隐藏于展示都在菜单的Control中。
  4. tableView的绑定,要注意cell的解绑与重新绑定,滑动当前展示的cell,需要解绑之前的,再绑定当前的。否则会出现绑定多个,而出现展示错误。

你可能感兴趣的:(音视频优化二:控制器瘦身及响应式实现)