延时队列
延时队列在大部分实现中,是一种以时间为权重的有序堆结构实现的队列,在某个时间后执行某些任务。Java\Go 等后端语言都有现成的数据结构和实现。
但是在iOS客户端中,某些情况下也需要类似延时队列的处理方式。一个普调的串行任务队列,但是需要具备每个任务可以超时的能力。但是 iOS 没有相关现成的组件可用。
比如某些接口需要串行请求,在上一个请求回来后再请求下一个接口,但是在某些情况下网络堆积,所以我们需要一个超时时间,在当前任务超时后,接直接丢弃该任务请求下一个任务。
又比如某些动画组件,需要一个动画一个动画的显示,上一个动画完成后再开始下一个动画等。
实现
我们可以通过 GCD 串行队列和 GCD 延时来实现这个超时任务队列。
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface GRYTaskModel : NSObject
@property (nonatomic, copy) NSString *taskId;
@property (nonatomic, copy) NSString *businessId;
@property (nonatomic, copy) dispatch_block_t executeBlock;
@property (nonatomic, assign) NSUInteger timeOutDuration; // 0 means never timeout
@end
/**
* This is a simple task manager. It implements a management function similar to a DelayQueue.
* Tasks will be added to a serial queue. Tasks in the queue will be executed serially.
* After the task times out, it will be removed from the queue and the next task will be executed
*/
@interface GRYTaskManager : NSObject
/**
* Init a task manager with queue name
* @param label queue label
* @return instancetype
*/
- (instancetype)initWithQueueLabel:(NSString *)label;
/**
* Add a task to queue.
* @param task your task
*/
- (void)enqueueTask:(GRYTaskModel *)task;
/**
* Tell manager this task has completed.
* @param taskId taskId
*/
- (void)taskCompletedWithTaskId:(NSString *)taskId;
@end
NS_ASSUME_NONNULL_END
#import "GRYTaskManager.h"
@implementation GRYTaskModel
@end
@interface GRYTaskManager ()
@property (nonatomic) dispatch_queue_t queue;
@property (nonatomic) dispatch_semaphore_t semaphore;
@property (nonatomic, strong) NSMutableArray<GRYTaskModel *> *tasks;
@property (nonatomic, strong) GRYTaskModel *currentTask;
@property (nonatomic, copy) dispatch_block_t completeBlock;
@end
@implementation GRYTaskManager
- (void)dealloc {
// remove block
if (self.completeBlock) {
dispatch_block_cancel(self.completeBlock);
}
// keep semaphore
for (int i = 0; i < _tasks.count + 1; i++) {
dispatch_semaphore_signal(_semaphore);
}
}
- (instancetype)initWithQueueLabel:(NSString *)label {
self = [super init];
if (self) {
_queue = dispatch_queue_create([label UTF8String], DISPATCH_QUEUE_SERIAL);
_semaphore = dispatch_semaphore_create(1);
_tasks = [[NSMutableArray alloc] init];
}
return self;
}
- (void)enqueueTask:(GRYTaskModel *)task {
if (!task) {
return;
}
// 把任务加入任务队列,并触发执行任务
dispatch_async(dispatch_get_main_queue(), ^{
[self.tasks addObject:task];
[self executeTasks];
});
}
- (void)executeTasks {
// 使用 GCD 加入队列
dispatch_async(self.queue, ^{
// 用信号量来等待 信号量 -1
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
// 等待完成,执行
GRYTaskModel *task = self.tasks.firstObject;
self.currentTask = task;
[self.tasks removeObject:task];
!task.executeBlock ?: task.executeBlock();
// 超时处理
__weak typeof(self) weakSelf = self;
dispatch_block_t block = dispatch_block_create(0, ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (![task.taskId isEqualToString:strongSelf.currentTask.taskId]) return;
[self _taskCompleted];
});
self.completeBlock = block;
if (task.timeOutDuration) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) ((task.timeOutDuration) * NSEC_PER_SEC)), dispatch_get_main_queue(), self.completeBlock);
}
});
});
}
// 外界通知任务结束
- (void)taskCompletedWithTaskId:(NSString *)taskId {
if (![taskId isEqualToString:self.currentTask.taskId]) return;
// 如果还未超时,取消超时
if (self.completeBlock) {
dispatch_block_cancel(self.completeBlock);
}
[self _taskCompleted];
}
- (void)_taskCompleted {
self.currentTask = nil;
dispatch_semaphore_signal(self.semaphore);
}
@end
本文介绍了如何在iOS客户端中实现一个带有超时功能的延时队列,以应对网络堆积等情况。通常使用GCD串行队列结合GCD延迟操作,确保任务在超时时能被正确处理,例如在接口请求和动画播放中。
2510

被折叠的 条评论
为什么被折叠?



