新增 micButton 状态表格文档,详细记录了 micButton 在不同用户状态下的显示和可用性,以及状态变化的关键时序和同步机制。同时,新增 GiftComboManager 线程优化总结文档,优化了线程处理和网络请求逻辑,提升了代码性能和可维护性。更新了 CountdownRingView 和 GiftComboView 的内存管理和状态处理逻辑,确保资源的正确释放和避免内存泄漏。

This commit is contained in:
edwinQQQ
2025-08-19 19:33:26 +08:00
parent 6d4061bea5
commit aeb9fcd30e
12 changed files with 731 additions and 227 deletions

View File

@@ -1,35 +1,79 @@
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 0 -[XPSendGiftView xPGiftBarView:didClickSendGift:] [Line 771][Combo effect] 🎁 开始送礼物流程
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 3 -[XPSendGiftView readyForCombo:gift:] [Line 727][Combo effect] 🔧 准备连击状态 - giftType: 18, segmentType: 8
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 4 -[XPSendGiftView readyForCombo:gift:] [Line 745][Combo effect] ✅ 礼物支持连击,启用连击功能
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 5 -[GiftComboManager activate] [Line 197][Combo effect] 🔧 激活连击功能
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 6 -[GiftComboManager configureWithGiftInfo:targetUIDs:roomUID:sessionID:userInfo:countModel:sourceType:sendType:giftNum:] [Line 696][Combo effect] 🔧 统一配置连击参数
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 7 -[GiftComboManager configureWithGiftInfo:targetUIDs:roomUID:sessionID:userInfo:countModel:sourceType:sendType:giftNum:] [Line 709][Combo effect] ✅ 连击参数配置完成 - giftId: 2263, targetCount: 1
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 8 -[XPSendGiftView readyForCombo:gift:] [Line 761][Combo effect] ✅ 连击状态准备完成
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 9 -[GiftComboManager printComboState] [Line 377][Combo effect] 📊 当前连击状态:
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 10 -[GiftComboManager printComboState] [Line 378][Combo effect] - isCombing: NO
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 11 -[GiftComboManager printComboState] [Line 379][Combo effect] - enableCombo: YES
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 12 -[GiftComboManager printComboState] [Line 380][Combo effect] - combo: 1
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 13 -[GiftComboManager printComboState] [Line 381][Combo effect] - hasGiftInfo: YES
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 14 -[GiftComboManager printComboState] [Line 382][Combo effect] - targetCount: 1
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 15 -[GiftComboManager printComboState] [Line 383][Combo effect] - errorMessage:
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 16 -[XPSendGiftView xPGiftBarView:didClickSendGift:] [Line 778][Combo effect] ✅ 连击功能已启用准备调用resetCombo
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 17 -[XPSendGiftView sendGiftSuccess:originDic:uidCount:] [Line 1198][Combo effect] 📱 检查连击状态 - enableCombo: YES
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 18 -[XPSendGiftView sendGiftSuccess:originDic:uidCount:] [Line 1207][Combo effect] 📱 originDic 连击计数检查 - comboCount: (null)
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 19 -[XPSendGiftView sendGiftSuccess:originDic:uidCount:] [Line 1210][Combo effect] 📱 启用连击模式,重置连击状态
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 20 -[GiftComboManager reset] [Line 141][Combo effect] 🔄 开始连击重置 - combo: 1 -> 1, enableCombo: YES, actionCallback: 为空
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 21 -[GiftComboManager reset] [Line 154][Combo effect] 🔍 重置后验证 - combo: 1
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 22 -[GiftComboManager reset] [Line 173][Combo effect] ⚠️ actionCallback为空不显示连击面板
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 23 -[GiftComboManager reset] [Line 177][Combo effect] 🎮 隐藏房间UI元素
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 24 -[GiftComboManager reset] [Line 186][Combo effect] ✅ 连击重置完成 - isCombing: NO
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 25 -[XPSendGiftView sendCustomMessage:oringinDic:] [Line 425][Combo effect] 📨 云信消息连击计数检查 - comboCount: 1, giftId: 2263
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 26 -[GiftComboView setupTimer] [Line 284][Combo effect] ⏰ 设置连击倒计时
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 27 -[CountdownRingView startCountdown] [Line 97][Combo effect] 开始倒计时
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 28 -[CountdownRingView animateRing] [Line 246][Combo effect] 🎬 环形动画已启动,时长: 5.0秒
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 29 -[CountdownRingView startCountdown] [Line 109][Combo effect] ⏰ 倒计时已启动
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 30 -[GiftComboView setupTimer] [Line 293][Combo effect] ⏰ 连击倒计时已启动
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 31 -[GiftComboView updateCount] [Line 171][Combo effect] 🔢 更新连击次数显示 - combo: 1
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 32 -[GiftComboManager printComboState] [Line 377][Combo effect] 📊 当前连击状态:
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 33 -[GiftComboManager printComboState] [Line 378][Combo effect] - isCombing: NO
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 34 -[GiftComboManager printComboState] [Line 379][Combo effect] - enableCombo: YES
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 35 -[GiftComboManager printComboState] [Line 380][Combo effect] - combo: 1
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 0 -[GiftComboManager printComboState] [Line 381][Combo effect] - hasGiftInfo: YES
-[GiftComboManager printComboState] [Line 382][Combo effect] - targetCount: 1
-[GiftComboManager printComboState] [Line 383][Combo effect] - errorMessage:
-[GiftComboManager forceBoomStateReset] [Line 275][Combo effect] 🚨 执行强制Boom连击状态重置
-[GiftComboManager forceBoomStateReset] [Line 278][Combo effect] ⏰ 停止所有定时器
-[GiftComboManager forceStopAllTimers] [Line 321][Combo effect] ⏰ 强制停止所有Timer
-[GiftComboManager forceBoomStateReset] [Line 282][Combo effect] 🗑️ 清空所有队列
-[GiftComboManager forceBoomStateReset] [Line 287][Combo effect] 🔄 重置状态标志 - isCombing: NO -> NO
-[GiftComboManager forceBoomStateReset] [Line 292][Combo effect] 🔄 combo计数重置为0
-[GiftComboManager forceBoomStateReset] [Line 301][Combo effect] 📢 发送强制重置通知
-[GiftComboManager forceBoomStateReset] [Line 309][Combo effect] 🎮 恢复房间UI元素
-[GiftComboManager forceBoomStateReset] [Line 316][Combo effect] ✅ 强制重置完成 - enableCombo保持: YES, actionCallback保持: 为空
-[XPSendGiftView removeAllComboRelatedViews] [Line 137][Combo effect] 🗑️ 开始移除连击相关视图
-[XPSendGiftView removeAllComboRelatedViews] [Line 148][Combo effect] 🗑️ comboView存在但无superview直接清理
-[CountdownRingView stopCountdown] [Line 200][Combo effect] ⏰ 停止倒计时开始
-[CountdownRingView stopCountdown] [Line 208][Combo effect] ⏰ Timer已停止
-[CountdownRingView stopCountdown] [Line 214][Combo effect] ⏰ 动画已停止
-[CountdownRingView stopCountdown] [Line 230][Combo effect] ⏰ 停止倒计时完成
-[GiftComboView dealloc] [Line 43][Combo effect] 🗑️ GiftComboView dealloc开始 - 0x13624e600
-[CountdownRingView stopCountdown] [Line 196][Combo effect] ⚠️ 倒计时未运行,无需停止
-[GiftComboView dealloc] [Line 72][Combo effect] 🗑️ GiftComboView dealloc完成 - 0x13624e600
-[XPSendGiftView removeAllComboRelatedViews] [Line 166][Combo effect] 🗑️ 连击相关视图移除完成
-[CountdownRingView dealloc] [Line 32][Combo effect] 🗑️ CountdownRingView dealloc开始 - 0x13624e800
-[CountdownRingView cleanup] [Line 251][Combo effect] 🗑️ 完全清理开始
-[CountdownRingView stopCountdown] [Line 196][Combo effect] ⚠️ 倒计时未运行,无需停止
-[CountdownRingView cleanup] [Line 260][Combo effect] 🗑️ 完全清理完成
-[CountdownRingView dealloc] [Line 37][Combo effect] 🗑️ CountdownRingView dealloc完成 - 0x13624e800
-[RoomAnimationView _handleGiftMessage:] [Line 1737][Combo effect] 📨 收到礼物消息 - second: 121
-[RoomAnimationView _handleGiftMessage:] [Line 1757][Combo effect] 📨 礼物消息解析完成 - giftId: 2263, combo: 1, uid: 3184
-[RoomAnimationView _handleGiftMessage:] [Line 1824][Combo effect] 🎁 处理普通礼物动画
-[GiftAnimationManager enqueueGift:] [Line 237][Combo effect] 🎬 添加礼物到动画队列 - giftId: 2263, combo: 1
-[GiftComboManager receiveGiftInfoForDisplayComboFlags:container:] [Line 407][Combo effect] 🎪 收到连击飘屏请求 - combo: 1, giftId: 2263
-[GiftAnimationManager enqueueGift:]_block_invoke [Line 241][Combo effect] 📊 动画队列当前数量: 1
-[GiftComboManager receiveGiftInfoForDisplayComboFlags:container:] [Line 410][Combo effect] 📊 连击飘屏队列数量: 1
-[GiftComboManager processGiftFlagQueue] [Line 462][Combo effect] 🎪 处理连击飘屏 - combo: 1, giftId: 2263
-[GiftComboManager processGiftFlagQueue] [Line 464][Combo effect] <20><> 移除后UI动画队列数量: 0
-[GiftAnimationManager processNextGift]_block_invoke [Line 100][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 1
-[GiftAnimationManager processNextGift]_block_invoke [Line 104][Combo effect] 📊 移除后动画队列数量: 0
-[GiftAnimationManager distributeGiftAnimation:] [Line 119][Combo effect] 🎯 开始分发动画 - uid: 3184
-[GiftAnimationManager distributeGiftAnimation:] [Line 122][Combo effect] 🎯 目标用户数量: 1
-[GiftAnimationManager distributeGiftAnimation:] [Line 126][Combo effect] 🎯 是否使用连击动画: NO
-[GiftAnimationManager distributeGiftAnimation:] [Line 133][Combo effect] 🎯 使用普通动画起点: {207, 285.79999999999995}
-[GiftAnimationManager distributeGiftAnimation:] [Line 136][Combo effect] 🎯 动画延迟时间: 0.30
-[GiftAnimationManager distributeGiftAnimation:] [Line 140][Combo effect] 🎯 为目标用户 3184 创建动画 - 终点: {207, 285.79999999999995}
-[GiftAnimationManager processNextGift]_block_invoke [Line 82][Combo effect] 📭 动画队列为空,停止处理

View File

@@ -193,7 +193,7 @@ TZImagePickerControllerDelegate>
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:1 TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:1
delegate:self]; delegate:self];
imagePickerVc.modalPresentationStyle = UIModalPresentationOverFullScreen; imagePickerVc.modalPresentationStyle = UIModalPresentationOverFullScreen;
imagePickerVc.allowPickingVideo = NO; imagePickerVc.allowPickingVideo = displayGIF;
imagePickerVc.allowTakeVideo = NO; imagePickerVc.allowTakeVideo = NO;
if (displayGIF) { if (displayGIF) {
imagePickerVc.allowTakePicture = NO; imagePickerVc.allowTakePicture = NO;

View File

@@ -32,7 +32,6 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
@property (nonatomic, strong) NSMutableArray *requestQueue; @property (nonatomic, strong) NSMutableArray *requestQueue;
// GiftReceiveInfoModel NSDictionary // GiftReceiveInfoModel NSDictionary
// @property (nonatomic, strong) NSMutableArray *giftComboQueue;
@property (nonatomic, strong) NSMutableArray *networkQueue; // AttachmentModel @property (nonatomic, strong) NSMutableArray *networkQueue; // AttachmentModel
@property (nonatomic, strong) NSMutableArray *uiQueue; // UIGiftReceiveInfoModel @property (nonatomic, strong) NSMutableArray *uiQueue; // UIGiftReceiveInfoModel
@property (nonatomic, strong) dispatch_source_t comboFlagTimer; @property (nonatomic, strong) dispatch_source_t comboFlagTimer;
@@ -71,10 +70,23 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
#pragma mark - #pragma mark -
- (void)dealloc { - (void)dealloc {
NSLog(@"[Combo effect] 🗑️ GiftComboManager dealloc开始 - %p", self);
// 🔥 timer
[self forceStopAllTimers];
// UI
[self stopProcessingUIQueue]; [self stopProcessingUIQueue];
if (self.uiQueue) {
self.uiQueue = NULL; //
} [self clearAllQueues];
//
self.isCombing = NO;
self.enableCombo = NO;
self.actionCallback = nil;
NSLog(@"[Combo effect] 🗑️ GiftComboManager dealloc完成 - %p", self);
} }
+ (instancetype)sharedManager { + (instancetype)sharedManager {
@@ -128,28 +140,51 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
self.enableCombo ? @"YES" : @"NO", self.enableCombo ? @"YES" : @"NO",
self.actionCallback ? @"可用" : @"为空"); self.actionCallback ? @"可用" : @"为空");
// 🔥
if (self.isCombing && self.combo == 0) {
NSLog(@"[Combo effect] ⚠️ 正在重置过程中,跳过重复重置");
return;
}
// 🔥 actionCallbackreset
if (!self.actionCallback) {
NSLog(@"[Combo effect] ⚠️ actionCallback为空延迟执行reset");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self reset];
});
return;
}
// 🔥 enableCombo
if (!self.enableCombo) {
NSLog(@"[Combo effect] ⚠️ enableCombo为NO尝试重新激活");
self.enableCombo = YES;
}
// 1 // 1
_combo = 1; _combo = 1;
_errorMessage = @""; _errorMessage = @"";
// //
NSLog(@"[Combo effect] 🔍 重置后验证 - combo: %ld", (long)self.combo); NSLog(@"[Combo effect] 🔍 重置后验证 - combo: %ld, enableCombo: %@", (long)self.combo, self.enableCombo ? @"YES" : @"NO");
// GiftComboView // GiftComboView
[[NSNotificationCenter defaultCenter] postNotificationName:@"ComboCountReset" object:nil]; [[NSNotificationCenter defaultCenter] postNotificationName:@"ComboCountReset" object:nil];
// 🔥
self.isCombing = YES;
// - 线UI // - 线UI
if (self.actionCallback && self.enableCombo) { if (self.actionCallback) {
NSLog(@"[Combo effect] 📱 触发连击面板显示回调"); NSLog(@"[Combo effect] 📱 触发连击面板显示回调");
@kWeakify(self); @kWeakify(self);
[self safeExecuteUIBlock:^{ [self safeExecuteUIBlock:^{
@kStrongify(self); @kStrongify(self);
self.actionCallback(ComboAction_ShowPanel); if (self && self.actionCallback) {
self.actionCallback(ComboAction_ShowPanel);
}
}]; }];
self.isCombing = YES; } else {
} else if (self.actionCallback && !self.enableCombo) {
NSLog(@"[Combo effect] ⚠️ enableCombo为NO不显示连击面板");
} else if (!self.actionCallback) {
NSLog(@"[Combo effect] ⚠️ actionCallback为空不显示连击面板"); NSLog(@"[Combo effect] ⚠️ actionCallback为空不显示连击面板");
} }
@@ -158,10 +193,12 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
@kWeakify(self); @kWeakify(self);
[self safeExecuteUIBlock:^{ [self safeExecuteUIBlock:^{
@kStrongify(self); @kStrongify(self);
self.handleRoomUIChanged(YES); if (self && self.handleRoomUIChanged) {
self.handleRoomUIChanged(YES);
}
}]; }];
} }
NSLog(@"[Combo effect] ✅ 连击重置完成 - isCombing: %@", self.isCombing ? @"YES" : @"NO"); NSLog(@"[Combo effect] ✅ 连击重置完成 - isCombing: %@, enableCombo: %@", self.isCombing ? @"YES" : @"NO", self.enableCombo ? @"YES" : @"NO");
} }
- (void)registerActions:(void (^)(ComboActionType))action { - (void)registerActions:(void (^)(ComboActionType))action {
@@ -201,15 +238,18 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
- (void)clear { - (void)clear {
NSLog(@"[Combo effect] 🗑️ 清除连击状态"); NSLog(@"[Combo effect] 🗑️ 清除连击状态");
[self forceBoomStateReset]; [self forceBoomStateReset];
// UI - 线UI // 🔥 线UI
if (self.actionCallback) { if (self.actionCallback) {
NSLog(@"[Combo effect] 📱 触发连击面板移除回调"); NSLog(@"[Combo effect] 📱 触发连击面板移除回调");
@kWeakify(self); @kWeakify(self);
[self safeExecuteUIBlock:^{ [self safeExecuteUIBlock:^{
@kStrongify(self); @kStrongify(self);
self.actionCallback(ComboAction_RemovePanel); if (self && self.actionCallback) {
self.actionCallback(ComboAction_RemovePanel);
}
}]; }];
} }
} }
@@ -293,11 +333,15 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
// //
- (void)forceStopAllTimers { - (void)forceStopAllTimers {
NSLog(@"[Combo effect] ⏰ 强制停止所有Timer");
//
if (self.timer) { if (self.timer) {
dispatch_source_cancel(self.timer); dispatch_source_cancel(self.timer);
self.timer = nil; // self.timer = nil;
} }
// UI
if (self.comboFlagTimer) { if (self.comboFlagTimer) {
dispatch_source_cancel(self.comboFlagTimer); dispatch_source_cancel(self.comboFlagTimer);
self.comboFlagTimer = nil; self.comboFlagTimer = nil;
@@ -561,7 +605,13 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
// - // -
@kWeakify(self);
dispatch_source_set_event_handler(self.timer, ^{ dispatch_source_set_event_handler(self.timer, ^{
@kStrongify(self);
if (!self) {
NSLog(@"[Combo effect] ⚠️ self已释放忽略timer回调");
return;
}
[self processRequestQueue]; [self processRequestQueue];
[self processGiftComboQueue]; [self processGiftComboQueue];
}); });
@@ -577,17 +627,17 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
// //
- (void)stopProcessingQueue { - (void)stopProcessingQueue {
if (self.timer) { if (self.timer) {
if (self.requestQueue.count == 0 && self.networkQueue.count == 0) { // 🔥 timer
// dispatch_source_cancel(self.timer);
dispatch_source_cancel(self.timer);
// timer nil
// timer nil @kWeakify(self);
@kWeakify(self); dispatch_source_set_cancel_handler(self.timer, ^{
dispatch_source_set_cancel_handler(self.timer, ^{ @kStrongify(self);
@kStrongify(self); if (self) {
self.timer = nil; self.timer = nil;
}); }
} });
} }
} }

View File

@@ -1,6 +1,7 @@
# GiftComboManager 调用方更新总结 # GiftComboManager 调用方更新总结
## 🎯 更新目标 ## 🎯 更新目标
将调用方代码从使用已删除的废弃方法迁移到新的简化接口 将调用方代码从使用已删除的废弃方法迁移到新的简化接口
## ✅ 已完成的更新 ## ✅ 已完成的更新
@@ -8,6 +9,7 @@
### 1. XPSendGiftView.m 更新 ### 1. XPSendGiftView.m 更新
#### 1.1 readyForCombo 方法优化 #### 1.1 readyForCombo 方法优化
**更新前** **更新前**
```objc ```objc
[[GiftComboManager sharedManager] enableToCombo:YES]; [[GiftComboManager sharedManager] enableToCombo:YES];
@@ -40,6 +42,7 @@
``` ```
#### 1.2 其他方法调用更新 #### 1.2 其他方法调用更新
- `enableToCombo:NO``deactivate` - `enableToCombo:NO``deactivate`
- `enableToCombo:YES``activate` - `enableToCombo:YES``activate`
- `resetCombo``reset` - `resetCombo``reset`
@@ -48,6 +51,7 @@
### 2. XPRoomViewController.m 更新 ### 2. XPRoomViewController.m 更新
#### 2.1 调试方法更新 #### 2.1 调试方法更新
更新了4个调试方法中的调用 更新了4个调试方法中的调用
- `simulateAppEnterBackground` - `simulateAppEnterBackground`
- `simulateMemoryWarning` - `simulateMemoryWarning`
@@ -67,6 +71,7 @@
``` ```
#### 2.2 状态检查方法更新 #### 2.2 状态检查方法更新
更新了以下方法中的状态检查: 更新了以下方法中的状态检查:
- `viewWillDisappear` - `viewWillDisappear`
- `applicationDidEnterBackground` - `applicationDidEnterBackground`
@@ -94,16 +99,19 @@ if ([[GiftComboManager sharedManager] isActive]) {
## 🎉 更新效果 ## 🎉 更新效果
### 代码简化 ### 代码简化
-**配置调用从9个减少到1个**:大幅简化配置流程 -**配置调用从9个减少到1个**:大幅简化配置流程
-**方法调用更语义化**`activate/deactivate``enableToCombo` 更清晰 -**方法调用更语义化**`activate/deactivate``enableToCombo` 更清晰
-**状态检查统一**`isActive` 替代 `isGiftCombing` -**状态检查统一**`isActive` 替代 `isGiftCombing`
### 功能保持 ### 功能保持
-**所有功能保持不变**:只是接口调用方式改变 -**所有功能保持不变**:只是接口调用方式改变
-**向后兼容**:通过废弃标记处理兼容性 -**向后兼容**:通过废弃标记处理兼容性
-**错误处理**:保持原有的错误处理逻辑 -**错误处理**:保持原有的错误处理逻辑
### 维护性提升 ### 维护性提升
-**代码更简洁**:减少重复的配置调用 -**代码更简洁**:减少重复的配置调用
-**逻辑更清晰**:统一的方法命名和调用方式 -**逻辑更清晰**:统一的方法命名和调用方式
-**易于扩展**:新的接口设计更易于后续扩展 -**易于扩展**:新的接口设计更易于后续扩展
@@ -111,16 +119,19 @@ if ([[GiftComboManager sharedManager] isActive]) {
## 🔄 后续建议 ## 🔄 后续建议
### 立即执行(高优先级) ### 立即执行(高优先级)
1. **编译测试**:确保所有更新后的代码能正常编译 1. **编译测试**:确保所有更新后的代码能正常编译
2. **功能测试**:验证连击功能的所有场景正常工作 2. **功能测试**:验证连击功能的所有场景正常工作
3. **性能测试**:确认优化后的性能表现 3. **性能测试**:确认优化后的性能表现
### 中期优化(中优先级) ### 中期优化(中优先级)
1. **其他调用方**:检查是否还有其他文件使用了废弃方法 1. **其他调用方**:检查是否还有其他文件使用了废弃方法
2. **文档更新**:更新相关文档和注释 2. **文档更新**:更新相关文档和注释
3. **代码审查**:进行代码审查确保质量 3. **代码审查**:进行代码审查确保质量
### 长期规划(低优先级) ### 长期规划(低优先级)
1. **完全移除废弃方法**:在确认所有调用方都更新后,可以考虑完全移除废弃方法 1. **完全移除废弃方法**:在确认所有调用方都更新后,可以考虑完全移除废弃方法
2. **接口标准化**:考虑将这种简化模式应用到其他模块 2. **接口标准化**:考虑将这种简化模式应用到其他模块
3. **自动化测试**:添加自动化测试确保接口变更不会破坏功能 3. **自动化测试**:添加自动化测试确保接口变更不会破坏功能

View File

@@ -1,6 +1,7 @@
# GiftComboManager 优化报告 # GiftComboManager 优化报告
## 🎯 优化目标 ## 🎯 优化目标
- 减少50%的冗余方法 - 减少50%的冗余方法
- 简化方法调用链 - 简化方法调用链
- 提高代码可维护性 - 提高代码可维护性
@@ -11,50 +12,60 @@
### Phase 1: 移除废弃方法(高优先级) ### Phase 1: 移除废弃方法(高优先级)
#### ✅ 1.1 移除 `enableToCombo:` 方法 #### ✅ 1.1 移除 `enableToCombo:` 方法
- **原因**:已有 `activate/deactivate` 方法替代 - **原因**:已有 `activate/deactivate` 方法替代
- **影响**减少1个冗余方法 - **影响**减少1个冗余方法
#### ✅ 1.2 移除 `resetCombo` 方法 #### ✅ 1.2 移除 `resetCombo` 方法
- **原因**:已有 `reset` 方法替代 - **原因**:已有 `reset` 方法替代
- **操作**:将 `resetCombo` 的逻辑合并到 `reset` 方法中 - **操作**:将 `resetCombo` 的逻辑合并到 `reset` 方法中
- **影响**减少1个冗余方法简化调用链 - **影响**减少1个冗余方法简化调用链
#### ✅ 1.3 移除 `forceRemove` 方法 #### ✅ 1.3 移除 `forceRemove` 方法
- **原因**:与 `forceBoomStateReset` 功能重复 - **原因**:与 `forceBoomStateReset` 功能重复
- **操作**:修改 `clear` 方法直接调用 `forceBoomStateReset` - **操作**:修改 `clear` 方法直接调用 `forceBoomStateReset`
- **影响**减少1个冗余方法简化调用链 - **影响**减少1个冗余方法简化调用链
#### ✅ 1.4 移除 `loadComboCountFromSendGiftView` 方法 #### ✅ 1.4 移除 `loadComboCountFromSendGiftView` 方法
- **原因**:已有 `incrementCount` 方法替代 - **原因**:已有 `incrementCount` 方法替代
- **影响**减少1个冗余方法 - **影响**减少1个冗余方法
#### ✅ 1.5 移除 `loadComboCount` 方法 #### ✅ 1.5 移除 `loadComboCount` 方法
- **原因**:已有 `currentCount` 方法替代 - **原因**:已有 `currentCount` 方法替代
- **操作**:将逻辑合并到 `currentCount` 方法中 - **操作**:将逻辑合并到 `currentCount` 方法中
- **影响**减少1个冗余方法 - **影响**减少1个冗余方法
#### ✅ 1.6 移除 `isGiftCombing` 方法 #### ✅ 1.6 移除 `isGiftCombing` 方法
- **原因**:已有 `isActive` 方法替代 - **原因**:已有 `isActive` 方法替代
- **影响**减少1个冗余方法 - **影响**减少1个冗余方法
### Phase 2: 简化清除方法链(中优先级) ### Phase 2: 简化清除方法链(中优先级)
#### ✅ 2.1 合并 `clear` 和 `forceRemove` 方法 #### ✅ 2.1 合并 `clear` 和 `forceRemove` 方法
- **操作**`clear` 方法直接调用 `forceBoomStateReset` - **操作**`clear` 方法直接调用 `forceBoomStateReset`
- **效果**:简化方法调用链 - **效果**:简化方法调用链
#### ✅ 2.2 优化 `forceBoomStateReset` 方法 #### ✅ 2.2 优化 `forceBoomStateReset` 方法
- **状态**:方法已经优化,无重复逻辑 - **状态**:方法已经优化,无重复逻辑
- **功能**:停止定时器、清空队列、重置状态、发送通知 - **功能**:停止定时器、清空队列、重置状态、发送通知
### Phase 3: 统一配置方法(低优先级) ### Phase 3: 统一配置方法(低优先级)
#### ✅ 3.1 创建统一的配置方法 #### ✅ 3.1 创建统一的配置方法
- **新增**`configureWithGiftInfo:targetUIDs:roomUID:sessionID:userInfo:countModel:sourceType:sendType:giftNum:` - **新增**`configureWithGiftInfo:targetUIDs:roomUID:sessionID:userInfo:countModel:sourceType:sendType:giftNum:`
- **替代**9个独立的save方法 - **替代**9个独立的save方法
- **效果**:大幅简化配置流程 - **效果**:大幅简化配置流程
#### ✅ 3.2 移除冗余的save方法 #### ✅ 3.2 移除冗余的save方法
- **移除的方法** - **移除的方法**
- `saveSendGiftTo:` - `saveSendGiftTo:`
- `saveGiftSourceType:` - `saveGiftSourceType:`
@@ -81,16 +92,19 @@
## 🎉 优化效果 ## 🎉 优化效果
### 代码简化 ### 代码简化
-**方法数量减少62.5%**从24个方法减少到9个方法 -**方法数量减少62.5%**从24个方法减少到9个方法
-**调用链简化**清除方法从3层调用简化为1层 -**调用链简化**清除方法从3层调用简化为1层
-**配置流程简化**从9个独立调用简化为1个统一调用 -**配置流程简化**从9个独立调用简化为1个统一调用
### 功能保持 ### 功能保持
-**所有核心功能保持不变** -**所有核心功能保持不变**
-**向后兼容性通过废弃标记处理** -**向后兼容性通过废弃标记处理**
-**新接口更简洁易用** -**新接口更简洁易用**
### 维护性提升 ### 维护性提升
-**代码逻辑更清晰** -**代码逻辑更清晰**
-**减少重复代码** -**减少重复代码**
-**降低维护成本** -**降低维护成本**
@@ -98,16 +112,19 @@
## 🔄 后续建议 ## 🔄 后续建议
### 立即执行(高优先级) ### 立即执行(高优先级)
1. **更新调用方**:将使用废弃方法的代码迁移到新方法 1. **更新调用方**:将使用废弃方法的代码迁移到新方法
2. **测试验证**:确保所有功能正常工作 2. **测试验证**:确保所有功能正常工作
3. **文档更新**:更新相关文档和注释 3. **文档更新**:更新相关文档和注释
### 中期优化(中优先级) ### 中期优化(中优先级)
1. **合并定时器系统**:将两个定时器合并为单一系统 1. **合并定时器系统**:将两个定时器合并为单一系统
2. **优化队列处理**:统一队列处理逻辑 2. **优化队列处理**:统一队列处理逻辑
3. **性能优化**:减少不必要的同步操作 3. **性能优化**:减少不必要的同步操作
### 长期规划(低优先级) ### 长期规划(低优先级)
1. **架构重构**:考虑将飘屏逻辑分离到独立模块 1. **架构重构**:考虑将飘屏逻辑分离到独立模块
2. **接口标准化**:统一所有回调接口 2. **接口标准化**:统一所有回调接口
3. **错误处理优化**:完善错误处理机制 3. **错误处理优化**:完善错误处理机制
@@ -142,6 +159,7 @@
## ✅ 总结 ## ✅ 总结
本次优化成功实现了预期目标: 本次优化成功实现了预期目标:
- **方法数量减少62.5%** - **方法数量减少62.5%**
- **代码逻辑更清晰** - **代码逻辑更清晰**
- **维护成本显著降低** - **维护成本显著降低**

View File

@@ -0,0 +1,181 @@
# GiftComboManager 线程优化总结
## 🎯 优化目标
- 避免主线程阻塞
- 提升响应速度
- 确保线程安全
- 优化云信消息发送
## 🔧 主要优化内容
### 1. 线程分离优化
#### 1.1 新增后台处理队列
```objc
// 新增:后台处理队列
@property (nonatomic, strong) dispatch_queue_t backgroundQueue;
@property (nonatomic, strong) dispatch_queue_t networkProcessingQueue;
```
#### 1.2 队列初始化
```objc
// 初始化后台处理队列
sharedInstance.backgroundQueue = dispatch_queue_create("com.yumi.giftcombo.background", DISPATCH_QUEUE_CONCURRENT);
sharedInstance.networkProcessingQueue = dispatch_queue_create("com.yumi.giftcombo.network", DISPATCH_QUEUE_SERIAL);
```
### 2. 定时器优化
#### 2.1 从主线程迁移到后台线程
```objc
// 优化前:主线程
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
// 优化后:后台线程
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.backgroundQueue);
```
#### 2.2 提升处理频率
```objc
// 优化前0.25秒间隔
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 0.25 * NSEC_PER_SEC, 0.01 * NSEC_PER_SEC);
// 优化后0.1秒间隔
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC, 0.01 * NSEC_PER_SEC);
```
### 3. 网络处理优化
#### 3.1 云信消息处理异步化
```objc
// 优化前:同步处理
[self processGiftComboWith:(AttachmentModel *)networkData];
// 优化后:异步处理
dispatch_async(self.networkProcessingQueue, ^{
[self processGiftComboWith:(AttachmentModel *)networkData];
});
```
#### 3.2 API请求异步化
```objc
// 优化前:同步处理
[self handleSendGift:dic];
// 优化后:异步处理
dispatch_async(self.networkProcessingQueue, ^{
[self handleSendGift:dic];
});
```
### 4. 云信消息发送优化
#### 4.1 线程安全检查
```objc
// 优化:确保在主线程发送云信消息
if ([NSThread isMainThread]) {
[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
NSError *mainThreadError = nil;
[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&mainThreadError];
// 错误处理...
});
return;
}
```
### 5. UI回调线程安全优化
#### 5.1 统一UI回调处理
```objc
// 优化确保所有UI回调都在主线程执行
if ([NSThread isMainThread]) {
self.actionCallback(ComboAction_ShowPanel);
} else {
dispatch_async(dispatch_get_main_queue(), ^{
self.actionCallback(ComboAction_ShowPanel);
});
}
```
#### 5.2 新增辅助方法
```objc
// 新增辅助方法统一处理UI回调的线程安全
- (void)safeExecuteUIBlock:(void (^)(void))uiBlock {
if (!uiBlock) return;
if ([NSThread isMainThread]) {
uiBlock();
} else {
dispatch_async(dispatch_get_main_queue(), uiBlock);
}
}
```
## 📊 性能提升
### 响应速度提升
- **定时器间隔**:从 0.25秒 → 0.1秒(提升 60%
- **处理频率**:从 4次/秒 → 10次/秒
### 线程优化
- **主线程负载**大幅降低避免UI阻塞
- **后台处理**:网络请求和数据处理完全异步化
- **线程安全**所有UI回调确保在主线程执行
### 内存优化
- **队列管理**:使用串行队列避免资源竞争
- **定时器优化**:及时释放资源,避免内存泄漏
## 🔍 优化效果
### 1. 用户体验提升
- ✅ 连击响应更快
- ✅ UI更流畅无卡顿
- ✅ 网络请求不阻塞界面
### 2. 稳定性提升
- ✅ 线程安全保证
- ✅ 云信SDK兼容性
- ✅ 错误处理更完善
### 3. 性能监控
- ✅ 新增性能指标监控
- ✅ 队列状态可视化
- ✅ 调试信息更详细
## 🚀 使用建议
### 1. 监控性能
```objc
// 定期调用性能监控
[[GiftComboManager sharedManager] logPerformanceMetrics];
```
### 2. 错误处理
- 网络错误保持连击状态
- 服务器错误允许重试
- 余额不足强制重置
### 3. 调试模式
- 详细的日志输出
- 性能指标监控
- 线程状态跟踪
## 📝 注意事项
1. **云信SDK兼容性**确保在主线程调用云信相关API
2. **UI回调安全**所有UI更新都在主线程执行
3. **内存管理**:及时释放定时器和队列资源
4. **错误恢复**:区分临时错误和永久错误
## 🎉 总结
通过本次优化GiftComboManager 实现了:
- **线程分离**主线程专注UI后台线程处理业务逻辑
- **性能提升**响应速度提升60%处理频率提升150%
- **稳定性增强**:完善的线程安全机制和错误处理
- **可维护性**:清晰的代码结构和详细的监控机制
这些优化确保了连击功能的高性能和稳定性,为用户提供流畅的体验。

View File

@@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)startCountdown; - (void)startCountdown;
- (void)resetCountdown; // 重置功能 - (void)resetCountdown; // 重置功能
- (void)stopCountdown; - (void)stopCountdown;
- (void)setCompletionHandler:(void (^__nullable)(void))completionHandler; // 计时结束的回调 - (void)setupCompletionHandler:(void (^__nullable)(void))completionHandler; // 计时结束的回调
@end @end

View File

@@ -16,6 +16,12 @@
@property (nonatomic, assign) CGFloat remainingTime; @property (nonatomic, assign) CGFloat remainingTime;
@property (nonatomic, assign) NSInteger totalDuration; @property (nonatomic, assign) NSInteger totalDuration;
@property (nonatomic, copy) void (^completionHandler)(void); @property (nonatomic, copy) void (^completionHandler)(void);
@property (nonatomic, assign) CGFloat timerInterval;
@property (nonatomic, assign) CGFloat animeInterval;
// 🔥
@property (nonatomic, assign) BOOL isRunning;
@property (nonatomic, assign) BOOL isStopping;
@end @end
@@ -23,12 +29,19 @@
- (void)dealloc - (void)dealloc
{ {
[self stopCountdown]; NSLog(@"[Combo effect] 🗑️ CountdownRingView dealloc开始 - %p", self);
// 🔥
[self cleanup];
NSLog(@"[Combo effect] 🗑️ CountdownRingView dealloc完成 - %p", self);
} }
- (instancetype)initWithFrame:(CGRect)frame duration:(NSInteger)duration { - (instancetype)initWithFrame:(CGRect)frame duration:(NSInteger)duration {
self = [super initWithFrame:frame]; self = [super initWithFrame:frame];
if (self) { if (self) {
self.timerInterval = 0.1;
self.animeInterval = 0.25;
self.userInteractionEnabled = NO; self.userInteractionEnabled = NO;
self.remainingTime = duration; self.remainingTime = duration;
self.totalDuration = duration; self.totalDuration = duration;
@@ -69,70 +82,183 @@
[self addSubview:self.countdownLabel]; [self addSubview:self.countdownLabel];
} }
- (void)setupCompletionHandler:(void (^)(void))completionHandler {
_completionHandler = completionHandler;
}
// //
- (void)startCountdown { - (void)startCountdown {
// 🔥
if (self.isRunning) {
NSLog(@"[Combo effect] ⚠️ 倒计时已在运行中,忽略重复启动");
return;
}
NSLog(@"[Combo effect] ⏰ 开始倒计时");
self.isRunning = YES;
[self animateRing]; [self animateRing];
[self triggerLabelAnimation];
self.timer = [NSTimer scheduledTimerWithTimeInterval:self.timerInterval
target:self
selector:@selector(updateCountdown)
userInfo:nil
repeats:YES];
NSLog(@"[Combo effect] ⏰ 倒计时已启动");
}
//
- (void)resetCountdown {
NSLog(@"[Combo effect] ⏰ 重置倒计时开始");
// 🔥
if (!self.isRunning) {
NSLog(@"[Combo effect] ⚠️ 倒计时未运行,直接启动");
[self startCountdown];
return;
}
// 🔥
@synchronized(self) {
//
[self.foregroundLayer removeAllAnimations];
//
self.remainingTime = self.totalDuration;
self.foregroundLayer.strokeEnd = 1.0;
//
[self animateRing];
}
// 🔥 UI
dispatch_async(dispatch_get_main_queue(), ^{
[self triggerLabelAnimation];
});
NSLog(@"[Combo effect] ⏰ 重置倒计时完成");
}
//
- (void)triggerLabelAnimation {
self.countdownLabel.transform = CGAffineTransformIdentity; self.countdownLabel.transform = CGAffineTransformIdentity;
[UIView animateWithDuration:0.25 [UIView animateWithDuration:self.animeInterval
animations:^{ animations:^{
self.countdownLabel.transform = CGAffineTransformMakeScale(2, 2); self.countdownLabel.transform = CGAffineTransformMakeScale(2, 2);
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
self.countdownLabel.transform = CGAffineTransformIdentity; self.countdownLabel.transform = CGAffineTransformIdentity;
}]; }];
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:@selector(updateCountdown)
userInfo:nil
repeats:YES];
}
//
- (void)resetCountdown {
//
[self.timer invalidate];
self.timer = nil;
//
self.remainingTime = self.totalDuration;
//
self.foregroundLayer.strokeEnd = 1.0;
//
[self startCountdown];
} }
// //
- (void)updateCountdown { - (void)updateCountdown {
self.remainingTime -= 0.1; // 🔥 线
@synchronized(self) {
if (self.remainingTime <= 0) { // 🔥 self
[self.timer invalidate]; if (!self) {
self.timer = nil; NSLog(@"[Combo effect] ⚠️ self已释放忽略updateCountdown调用");
return;
}
if (self.completionHandler) { if (!self.isRunning) {
self.completionHandler(); NSLog(@"[Combo effect] ⚠️ 倒计时已停止忽略updateCountdown调用");
[self stopCountdown];
return;
}
self.remainingTime -= self.timerInterval;
if (self.remainingTime <= 0) {
NSLog(@"[Combo effect] ⏰ 倒计时结束,准备触发回调");
// 🔥 Timer
void (^completion)(void) = self.completionHandler;
[self stopCountdown];
// 🔥
if (completion) {
NSLog(@"[Combo effect] ⏰ 执行倒计时结束回调");
completion(); //
}
} }
} }
} }
- (void)stopCountdown { - (void)stopCountdown {
self.completionHandler = nil; // 🔥
[self.timer invalidate]; if (self.isStopping) {
self.timer = nil; NSLog(@"[Combo effect] ⚠️ 已在停止过程中,忽略重复调用");
return;
}
if (!self.isRunning) {
NSLog(@"[Combo effect] ⚠️ 倒计时未运行,无需停止");
return;
}
NSLog(@"[Combo effect] ⏰ 停止倒计时开始");
self.isStopping = YES;
@synchronized(self) {
// 🔥 Timer
if (self.timer) {
[self.timer invalidate];
self.timer = nil;
NSLog(@"[Combo effect] ⏰ Timer已停止");
}
//
if (self.foregroundLayer) {
[self.foregroundLayer removeAllAnimations];
NSLog(@"[Combo effect] ⏰ 动画已停止");
}
// UI
if (self.countdownLabel) {
self.countdownLabel.transform = CGAffineTransformIdentity;
}
//
self.isRunning = NO;
// 🔥
self.completionHandler = nil;
}
self.isStopping = NO;
NSLog(@"[Combo effect] ⏰ 停止倒计时完成");
} }
// //
- (void)animateRing { - (void)animateRing {
// 🔥
[self.foregroundLayer removeAnimationForKey:@"ringAnimation"];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation.duration = self.remainingTime; // animation.duration = self.remainingTime; //
animation.fromValue = @1.0; animation.fromValue = @1.0;
animation.toValue = @0.0; animation.toValue = @0.0;
animation.fillMode = kCAFillModeForwards; animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO; animation.removedOnCompletion = YES; // 🔥
[self.foregroundLayer addAnimation:animation forKey:@"ringAnimation"]; [self.foregroundLayer addAnimation:animation forKey:@"ringAnimation"];
NSLog(@"[Combo effect] 🎬 环形动画已启动,时长: %.1f秒", self.remainingTime);
}
// 🔥 dealloc
- (void)cleanup {
NSLog(@"[Combo effect] 🗑️ 完全清理开始");
@synchronized(self) {
[self stopCountdown];
// 🔥
self.completionHandler = nil;
}
NSLog(@"[Combo effect] 🗑️ 完全清理完成");
} }
@end @end

View File

@@ -28,8 +28,11 @@
@property(nonatomic, strong) UIImpactFeedbackGenerator *feedbackGenerator; @property(nonatomic, strong) UIImpactFeedbackGenerator *feedbackGenerator;
@property (nonatomic, strong) NSTimer *longPressTimer; @property (nonatomic, strong) id<NSObject> observer_receiveLuckGiftWinning;
@property (nonatomic, strong) id<NSObject> observer_ComboCountReset;
// 🔥
@property (nonatomic, assign) BOOL isDeallocating;
@end @end
@@ -37,15 +40,36 @@
- (void)dealloc - (void)dealloc
{ {
NSLog(@"[Combo effect] 🗑️ GiftComboView dealloc开始 - %p", self);
// 🔥
self.isDeallocating = YES;
// 🔥 Timer
[self.countdownRingView stopCountdown]; [self.countdownRingView stopCountdown];
[self.countdownRingView removeFromSuperview]; [self.countdownRingView removeFromSuperview];
self.countdownRingView = nil; self.countdownRingView = nil;
[self.playImageView stopAnimation]; [self.playImageView stopAnimation];
[self.playImageView clear]; [self.playImageView clear];
self.playImageView.delegate = nil; self.playImageView.delegate = nil;
// 🔥
if (self.observer_ComboCountReset) {
[[NSNotificationCenter defaultCenter] removeObserver:self.observer_ComboCountReset];
self.observer_ComboCountReset = nil;
}
if (self.observer_receiveLuckGiftWinning) {
[[NSNotificationCenter defaultCenter] removeObserver:self.observer_receiveLuckGiftWinning];
self.observer_receiveLuckGiftWinning = nil;
}
[[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSNotificationCenter defaultCenter] removeObserver:self];
[self.longPressTimer invalidate];
self.longPressTimer = nil; // 🔥
[self.updateGoldQueue removeAllObjects];
self.feedbackGenerator = nil;
NSLog(@"[Combo effect] 🗑️ GiftComboView dealloc完成 - %p", self);
} }
- (instancetype)init { - (instancetype)init {
@@ -73,33 +97,41 @@
- (void)setupSVGAParser { - (void)setupSVGAParser {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
SVGAParser *parser = [SVGAParser new]; SVGAParser *parser = [SVGAParser new];
@kWeakify(self); @kWeakify(self);
[parser parseWithNamed:@"Combo_Boom" [parser parseWithNamed:@"Combo_Boom"
inBundle:nil inBundle:nil
completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) { completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) {
@kStrongify(self); @kStrongify(self);
dispatch_async(dispatch_get_main_queue(), ^{ if (!self) return; // 🔥 self
self.svgaVideoEntity = videoItem;
dispatch_async(dispatch_get_main_queue(), ^{
@kStrongify(self);
if (!self) return; // 🔥 self
self.svgaVideoEntity = videoItem; self.svgaVideoEntity = videoItem;
self.playImageView.loops = 1; self.playImageView.loops = 1;
self.playImageView.clearsAfterStop = NO; self.playImageView.clearsAfterStop = NO;
self.playImageView.videoItem = videoItem; self.playImageView.videoItem = videoItem;
}); });
} failureBlock:^(NSError * _Nullable error) { } failureBlock:^(NSError * _Nullable error) {
// NSLog(@"%@", error); //
}]; }];
}); });
} }
- (void)setupNotification { - (void)setupNotification {
// 🔥 使
@kWeakify(self); @kWeakify(self);
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"receiveLuckGiftWinning" object:nil];
[[NSNotificationCenter defaultCenter] addObserverForName:@"receiveLuckGiftWinning" // 使
_observer_receiveLuckGiftWinning = [[NSNotificationCenter defaultCenter] addObserverForName:@"receiveLuckGiftWinning"
object:nil object:nil
queue:[NSOperationQueue mainQueue] queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * _Nonnull notification) { usingBlock:^(NSNotification * _Nonnull notification) {
@kStrongify(self); @kStrongify(self);
if (!self) return; // 🔥 self
if ([notification.object isKindOfClass:[NSString class]]) { if ([notification.object isKindOfClass:[NSString class]]) {
[self handleStringNotification:notification.object]; [self handleStringNotification:notification.object];
} else if ([notification.object isKindOfClass:[NSDictionary class]]) { } else if ([notification.object isKindOfClass:[NSDictionary class]]) {
@@ -108,11 +140,13 @@
}]; }];
// //
[[NSNotificationCenter defaultCenter] addObserverForName:@"ComboCountReset" _observer_ComboCountReset = [[NSNotificationCenter defaultCenter] addObserverForName:@"ComboCountReset"
object:nil object:nil
queue:[NSOperationQueue mainQueue] queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * _Nonnull notification) { usingBlock:^(NSNotification * _Nonnull notification) {
@kStrongify(self); @kStrongify(self);
if (!self) return; // 🔥 self
NSLog(@"[Combo effect] 📢 收到连击计数重置通知"); NSLog(@"[Combo effect] 📢 收到连击计数重置通知");
[self resetComboCount]; [self resetComboCount];
}]; }];
@@ -185,7 +219,7 @@
} }
- (void)endCombo { - (void)endCombo {
[self.countdownRingView removeFromSuperview];
} }
- (void)setupUI { - (void)setupUI {
@@ -249,7 +283,7 @@
- (void)setupTimer { - (void)setupTimer {
NSLog(@"[Combo effect] ⏰ 设置连击倒计时"); NSLog(@"[Combo effect] ⏰ 设置连击倒计时");
@kWeakify(self); @kWeakify(self);
[self.countdownRingView setCompletionHandler:^{ [self.countdownRingView setupCompletionHandler:^{
@kStrongify(self); @kStrongify(self);
NSLog(@"[Combo effect] ⏰ 连击倒计时结束,触发强制移除"); NSLog(@"[Combo effect] ⏰ 连击倒计时结束,触发强制移除");
self.userInteractionEnabled = NO; self.userInteractionEnabled = NO;
@@ -481,7 +515,8 @@
} }
- (SVGAImageView *)playImageView { - (SVGAImageView *)playImageView {
if (_playImageView == nil) { // 🔥
if (_playImageView == nil && !self.isDeallocating) {
_playImageView = [[SVGAImageView alloc]init]; _playImageView = [[SVGAImageView alloc]init];
_playImageView.contentMode = UIViewContentModeScaleAspectFill; _playImageView.contentMode = UIViewContentModeScaleAspectFill;
_playImageView.hidden = NO; _playImageView.hidden = NO;
@@ -491,39 +526,16 @@
} }
- (CountdownRingView *)countdownRingView { - (CountdownRingView *)countdownRingView {
if (!_countdownRingView) { // 🔥
if (!_countdownRingView && !self.isDeallocating) {
_countdownRingView = [[CountdownRingView alloc] initWithFrame:CGRectMake(0, 0, kGetScaleWidth(90), kGetScaleWidth(90)) _countdownRingView = [[CountdownRingView alloc] initWithFrame:CGRectMake(0, 0, kGetScaleWidth(90), kGetScaleWidth(90))
duration:5]; duration:5];
_countdownRingView.userInteractionEnabled = YES; _countdownRingView.userInteractionEnabled = YES;
//#if DEBUG
// UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
// longPress.minimumPressDuration = 0.1;
// [_countdownRingView addGestureRecognizer:longPress];
//#else
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap)]; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap)];
[_countdownRingView addGestureRecognizer:tap]; [_countdownRingView addGestureRecognizer:tap];
//#endif
} }
return _countdownRingView; return _countdownRingView;
} }
- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture {
if (gesture.state == UIGestureRecognizerStateBegan) {
//
[self.longPressTimer invalidate];
self.longPressTimer = [NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:@selector(handleTap)
userInfo:nil
repeats:YES];
[self handleTap];
} else if (gesture.state == UIGestureRecognizerStateEnded ||
gesture.state == UIGestureRecognizerStateCancelled ||
gesture.state == UIGestureRecognizerStateFailed) {
//
[self.longPressTimer invalidate];
self.longPressTimer = nil;
}
}
@end @end

View File

@@ -42,12 +42,6 @@ typedef NS_ENUM(NSInteger, SendGiftType) {
// 强制重置连击状态 // 强制重置连击状态
- (void)forceBoomStateReset; - (void)forceBoomStateReset;
#if DEBUG
// 调试工具
- (void)simulateComboViewDisappear;
- (void)simulateNetworkFailureDuringCombo;
#endif
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -134,12 +134,21 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
// //
- (void)removeAllComboRelatedViews { - (void)removeAllComboRelatedViews {
NSLog(@"[Combo effect] 🗑️ 开始移除连击相关视图");
// //
if (self.comboView && self.comboView.superview) { if (self.comboView && self.comboView.superview) {
NSLog(@"[Combo effect] 🗑️ 移除comboView");
[self.comboView stopTimer]; [self.comboView stopTimer];
[self.comboView endCombo]; [self.comboView endCombo];
[self.comboView removeFromSuperview]; [self.comboView removeFromSuperview];
self.comboView = nil; self.comboView = nil;
} else if (self.comboView) {
// 🔥 使superview
NSLog(@"[Combo effect] 🗑️ comboView存在但无superview直接清理");
[self.comboView stopTimer];
[self.comboView endCombo];
self.comboView = nil;
} }
// //
@@ -153,6 +162,8 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
if (self->_bravoGiftView) { if (self->_bravoGiftView) {
self.bravoGiftView.hidden = NO; self.bravoGiftView.hidden = NO;
} }
NSLog(@"[Combo effect] 🗑️ 连击相关视图移除完成");
} }
// //
@@ -186,40 +197,6 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
} }
} }
#pragma mark -
#if DEBUG
// UI
- (void)simulateComboViewDisappear {
NSLog(@"🔴 [调试] 模拟连击UI消失异常");
//
if (self.comboView && self.comboView.superview) {
[self.comboView removeFromSuperview];
self.comboView = nil;
NSLog(@"🔴 模拟异常连击UI已消失但状态未重置");
NSLog(@" 当前连击状态:%@", [[GiftComboManager sharedManager] isActive] ? @"进行中" : @"未进行");
} else {
NSLog(@"⚠️ 当前没有连击面板可以移除");
}
}
//
- (void)simulateNetworkFailureDuringCombo {
NSLog(@"🔴 [调试] 模拟网络异常导致连击错误");
//
[[GiftComboManager sharedManager] activate];
[[GiftComboManager sharedManager] reset];
//
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[GiftComboManager sharedManager] clear];
NSLog(@"🔴 已模拟网络异常,触发强制移除");
});
}
#endif
- (instancetype)initWithType:(SendGiftType)type uid:(NSString * __nullable)uid{ - (instancetype)initWithType:(SendGiftType)type uid:(NSString * __nullable)uid{
if (self = [super init]) { if (self = [super init]) {
self.modalPresentationStyle = UIModalPresentationOverFullScreen; self.modalPresentationStyle = UIModalPresentationOverFullScreen;
@@ -238,16 +215,6 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
selector:@selector(handleBoomStateForceReset:) selector:@selector(handleBoomStateForceReset:)
name:kBoomStateForceResetNotification name:kBoomStateForceResetNotification
object:nil]; object:nil];
#if DEBUG
//
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(simulateComboViewDisappear)
name:@"DebugSimulateComboViewDisappear"
object:nil];
#endif
[self initSubViews]; [self initSubViews];
[self initSubViewConstraints]; [self initSubViewConstraints];
@@ -256,6 +223,7 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
if (self.usingplaceType == SendGiftType_User) { if (self.usingplaceType == SendGiftType_User) {
return; return;
} }
NSLog(@"[Combo effect] 📱 开始注册actionCallback - usingplaceType: %ld", (long)self.usingplaceType);
@kWeakify(self); @kWeakify(self);
[[GiftComboManager sharedManager] registerActions:^(ComboActionType type) { [[GiftComboManager sharedManager] registerActions:^(ComboActionType type) {
@kStrongify(self); @kStrongify(self);
@@ -264,6 +232,18 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
} }
switch (type) { switch (type) {
case ComboAction_ShowPanel: { case ComboAction_ShowPanel: {
// 🔥
if (![[GiftComboManager sharedManager] isActive]) {
NSLog(@"[Combo effect] ⚠️ 连击未激活,跳过显示面板");
return;
}
// 🔥 usingplaceType
if (self.usingplaceType == SendGiftType_User) {
NSLog(@"[Combo effect] ⚠️ 私聊模式,跳过显示连击面板");
return;
}
self.contentView.hidden = YES; self.contentView.hidden = YES;
// if (self->_superGiftView) { // if (self->_superGiftView) {
// self.superGiftView.hidden = YES; // self.superGiftView.hidden = YES;
@@ -277,10 +257,22 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
if (self->_bravoGiftView) { if (self->_bravoGiftView) {
self.bravoGiftView.hidden = YES; self.bravoGiftView.hidden = YES;
} }
// 🔥 comboView
if (!self.comboView) {
NSLog(@"[Combo effect] 📱 创建新的comboView");
self->_comboView = [[GiftComboView alloc] init];
}
[self.view addSubview:self.comboView]; [self.view addSubview:self.comboView];
[self.comboView mas_makeConstraints:^(MASConstraintMaker *make) { [self.comboView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.view); make.edges.mas_equalTo(self.view);
}]; }];
// 🔥
if (self.giftBarView.walletInfoModel) {
[self.comboView setupCurrentGold:self.giftBarView.walletInfoModel.diamonds.doubleValue];
}
} }
break; break;
case ComboAction_RemovePanel:{ case ComboAction_RemovePanel:{
@@ -299,10 +291,13 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
} }
[self.presenter getUserWalletInfo]; [self.presenter getUserWalletInfo];
[self.comboView stopTimer]; // 🔥
[self.comboView endCombo]; if (self.comboView) {
[self.comboView removeFromSuperview]; // [self.comboView stopTimer]; // Timer
self.comboView = nil; // [self.comboView endCombo]; // combo
[self.comboView removeFromSuperview]; //
self.comboView = nil; //
}
} }
break; break;
case ComboAction_Combo_Count_Update: { case ComboAction_Combo_Count_Update: {
@@ -741,7 +736,14 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
/// / /// /
- (void)readyForCombo:(XPGiftCountModel *)giftCount - (void)readyForCombo:(XPGiftCountModel *)giftCount
gift:(GiftInfoModel *)giftInfo { gift:(GiftInfoModel *)giftInfo {
NSLog(@"[Combo effect] 🔧 准备连击状态 - giftType: %ld, segmentType: %ld", (long)giftInfo.giftType, (long)self.segmentType); NSLog(@"[Combo effect] 🔧 准备连击状态 - giftType: %ld, segmentType: %ld, usingplaceType: %ld", (long)giftInfo.giftType, (long)self.segmentType, (long)self.usingplaceType);
// 🔥 usingplaceType
if (self.usingplaceType == SendGiftType_User) {
NSLog(@"[Combo effect] ❌ 私聊模式不支持连击");
[[GiftComboManager sharedManager] deactivate];
return;
}
if (self.segmentType == GiftSegmentType_Pack) { if (self.segmentType == GiftSegmentType_Pack) {
NSLog(@"[Combo effect] ❌ 背包礼物不支持连击"); NSLog(@"[Combo effect] ❌ 背包礼物不支持连击");
@@ -1206,43 +1208,38 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
} else { } else {
self.giftBarView.walletInfoModel = receiveInfo.userPurse; self.giftBarView.walletInfoModel = receiveInfo.userPurse;
} }
NSLog(@"[Combo effect] 📱 检查连击状态 - enableCombo: %@", [GiftComboManager sharedManager].enableCombo ? @"YES" : @"NO");
// @kWeakify(self);
// dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC));
// dispatch_after(delayTime, dispatch_get_main_queue(), ^{
// @kStrongify(self); if ([GiftComboManager sharedManager].enableCombo && self.usingplaceType == SendGiftType_Room) {
if (self) { NSLog(@"[Combo effect] 📱 启用连击模式,重置连击状态");
NSLog(@"[Combo effect] 📱 检查连击状态 - enableCombo: %@", [GiftComboManager sharedManager].enableCombo ? @"YES" : @"NO");
// originDic
// originDic NSNumber *originComboCount = originDic[@"comboCount"];
NSNumber *originComboCount = originDic[@"comboCount"]; if (!originComboCount) {
if (!originComboCount) { NSMutableDictionary *editDic = originDic.mutableCopy;
NSMutableDictionary *editDic = originDic.mutableCopy; editDic[@"comboCount"] = @(1);
editDic[@"comboCount"] = @(1); originDic = editDic.copy;
originDic = editDic.copy;
}
NSLog(@"[Combo effect] 📱 originDic 连击计数检查 - comboCount: %@", originComboCount);
if ([GiftComboManager sharedManager].enableCombo) {
NSLog(@"[Combo effect] 📱 启用连击模式,重置连击状态");
[[GiftComboManager sharedManager] reset];
[self sendCustomMessage:receiveInfo oringinDic:originDic];
[self.comboView setupCurrentGold:receiveInfo.userPurse.diamonds.doubleValue];
@kWeakify(self);
[[GiftComboManager sharedManager] setHandleComboSuccess:^(GiftReceiveInfoModel * _Nonnull receiveModel, NSMutableDictionary * _Nonnull originDic) {
@kStrongify(self);
NSLog(@"[Combo effect] 📱 连击回调中发送消息 - comboCount: %@", originDic[@"comboCount"]);
[self sendCustomMessage:receiveInfo oringinDic:originDic.copy];
}];
} else {
NSLog(@"[Combo effect] 📱 未启用连击模式,直接发送消息");
[self sendCustomMessage:receiveInfo oringinDic:originDic];
}
} }
// }); NSLog(@"[Combo effect] 📱 originDic 连击计数检查 - comboCount: %@", originComboCount);
/// [[GiftComboManager sharedManager] reset];
// [self sendGraffitiGiftMessage];
// 🔥 访comboView
// [self.comboView setupCurrentGold:receiveInfo.userPurse.diamonds.doubleValue];
@kWeakify(self);
[[GiftComboManager sharedManager] setHandleComboSuccess:^(GiftReceiveInfoModel * _Nonnull receiveModel, NSMutableDictionary * _Nonnull originDic) {
@kStrongify(self);
NSLog(@"[Combo effect] 📱 连击回调中发送消息 - comboCount: %@", originDic[@"comboCount"]);
[self sendCustomMessage:receiveInfo oringinDic:originDic.copy];
}];
} else {
NSLog(@"[Combo effect] 📱 未启用连击模式,直接发送消息");
}
[self sendCustomMessage:receiveInfo oringinDic:originDic];
} }
/// ///

71
micButton状态表格.md Normal file
View File

@@ -0,0 +1,71 @@
# micButton 状态表格
## micButton 可用状态总览
| 场景 | 用户状态 | micButton显示 | micButton可用性 | micState值 | 音频状态 | 备注 |
|------|----------|---------------|----------------|------------|----------|------|
| **用户上麦前** | 未在麦位 | 隐藏 | 不可用 | MICState_None | 无音频 | isOnMic = NO |
| **用户刚上麦** | 刚上麦位 | 显示 | 可用 | MICState_Close | 静音 | 默认静音状态localMuted = YES |
| **用户开麦** | 在麦位 | 显示开麦状态 | 可用 | MICState_Open | 开启音频 | 用户可以说话 |
| **用户关麦** | 在麦位 | 显示关麦状态 | 可用 | MICState_Close | 静音 | 用户无法说话 |
| **用户下麦** | 离开麦位 | 隐藏 | 不可用 | MICState_None | 无音频 | isOnMic = NO |
## 不同场景下的状态变化
### 1. 用户加入/离开舞台
| 操作 | micButton状态变化 | 音频状态变化 | UI更新 |
|------|------------------|--------------|--------|
| 用户上麦 | 隐藏 → 显示(关麦状态) | 无音频 → 静音 | isOnMic: NO → YES |
| 用户下麦 | 显示 → 隐藏 | 当前状态 → 无音频 | isOnMic: YES → NO |
### 2. 其他用户加入/离开舞台
| 操作 | 当前用户micButton | 影响范围 | 说明 |
|------|------------------|----------|------|
| 他人上麦 | 无变化 | 仅更新麦位显示 | micButton状态不受影响 |
| 他人下麦 | 无变化 | 仅更新麦位显示 | micButton状态不受影响 |
### 3. 房间最小化场景
| 状态 | micButton处理 | 音频处理 | 数据同步 |
|------|---------------|----------|----------|
| 最小化时 | 监听队列变化 | 继续广播音频 | selfNeedBroadcast基于MicroMicStateType_Open |
| 恢复显示 | recheckMicState同步 | 保持当前状态 | 从XPSkillCardPlayerManager.micState同步 |
## micButton 状态枚举详解
| MICState枚举 | 数值 | 含义 | UI表现 | 用户能否说话 |
|-------------|------|------|--------|-------------|
| MICState_None | 0 | 无麦克风状态 | micButton隐藏 | ❌ 否 |
| MICState_Close | 1 | 麦克风关闭 | 显示关麦图标 | ❌ 否 |
| MICState_Open | 2 | 麦克风开启 | 显示开麦图标 | ✅ 是 |
## 关键时序和同步机制
### 状态更新流程
```
用户操作 → StageView处理 → 麦位队列更新 → onMicroQueueUpdate回调
→ XPRoomViewController分发 → XPRoomMenuContainerView更新
→ micButton状态/显示更新 → recheckMicState同步检查
```
### 重要同步点
| 时机 | 同步操作 | 目的 |
|------|----------|------|
| viewWillAppear | recheckMicState | 确保UI与全局状态一致 |
| 房间退出 | micState = MICState_None | 重置状态 |
| 麦位变化 | onMicroQueueUpdate | 实时更新UI |
## 特殊情况处理
| 特殊情况 | micButton行为 | 处理逻辑 |
|----------|---------------|----------|
| 网络断线重连 | 重新同步状态 | recheckMicState确保一致性 |
| 被踢出麦位 | 立即隐藏 | NIMChatroomEventTypeKicked触发 |
| 房间模式切换 | 根据新模式调整 | 不同RoomModeType有不同处理 |
| 禁麦状态 | 显示但可能限制功能 | isNoProhibitMic控制 |
---
**总结**: micButton的可用状态主要取决于用户是否在麦位(isOnMic)在麦位时根据MICState显示不同状态用户只有在MICState_Open时才能说话。