iOS中NSOperationQueue操作优先级调整技术问询
用NSOperationQueue实现优先级调整与错误重试的可行方案
先给你吃颗定心丸:NSOperationQueue完全能实现你要的两个需求,没必要切换到GCD。下面分场景给你具体拆解实现思路:
一、让已加入队列的操作C优先执行
这里要分两种情况处理,核心是利用NSOperation的queuePriority属性:
如果A、B还未开始执行:直接给C设置最高优先级,同时可以调低A、B的优先级(可选,进一步确保C被优先调度):
// Objective-C C.queuePriority = NSOperationQueuePriorityHigh; A.queuePriority = NSOperationQueuePriorityLow; B.queuePriority = NSOperationQueuePriorityLow;// Swift C.queuePriority = .high A.queuePriority = .low B.queuePriority = .lowNSOperationQueue在调度等待队列里的任务时,会优先挑选高优先级的操作执行,这样C会被优先启动。
如果A或B已经在执行中:NSOperationQueue设计上不支持抢占正在执行的任务(避免线程安全问题),此时只能等当前运行的任务完成后,C才会被优先调度。如果必须让C立刻执行,可以这么做:
- 给A、B添加响应取消的逻辑(比如在任务循环里检查
isCancelled状态),然后调用A.cancel()和B.cancel() - 把取消后的A、B重新加入队列并设置低优先级,同时保持C的高优先级,这样C会率先执行
- 给A、B添加响应取消的逻辑(比如在任务循环里检查
二、出错操作重新入队并设置高优先级
这里有两种常用实现方式,推荐用子类化的方式封装逻辑:
方法1:利用completionBlock快速实现
在每个操作初始化时,绑定完成回调,判断任务是否执行失败,失败则创建新的高优先级操作重试:
// Objective-C NSOperation *operation = [[NSOperation alloc] init]; __weak typeof(operation) weakOp = operation; operation.completionBlock = ^{ if (weakOp.isCancelled) return; if (/* 这里写判断操作失败的逻辑,比如自定义error标记 */) { // 注意:已完成的NSOperation不能重新入队,必须创建新实例 NSOperation *retryOp = [[NSOperation alloc] init]; retryOp.queuePriority = NSOperationQueuePriorityHigh; // 拷贝原操作的任务逻辑到retryOp [yourQueue addOperation:retryOp]; } }; [yourQueue addOperation:operation];
// Swift let operation = Operation() weak var weakOp = operation operation.completionBlock = { guard let op = weakOp, !op.isCancelled else { return } if /* 这里写判断操作失败的逻辑 */ { let retryOp = Operation() retryOp.queuePriority = .high // 拷贝原操作的任务逻辑到retryOp yourQueue.addOperation(retryOp) } } yourQueue.addOperation(operation)
方法2:子类化NSOperation(更优雅的方案)
把重试逻辑封装到自定义Operation子类里,代码复用性更强:
// Objective-C @interface RetryableOperation : NSOperation @property (nonatomic, assign) NSInteger retryCount; @property (nonatomic, assign) NSInteger maxRetryCount; // 最大重试次数 @end @implementation RetryableOperation - (void)main { @autoreleasepool { if (self.isCancelled) return; BOOL success = NO; @try { // 执行你的核心任务逻辑 success = [self executeTask]; } @catch (NSException *exception) { success = NO; } if (!success && self.retryCount < self.maxRetryCount) { self.retryCount++; RetryableOperation *retryOp = [[RetryableOperation alloc] init]; retryOp.retryCount = self.retryCount; retryOp.maxRetryCount = self.maxRetryCount; retryOp.queuePriority = NSOperationQueuePriorityHigh; // 加入原队列重试 [self.queue addOperation:retryOp]; } } } - (BOOL)executeTask { // 这里写你的任务执行逻辑,返回是否成功 return YES; } @end
// Swift class RetryableOperation: Operation { var retryCount = 0 let maxRetryCount: Int init(maxRetryCount: Int = 3) { self.maxRetryCount = maxRetryCount super.init() } override func main() { guard !isCancelled else { return } var success = false do { success = try executeTask() } catch { success = false } if !success && retryCount < maxRetryCount { retryCount += 1 let retryOp = RetryableOperation(maxRetryCount: maxRetryCount) retryOp.retryCount = retryCount retryOp.queuePriority = .high self.queue?.addOperation(retryOp) } } func executeTask() throws -> Bool { // 这里写你的任务执行逻辑,返回成功状态或抛出错误 return true } }
几个关键注意点
- NSOperationQueue的优先级是调度优先级,不是抢占式的:如果低优先级操作已经在运行,高优先级操作不会打断它,只能等它完成后再执行。
- 已完成的NSOperation不能重新加入队列,必须创建新的操作实例来重试。
- 如果是还未加入队列的任务,还可以结合
NSOperation的依赖关系(比如让A、B依赖C),但你这里A、B已经入队,这个方案就不适用了。
内容的提问来源于stack exchange,提问作者Naveen Reddy
相关产品推荐
相关产品推荐

