231 lines
6.4 KiB
Objective-C
231 lines
6.4 KiB
Objective-C
//
|
||
// 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<BannerSchedulerDelegate>)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) {
|
||
return;
|
||
}
|
||
|
||
[self.bannerQueue addObject:banner];
|
||
|
||
// 如果当前没有在播放且未暂停,则开始处理
|
||
if (!self.isPlaying && !self.isPaused) {
|
||
[self processNextBanner];
|
||
}
|
||
}
|
||
|
||
- (void)processNextBanner {
|
||
if (self.isPaused) {
|
||
return;
|
||
}
|
||
|
||
// 🔧 新增:检查 delegate 是否有效
|
||
if (!self.delegate) {
|
||
[self clearQueue];
|
||
self.isPlaying = NO;
|
||
return;
|
||
}
|
||
|
||
if (self.isPaused) {
|
||
return;
|
||
}
|
||
|
||
if (self.bannerQueue.count == 0) {
|
||
self.isPlaying = NO;
|
||
return;
|
||
}
|
||
|
||
if (self.isPlaying) {
|
||
return;
|
||
}
|
||
|
||
// 对队列进行优先级排序
|
||
[self sortQueueByPriority];
|
||
|
||
// 取出队列中的第一个 Banner
|
||
id nextBanner = [self.bannerQueue firstObject];
|
||
[self.bannerQueue removeObjectAtIndex:0];
|
||
|
||
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 {
|
||
[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
|