// // BannerScheduler.m // YuMi // // Created by AI Assistant on 2025/1/13. // #import "BannerScheduler.h" @interface BannerScheduler () @property (nonatomic, strong) NSMutableArray *bannerQueue; @property (nonatomic, assign) BOOL isPlaying; @property (nonatomic, assign) BOOL isPaused; @end @implementation BannerScheduler #pragma mark - Initialization - (instancetype)initWithDelegate:(id)delegate { if (self = [super init]) { _delegate = delegate; _bannerQueue = [NSMutableArray array]; _isPlaying = NO; _isPaused = NO; } return self; } #pragma mark - Public Methods - (void)enqueueBanner:(id)banner { if (!banner) { NSLog(@"⚠️ BannerScheduler: 尝试添加空的 Banner"); return; } NSLog(@"🔄 BannerScheduler: 添加 Banner 到队列 - 类型: %@, 队列长度: %ld", [banner class], (long)self.bannerQueue.count); [self.bannerQueue addObject:banner]; // 如果当前没有在播放且未暂停,则开始处理 if (!self.isPlaying && !self.isPaused) { [self processNextBanner]; } } - (void)processNextBanner { if (self.isPaused) { NSLog(@"⏸️ BannerScheduler: 调度器已暂停,跳过处理"); return; } // 🔧 新增:检查 delegate 是否有效 if (!self.delegate) { NSLog(@"⚠️ BannerScheduler: delegate 已失效,停止处理"); [self clearQueue]; self.isPlaying = NO; return; } if (self.isPaused) { NSLog(@"⏸️ BannerScheduler: 调度器已暂停,跳过处理"); return; } if (self.bannerQueue.count == 0) { NSLog(@"🔄 BannerScheduler: 队列为空,停止播放"); self.isPlaying = NO; return; } if (self.isPlaying) { NSLog(@"🔄 BannerScheduler: 已有 Banner 正在播放,跳过处理"); return; } // 对队列进行优先级排序 [self sortQueueByPriority]; // 取出队列中的第一个 Banner id nextBanner = [self.bannerQueue firstObject]; [self.bannerQueue removeObjectAtIndex:0]; NSLog(@"🔄 BannerScheduler: 开始播放 Banner - 类型: %@, 剩余队列: %ld", [nextBanner class], (long)self.bannerQueue.count); self.isPlaying = YES; // 通知代理开始播放 if ([self.delegate respondsToSelector:@selector(bannerScheduler:didStartPlayingBanner:)]) { [self.delegate bannerScheduler:self didStartPlayingBanner:nextBanner]; } // 通知代理播放 Banner if ([self.delegate respondsToSelector:@selector(bannerScheduler:shouldPlayBanner:)]) { [self.delegate bannerScheduler:self shouldPlayBanner:nextBanner]; } } - (void)clearQueue { NSLog(@"🗑️ BannerScheduler: 清空 Banner 队列 - 原有数量: %ld", (long)self.bannerQueue.count); [self.bannerQueue removeAllObjects]; } - (void)sortQueueByPriority { // 保持先进先出(FIFO)策略,不需要排序 // 队列顺序就是添加顺序,确保公平性 NSLog(@"🔄 BannerScheduler: 使用先进先出策略,保持队列原有顺序"); } - (void)pause { if (self.isPaused) { NSLog(@"⏸️ BannerScheduler: 调度器已经处于暂停状态"); return; } NSLog(@"⏸️ BannerScheduler: 暂停调度器"); self.isPaused = YES; } - (void)resume { NSLog(@"▶️ BannerScheduler: 恢复调度器"); self.isPaused = NO; // 如果当前没有在播放,则开始处理队列 if (!self.isPlaying) { [self processNextBanner]; } } - (BOOL)isQueueEmpty { return self.bannerQueue.count == 0; } - (nullable id)bannerAtIndex:(NSInteger)index { if (index < 0 || index >= self.bannerQueue.count) { return nil; } return [self.bannerQueue objectAtIndex:index]; } - (BOOL)removeBannerAtIndex:(NSInteger)index { if (index < 0 || index >= self.bannerQueue.count) { return NO; } id removedBanner = [self.bannerQueue objectAtIndex:index]; [self.bannerQueue removeObjectAtIndex:index]; NSLog(@"🗑️ BannerScheduler: 从队列中移除 Banner - 索引: %ld, 类型: %@", (long)index, [removedBanner class]); return YES; } - (NSString *)queueStatusDescription { NSMutableString *description = [NSMutableString string]; [description appendFormat:@"BannerScheduler 状态:\n"]; [description appendFormat:@"- 播放状态: %@\n", self.isPlaying ? @"播放中" : @"空闲"]; [description appendFormat:@"- 暂停状态: %@\n", self.isPaused ? @"已暂停" : @"运行中"]; [description appendFormat:@"- 队列长度: %ld\n", (long)self.bannerQueue.count]; if (self.bannerQueue.count > 0) { [description appendString:@"- 队列内容:\n"]; for (NSInteger i = 0; i < self.bannerQueue.count; i++) { id banner = self.bannerQueue[i]; [description appendFormat:@" [%ld] 类型: %@\n", (long)i, [banner class]]; } } return description; } #pragma mark - Public Properties - (NSInteger)queueCount { return self.bannerQueue.count; } #pragma mark - Internal Methods /** * 标记 Banner 播放完成 * 这个方法应该由代理在 Banner 播放完成后调用 */ - (void)markBannerFinished { if (!self.isPlaying) { NSLog(@"⚠️ BannerScheduler: 尝试标记未播放的 Banner 为完成"); return; } // 防止过快标记完成,确保 banner 有足够的显示时间 static NSTimeInterval lastFinishTime = 0; NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970]; // 如果距离上次完成时间太短,延迟处理 if (currentTime - lastFinishTime < 0.3) { NSLog(@"⏳ BannerScheduler: Banner 完成间隔过短,延迟处理 (间隔: %.2f秒)", currentTime - lastFinishTime); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self markBannerFinished]; }); return; } lastFinishTime = currentTime; NSLog(@"✅ BannerScheduler: Banner 播放完成"); self.isPlaying = NO; // 通知代理播放完成 if ([self.delegate respondsToSelector:@selector(bannerSchedulerDidFinishPlaying:)]) { [self.delegate bannerSchedulerDidFinishPlaying:self]; } // 处理队列中的下一个 Banner [self processNextBanner]; } /** * 调试方法:检查调度器状态 * @return 调试信息字符串 */ - (NSString *)debugStatus { NSMutableString *debugInfo = [NSMutableString string]; [debugInfo appendFormat:@"BannerScheduler Debug Status:\n"]; [debugInfo appendFormat:@"- 播放状态: %@\n", self.isPlaying ? @"播放中" : @"空闲"]; [debugInfo appendFormat:@"- 暂停状态: %@\n", self.isPaused ? @"已暂停" : @"运行中"]; [debugInfo appendFormat:@"- 队列长度: %ld\n", (long)self.bannerQueue.count]; if (self.bannerQueue.count > 0) { [debugInfo appendString:@"- 队列内容:\n"]; for (NSInteger i = 0; i < self.bannerQueue.count; i++) { id banner = self.bannerQueue[i]; [debugInfo appendFormat:@" [%ld] 类型: %@\n", (long)i, [banner class]]; } } return debugInfo; } @end