新增 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
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 3
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 4
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 5
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 6
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 7
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 8
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 9
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 10
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 11
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 12
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 13
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 14
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 15
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 16
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 17
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 18
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 19
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 20
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 21
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 22
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 23
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 24
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 25
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 26
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 27
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 28
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 29
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 30
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 31
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 32
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 33
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 34
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 35
-[GiftAnimationManager processNextGift]_block_invoke [Line 93][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 0
-[XPSendGiftView xPGiftBarView:didClickSendGift:] [Line 771][Combo effect] 🎁 开始送礼物流程
-[XPSendGiftView readyForCombo:gift:] [Line 727][Combo effect] 🔧 准备连击状态 - giftType: 18, segmentType: 8
-[XPSendGiftView readyForCombo:gift:] [Line 745][Combo effect] ✅ 礼物支持连击,启用连击功能
-[GiftComboManager activate] [Line 197][Combo effect] 🔧 激活连击功能
-[GiftComboManager configureWithGiftInfo:targetUIDs:roomUID:sessionID:userInfo:countModel:sourceType:sendType:giftNum:] [Line 696][Combo effect] 🔧 统一配置连击参数
-[GiftComboManager configureWithGiftInfo:targetUIDs:roomUID:sessionID:userInfo:countModel:sourceType:sendType:giftNum:] [Line 709][Combo effect] ✅ 连击参数配置完成 - giftId: 2263, targetCount: 1
-[XPSendGiftView readyForCombo:gift:] [Line 761][Combo effect] ✅ 连击状态准备完成
-[GiftComboManager printComboState] [Line 377][Combo effect] 📊 当前连击状态:
-[GiftComboManager printComboState] [Line 378][Combo effect] - isCombing: NO
-[GiftComboManager printComboState] [Line 379][Combo effect] - enableCombo: YES
-[GiftComboManager printComboState] [Line 380][Combo effect] - combo: 1
-[GiftComboManager printComboState] [Line 381][Combo effect] - hasGiftInfo: YES
-[GiftComboManager printComboState] [Line 382][Combo effect] - targetCount: 1
-[GiftComboManager printComboState] [Line 383][Combo effect] - errorMessage:
-[XPSendGiftView xPGiftBarView:didClickSendGift:] [Line 778][Combo effect] ✅ 连击功能已启用准备调用resetCombo
-[XPSendGiftView sendGiftSuccess:originDic:uidCount:] [Line 1198][Combo effect] 📱 检查连击状态 - enableCombo: YES
-[XPSendGiftView sendGiftSuccess:originDic:uidCount:] [Line 1207][Combo effect] 📱 originDic 连击计数检查 - comboCount: (null)
-[XPSendGiftView sendGiftSuccess:originDic:uidCount:] [Line 1210][Combo effect] 📱 启用连击模式,重置连击状态
-[GiftComboManager reset] [Line 141][Combo effect] 🔄 开始连击重置 - combo: 1 -> 1, enableCombo: YES, actionCallback: 为空
-[GiftComboManager reset] [Line 154][Combo effect] 🔍 重置后验证 - combo: 1
-[GiftComboManager reset] [Line 173][Combo effect] ⚠️ actionCallback为空不显示连击面板
-[GiftComboManager reset] [Line 177][Combo effect] 🎮 隐藏房间UI元素
-[GiftComboManager reset] [Line 186][Combo effect] ✅ 连击重置完成 - isCombing: NO
-[XPSendGiftView sendCustomMessage:oringinDic:] [Line 425][Combo effect] 📨 云信消息连击计数检查 - comboCount: 1, giftId: 2263
-[GiftComboView setupTimer] [Line 284][Combo effect] ⏰ 设置连击倒计时
-[CountdownRingView startCountdown] [Line 97][Combo effect] 开始倒计时
-[CountdownRingView animateRing] [Line 246][Combo effect] 🎬 环形动画已启动,时长: 5.0秒
-[CountdownRingView startCountdown] [Line 109][Combo effect] ⏰ 倒计时已启动
-[GiftComboView setupTimer] [Line 293][Combo effect] ⏰ 连击倒计时已启动
-[GiftComboView updateCount] [Line 171][Combo effect] 🔢 更新连击次数显示 - combo: 1
-[GiftComboManager printComboState] [Line 377][Combo effect] 📊 当前连击状态:
-[GiftComboManager printComboState] [Line 378][Combo effect] - isCombing: NO
-[GiftComboManager printComboState] [Line 379][Combo effect] - enableCombo: YES
-[GiftComboManager printComboState] [Line 380][Combo effect] - combo: 1
-[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
delegate:self];
imagePickerVc.modalPresentationStyle = UIModalPresentationOverFullScreen;
imagePickerVc.allowPickingVideo = NO;
imagePickerVc.allowPickingVideo = displayGIF;
imagePickerVc.allowTakeVideo = NO;
if (displayGIF) {
imagePickerVc.allowTakePicture = NO;

View File

@@ -32,7 +32,6 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
@property (nonatomic, strong) NSMutableArray *requestQueue;
// GiftReceiveInfoModel NSDictionary
// @property (nonatomic, strong) NSMutableArray *giftComboQueue;
@property (nonatomic, strong) NSMutableArray *networkQueue; // AttachmentModel
@property (nonatomic, strong) NSMutableArray *uiQueue; // UIGiftReceiveInfoModel
@property (nonatomic, strong) dispatch_source_t comboFlagTimer;
@@ -71,10 +70,23 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
#pragma mark -
- (void)dealloc {
NSLog(@"[Combo effect] 🗑️ GiftComboManager dealloc开始 - %p", self);
// 🔥 timer
[self forceStopAllTimers];
// UI
[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 {
@@ -128,28 +140,51 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
self.enableCombo ? @"YES" : @"NO",
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
_combo = 1;
_errorMessage = @"";
//
NSLog(@"[Combo effect] 🔍 重置后验证 - combo: %ld", (long)self.combo);
NSLog(@"[Combo effect] 🔍 重置后验证 - combo: %ld, enableCombo: %@", (long)self.combo, self.enableCombo ? @"YES" : @"NO");
// GiftComboView
[[NSNotificationCenter defaultCenter] postNotificationName:@"ComboCountReset" object:nil];
// 🔥
self.isCombing = YES;
// - 线UI
if (self.actionCallback && self.enableCombo) {
if (self.actionCallback) {
NSLog(@"[Combo effect] 📱 触发连击面板显示回调");
@kWeakify(self);
[self safeExecuteUIBlock:^{
@kStrongify(self);
self.actionCallback(ComboAction_ShowPanel);
if (self && self.actionCallback) {
self.actionCallback(ComboAction_ShowPanel);
}
}];
self.isCombing = YES;
} else if (self.actionCallback && !self.enableCombo) {
NSLog(@"[Combo effect] ⚠️ enableCombo为NO不显示连击面板");
} else if (!self.actionCallback) {
} else {
NSLog(@"[Combo effect] ⚠️ actionCallback为空不显示连击面板");
}
@@ -158,10 +193,12 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
@kWeakify(self);
[self safeExecuteUIBlock:^{
@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 {
@@ -201,15 +238,18 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
- (void)clear {
NSLog(@"[Combo effect] 🗑️ 清除连击状态");
[self forceBoomStateReset];
// UI - 线UI
// 🔥 线UI
if (self.actionCallback) {
NSLog(@"[Combo effect] 📱 触发连击面板移除回调");
@kWeakify(self);
[self safeExecuteUIBlock:^{
@kStrongify(self);
self.actionCallback(ComboAction_RemovePanel);
if (self && self.actionCallback) {
self.actionCallback(ComboAction_RemovePanel);
}
}];
}
}
@@ -293,11 +333,15 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
//
- (void)forceStopAllTimers {
NSLog(@"[Combo effect] ⏰ 强制停止所有Timer");
//
if (self.timer) {
dispatch_source_cancel(self.timer);
self.timer = nil; //
self.timer = nil;
}
// UI
if (self.comboFlagTimer) {
dispatch_source_cancel(self.comboFlagTimer);
self.comboFlagTimer = nil;
@@ -561,7 +605,13 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
// -
@kWeakify(self);
dispatch_source_set_event_handler(self.timer, ^{
@kStrongify(self);
if (!self) {
NSLog(@"[Combo effect] ⚠️ self已释放忽略timer回调");
return;
}
[self processRequestQueue];
[self processGiftComboQueue];
});
@@ -577,17 +627,17 @@ NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotific
//
- (void)stopProcessingQueue {
if (self.timer) {
if (self.requestQueue.count == 0 && self.networkQueue.count == 0) {
//
dispatch_source_cancel(self.timer);
// timer nil
@kWeakify(self);
dispatch_source_set_cancel_handler(self.timer, ^{
@kStrongify(self);
// 🔥 timer
dispatch_source_cancel(self.timer);
// timer nil
@kWeakify(self);
dispatch_source_set_cancel_handler(self.timer, ^{
@kStrongify(self);
if (self) {
self.timer = nil;
});
}
}
});
}
}

View File

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

View File

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

View File

@@ -16,6 +16,12 @@
@property (nonatomic, assign) CGFloat remainingTime;
@property (nonatomic, assign) NSInteger totalDuration;
@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
@@ -23,12 +29,19 @@
- (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 {
self = [super initWithFrame:frame];
if (self) {
self.timerInterval = 0.1;
self.animeInterval = 0.25;
self.userInteractionEnabled = NO;
self.remainingTime = duration;
self.totalDuration = duration;
@@ -69,70 +82,183 @@
[self addSubview:self.countdownLabel];
}
- (void)setupCompletionHandler:(void (^)(void))completionHandler {
_completionHandler = completionHandler;
}
//
- (void)startCountdown {
// 🔥
if (self.isRunning) {
NSLog(@"[Combo effect] ⚠️ 倒计时已在运行中,忽略重复启动");
return;
}
NSLog(@"[Combo effect] ⏰ 开始倒计时");
self.isRunning = YES;
[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;
[UIView animateWithDuration:0.25
[UIView animateWithDuration:self.animeInterval
animations:^{
self.countdownLabel.transform = CGAffineTransformMakeScale(2, 2);
} completion:^(BOOL finished) {
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 {
self.remainingTime -= 0.1;
if (self.remainingTime <= 0) {
[self.timer invalidate];
self.timer = nil;
// 🔥 线
@synchronized(self) {
// 🔥 self
if (!self) {
NSLog(@"[Combo effect] ⚠️ self已释放忽略updateCountdown调用");
return;
}
if (self.completionHandler) {
self.completionHandler();
if (!self.isRunning) {
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 {
self.completionHandler = nil;
[self.timer invalidate];
self.timer = nil;
// 🔥
if (self.isStopping) {
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 {
// 🔥
[self.foregroundLayer removeAnimationForKey:@"ringAnimation"];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation.duration = self.remainingTime; //
animation.fromValue = @1.0;
animation.toValue = @0.0;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.removedOnCompletion = YES; // 🔥
[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

View File

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

View File

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

View File

@@ -134,12 +134,21 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
//
- (void)removeAllComboRelatedViews {
NSLog(@"[Combo effect] 🗑️ 开始移除连击相关视图");
//
if (self.comboView && self.comboView.superview) {
NSLog(@"[Combo effect] 🗑️ 移除comboView");
[self.comboView stopTimer];
[self.comboView endCombo];
[self.comboView removeFromSuperview];
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) {
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{
if (self = [super init]) {
self.modalPresentationStyle = UIModalPresentationOverFullScreen;
@@ -238,16 +215,6 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
selector:@selector(handleBoomStateForceReset:)
name:kBoomStateForceResetNotification
object:nil];
#if DEBUG
//
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(simulateComboViewDisappear)
name:@"DebugSimulateComboViewDisappear"
object:nil];
#endif
[self initSubViews];
[self initSubViewConstraints];
@@ -256,6 +223,7 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
if (self.usingplaceType == SendGiftType_User) {
return;
}
NSLog(@"[Combo effect] 📱 开始注册actionCallback - usingplaceType: %ld", (long)self.usingplaceType);
@kWeakify(self);
[[GiftComboManager sharedManager] registerActions:^(ComboActionType type) {
@kStrongify(self);
@@ -264,6 +232,18 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
}
switch (type) {
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;
// if (self->_superGiftView) {
// self.superGiftView.hidden = YES;
@@ -277,10 +257,22 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
if (self->_bravoGiftView) {
self.bravoGiftView.hidden = YES;
}
// 🔥 comboView
if (!self.comboView) {
NSLog(@"[Combo effect] 📱 创建新的comboView");
self->_comboView = [[GiftComboView alloc] init];
}
[self.view addSubview:self.comboView];
[self.comboView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.view);
}];
// 🔥
if (self.giftBarView.walletInfoModel) {
[self.comboView setupCurrentGold:self.giftBarView.walletInfoModel.diamonds.doubleValue];
}
}
break;
case ComboAction_RemovePanel:{
@@ -299,10 +291,13 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
}
[self.presenter getUserWalletInfo];
[self.comboView stopTimer];
[self.comboView endCombo];
[self.comboView removeFromSuperview];
self.comboView = nil;
// 🔥
if (self.comboView) {
// [self.comboView stopTimer]; // Timer
// [self.comboView endCombo]; // combo
[self.comboView removeFromSuperview]; //
self.comboView = nil; //
}
}
break;
case ComboAction_Combo_Count_Update: {
@@ -741,7 +736,14 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
/// /
- (void)readyForCombo:(XPGiftCountModel *)giftCount
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) {
NSLog(@"[Combo effect] ❌ 背包礼物不支持连击");
@@ -1206,43 +1208,38 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
} else {
self.giftBarView.walletInfoModel = receiveInfo.userPurse;
}
// @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 (self) {
NSLog(@"[Combo effect] 📱 检查连击状态 - enableCombo: %@", [GiftComboManager sharedManager].enableCombo ? @"YES" : @"NO");
// originDic
NSNumber *originComboCount = originDic[@"comboCount"];
if (!originComboCount) {
NSMutableDictionary *editDic = originDic.mutableCopy;
editDic[@"comboCount"] = @(1);
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] 📱 检查连击状态 - enableCombo: %@", [GiftComboManager sharedManager].enableCombo ? @"YES" : @"NO");
if ([GiftComboManager sharedManager].enableCombo && self.usingplaceType == SendGiftType_Room) {
NSLog(@"[Combo effect] 📱 启用连击模式,重置连击状态");
// originDic
NSNumber *originComboCount = originDic[@"comboCount"];
if (!originComboCount) {
NSMutableDictionary *editDic = originDic.mutableCopy;
editDic[@"comboCount"] = @(1);
originDic = editDic.copy;
}
// });
///
// [self sendGraffitiGiftMessage];
NSLog(@"[Combo effect] 📱 originDic 连击计数检查 - comboCount: %@", originComboCount);
[[GiftComboManager sharedManager] reset];
// 🔥 访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时才能说话。