BLMeetingViewControll瘦身
音视频优化一做完之后,性能问题没有了。基本可堪重用,但BLMeetingViewControll类的代码量在2000多行,后面又增加了横屏功能。太过臃肿。
拆完结构.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
- 各Control统一在BLMeetingViewController中创建,可相互调用。
- 每个类最多的也就400行,各职责清晰,分享、菜单、主讲、浮窗、旋转等功能。
- 拆分也不是一步到位,先拆分到各个分类中,再提取到各control。
响应式实现
什么是响应式,一个简单例子a+b=c,当a或b发生变化时,c会自动刷新。这样c就需要监听a和b的属性了。KVO可以胜任,但直接使用系统KVO,如果在dealloc中移除,忘了或者添加多次,或者移除多次都有可能会闪退。
KVOController第三方库,底层对KVO做了一层封装,不需要管理释放,在观察者对象释放时,会自动移除KVO,一个对象的同一个属性观察者也不会添加多次。
- 下面是视频view绑定开关视频、开关摄像头及主持人状态的绑定。
- (void)bind:(BLMeetingItemModel *)model{
@weakify(self);
[self.KVOController observe:model keyPath:@"isOpenMicro" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _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<NSString *,id> * _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<NSString *,id> * _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"];
}
- 主讲模式绑定了远程视频状态,如果有其他人共享,则显示旋转按钮。及按钮旋转状态绑定横竖屏刷新。
- (void)bind{
@weakify(self);
[self.KVOController observe:self.lectureView keyPath:@"showType" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _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<NSString *,id> * _Nonnull change) {
@strongify(self)
if (self.rotateControl.orientation == UIInterfaceOrientationPortrait) {
[self hiddenBottomView:NO];
}else{
[self hiddenBottomView:YES];
}
}];
}
- 美颜与翻转摄像头显示与隐藏绑定开关摄像头;菜单栏绑定横竖屏状态;翻转和主讲按钮绑定分享按钮状态。
- (void)bind{
@weakify(self);
[self.KVOController observe:self.bottomMenuView.cameraButton keyPath:@"selected" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _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<NSString *,id> * _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<NSString *,id> * _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];
}
}];
}
- 一个最重要的功能,如何监听数组的变化。
[self mutableArrayValueForKeyPath:@"dataArray"]可以监听到数组的变化,但存在的问题是监听到的操作不明确。
removeObjects会调用多次到观察者,removeObject会调用一次,区分不开。如在网络重连时,可能会先remove再addObject,但可能想要的结果只是在所有操作完成后进行一次回调。
如何很好的监听数组的变化
一个很好的方式,自定义。
继承NSMutableArray会有问题,官方文档说明:
继承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<NSString *,id> * _Nonnull change) {
@strongify(self);
if (object.arrayChangeType == BLArrayAddType) {
NSLog(@"会议:添加数据");
}else if (object.arrayChangeType == BLArrayAddAllType) {
NSLog(@"会议:添加数据完成");
}else if (object.arrayChangeType == BLArrayRemoveType){
NSLog(@"会议:移除单个数据");
}
}];
数组的状态能准确监听。
响应式的实现,解决了很多刷新问题,如开关摄像头,不再关注联动属性:美颜和翻转按钮的展示了;别人分享,显示旋转按钮;开关摄像头,能够实现局部精准刷新。
小结
- 绑定model,在model需要特殊处理下,如果存在的从缓存中拿,更新对应属性,否则产生新的对象。确保绑定过的对象不会变成另一个。
- 数组的监听,自定义更能达到想要的效果。
- 绑定遵循原则:谁观察者谁处理,如按钮的隐藏于展示都在菜单的Control中。
- tableView的绑定,要注意cell的解绑与重新绑定,滑动当前展示的cell,需要解绑之前的,再绑定当前的。否则会出现绑定多个,而出现展示错误。
网友评论