iOS 延时队列/超时任务队列

本文介绍了如何在iOS客户端中实现一个带有超时功能的延时队列,以应对网络堆积等情况。通常使用GCD串行队列结合GCD延迟操作,确保任务在超时时能被正确处理,例如在接口请求和动画播放中。

延时队列

延时队列在大部分实现中,是一种以时间为权重的有序堆结构实现的队列,在某个时间后执行某些任务。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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值