新增 micButton 状态表格文档,详细记录了 micButton 在不同用户状态下的显示和可用性,以及状态变化的关键时序和同步机制。同时,新增 GiftComboManager 线程优化总结文档,优化了线程处理和网络请求逻辑,提升了代码性能和可维护性。更新了 CountdownRingView 和 GiftComboView 的内存管理和状态处理逻辑,确保资源的正确释放和避免内存泄漏。
This commit is contained in:
114
DOC/log.txt
114
DOC/log.txt
@@ -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] 📭 动画队列为空,停止处理
|
@@ -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;
|
||||
|
@@ -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; // UI动画队列(GiftReceiveInfoModel)
|
||||
@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;
|
||||
}
|
||||
|
||||
// 🔥 修复:如果actionCallback为空,延迟执行reset
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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. **自动化测试**:添加自动化测试确保接口变更不会破坏功能
|
||||
|
@@ -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%**
|
||||
- **代码逻辑更清晰**
|
||||
- **维护成本显著降低**
|
||||
|
181
YuMi/Modules/YMRoom/View/SendGiftView/GiftComboManager_优化总结.md
Normal file
181
YuMi/Modules/YMRoom/View/SendGiftView/GiftComboManager_优化总结.md
Normal 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%
|
||||
- **稳定性增强**:完善的线程安全机制和错误处理
|
||||
- **可维护性**:清晰的代码结构和详细的监控机制
|
||||
|
||||
这些优化确保了连击功能的高性能和稳定性,为用户提供流畅的体验。
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -42,12 +42,6 @@ typedef NS_ENUM(NSInteger, SendGiftType) {
|
||||
// 强制重置连击状态
|
||||
- (void)forceBoomStateReset;
|
||||
|
||||
#if DEBUG
|
||||
// 调试工具
|
||||
- (void)simulateComboViewDisappear;
|
||||
- (void)simulateNetworkFailureDuringCombo;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@@ -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
71
micButton状态表格.md
Normal 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时才能说话。
|
Reference in New Issue
Block a user