美文网首页
iOS多线程详解(四)之NSOperation

iOS多线程详解(四)之NSOperation

作者: 鱼嘿蛮仁 | 来源:发表于2025-11-23 08:20 被阅读0次

一、NSOperation 是什么?

NSOperation是基于GCD之上的更高一层封装,提供了更强大的任务管理能力,NSOperation需要配合NSOperationQueue来实现多线程。
1.核心概念

// GCD 的思维:执行一个闭包
DispatchQueue.global().async {
    print("执行任务")
}

// NSOperation 的思维:创建一个任务对象
let operation = BlockOperation {
    print("执行任务")
}
let queue = OperationQueue()
queue.addOperation(operation)

2.NSOperation vs GCD

GCD:
- 轻量级
- 简单快速
- 任务依赖需要手动实现
- 取消任务困难

NSOperation:
- 面向对象
- 功能强大
- 内置任务依赖
- 内置取消机制
- 可以暂停/恢复队列
- 支持 KVO

二、NSOperation 的类型

  1. BlockOperation(最常用)
// 创建 BlockOperation
let operation = BlockOperation {
    print("执行任务")
    sleep(1)
    print("任务完成")
}

// 添加到队列
let queue = OperationQueue()
queue.addOperation(operation)

// 或者直接添加闭包
queue.addOperation {
    print("直接添加任务")
}

  1. 自定义 Operation
class DownloadOperation: Operation {
    let url: URL
    var downloadedData: Data?
    
    init(url: URL) {
        self.url = url
        super.init()
    }
    
    // 重写 main 方法
    override func main() {
        // 检查是否被取消
        if isCancelled {
            return
        }
        
        print("开始下载: \(url)")
        
        // 模拟下载
        sleep(2)
        
        // 再次检查取消状态
        if isCancelled {
            print("下载被取消")
            return
        }
        
        downloadedData = Data()
        print("下载完成")
    }
}

// 使用
let operation = DownloadOperation(url: URL(string: "https://example.com")!)
let queue = OperationQueue()
queue.addOperation(operation)

三、OperationQueue(操作队列)

  1. 创建队列
// 创建队列
let queue = OperationQueue()

// 设置队列名称
queue.name = "com.example.myqueue"

// 设置最大并发数
queue.maxConcurrentOperationCount = 3  // 最多3个任务同时执行

// 串行队列
queue.maxConcurrentOperationCount = 1

// 并发队列(默认)
queue.maxConcurrentOperationCount = OperationQueue.defaultMaxConcurrentOperationCount

// 主队列
let mainQueue = OperationQueue.main

  1. 添加任务
let queue = OperationQueue()

// 方式1:添加 Operation 对象
let operation = BlockOperation {
    print("任务1")
}
queue.addOperation(operation)

// 方式2:直接添加闭包
queue.addOperation {
    print("任务2")
}

// 方式3:批量添加
let op1 = BlockOperation { print("任务3") }
let op2 = BlockOperation { print("任务4") }
let op3 = BlockOperation { print("任务5") }
queue.addOperations([op1, op2, op3], waitUntilFinished: false)

// 方式4:等待完成
queue.addOperations([op1, op2, op3], waitUntilFinished: true)
print("所有任务完成")

四、任务依赖(核心功能)

  1. 基本依赖
let queue = OperationQueue()

// 创建三个任务
let downloadOp = BlockOperation {
    print("1. 下载图片")
    sleep(2)
}

let filterOp = BlockOperation {
    print("2. 应用滤镜")
    sleep(1)
}

let uploadOp = BlockOperation {
    print("3. 上传图片")
    sleep(1)
}

// 设置依赖关系
filterOp.addDependency(downloadOp)  // 滤镜依赖下载
uploadOp.addDependency(filterOp)    // 上传依赖滤镜

// 添加到队列(顺序无关)
queue.addOperations([uploadOp, filterOp, downloadOp], waitUntilFinished: false)

// 输出:
// 1. 下载图片
// 2. 应用滤镜
// 3. 上传图片

  1. 复杂依赖
class DependencyExample {
    let queue = OperationQueue()
    
    func demonstrateComplexDependency() {
        // 任务A:下载图片1
        let downloadA = BlockOperation {
            print("下载图片A")
            sleep(2)
        }
        
        // 任务B:下载图片2
        let downloadB = BlockOperation {
            print("下载图片B")
            sleep(2)
        }
        
        // 任务C:合并图片(依赖A和B)
        let merge = BlockOperation {
            print("合并图片")
            sleep(1)
        }
        merge.addDependency(downloadA)
        merge.addDependency(downloadB)
        
        // 任务D:应用滤镜(依赖合并)
        let filter = BlockOperation {
            print("应用滤镜")
            sleep(1)
        }
        filter.addDependency(merge)
        
        // 任务E:上传(依赖滤镜)
        let upload = BlockOperation {
            print("上传图片")
            sleep(1)
        }
        upload.addDependency(filter)
        
        // 添加所有任务
        queue.addOperations([upload, filter, merge, downloadB, downloadA], waitUntilFinished: false)
        
        // 执行顺序:
        // downloadA 和 downloadB 并发执行
        // → merge
        // → filter
        // → upload
    }
}

五、任务取消

  1. 基本取消
class CancellationExample {
    func demonstrateCancel() {
        let operation = BlockOperation {
            for i in 1...10 {
                // 检查取消状态
                if Thread.current.isCancelled {
                    print("任务被取消")
                    return
                }
                
                print("执行步骤 \(i)")
                sleep(1)
            }
        }
        
        let queue = OperationQueue()
        queue.addOperation(operation)
        
        // 2秒后取消
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            operation.cancel()
            print("已发送取消信号")
        }
    }
}

  1. 正确的取消模式
class DownloadOperation: Operation {
    let url: URL
    var downloadedData: Data?
    
    init(url: URL) {
        self.url = url
    }
    
    override func main() {
        // 检查点1:开始前
        guard !isCancelled else {
            print("任务在开始前被取消")
            return
        }
        
        print("开始下载")
        
        // 模拟下载过程
        for i in 1...10 {
            // 检查点2:循环中
            guard !isCancelled else {
                print("下载在 \(i*10)% 时被取消")
                return
            }
            
            print("下载进度: \(i*10)%")
            sleep(1)
        }
        
        // 检查点3:完成前
        guard !isCancelled else {
            print("任务在完成前被取消")
            return
        }
        
        downloadedData = Data()
        print("下载完成")
    }
}

// 使用
let operation = DownloadOperation(url: URL(string: "https://example.com")!)
let queue = OperationQueue()
queue.addOperation(operation)

// 5秒后取消
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
    operation.cancel()
}

  1. 取消所有任务
let queue = OperationQueue()

// 添加多个任务
for i in 1...10 {
    queue.addOperation {
        print("任务 \(i) 开始")
        sleep(2)
        print("任务 \(i) 完成")
    }
}

// 取消所有任务
queue.cancelAllOperations()
print("已取消所有任务")

六、任务状态

  1. 状态属性
let operation = BlockOperation {
    print("执行任务")
    sleep(2)
}

// 状态检查
print("isReady: \(operation.isReady)")           // 是否准备好
print("isExecuting: \(operation.isExecuting)")   // 是否正在执行
print("isFinished: \(operation.isFinished)")     // 是否已完成
print("isCancelled: \(operation.isCancelled)")   // 是否已取消

let queue = OperationQueue()
queue.addOperation(operation)

// 执行中
sleep(1)
print("isExecuting: \(operation.isExecuting)")   // true

// 完成后
sleep(2)
print("isFinished: \(operation.isFinished)")     // true

  1. 状态转换
Operation 的生命周期:

创建 → 准备 → 执行 → 完成
  ↓      ↓      ↓      ↓
isReady  ✅    ❌    ❌
isExecuting ❌  ✅    ❌
isFinished  ❌  ❌    ✅

取消可以在任何阶段发生:
isCancelled: ❌ → ✅

  1. KVO 监听状态
class OperationObserver: NSObject {
    var operation: Operation?
    
    func observeOperation(_ operation: Operation) {
        self.operation = operation
        
        // 监听状态变化
        operation.addObserver(
            self,
            forKeyPath: "isFinished",
            options: .new,
            context: nil
        )
        
        operation.addObserver(
            self,
            forKeyPath: "isExecuting",
            options: .new,
            context: nil
        )
    }
    
    override func observeValue(
        forKeyPath keyPath: String?,
        of object: Any?,
        change: [NSKeyValueChangeKey : Any]?,
        context: UnsafeMutableRawPointer?
    ) {
        if keyPath == "isFinished" {
            print("任务完成")
        } else if keyPath == "isExecuting" {
            print("任务开始执行")
        }
    }
    
    deinit {
        operation?.removeObserver(self, forKeyPath: "isFinished")
        operation?.removeObserver(self, forKeyPath: "isExecuting")
    }
}

七、完成回调

  1. completionBlock
let operation = BlockOperation {
    print("执行任务")
    sleep(2)
    print("任务完成")
}

// 设置完成回调
operation.completionBlock = {
    print("完成回调执行")
    print("注意:这个回调可能在任何线程执行")
}

let queue = OperationQueue()
queue.addOperation(operation)

// 输出:
// 执行任务
// 任务完成
// 完成回调执行

  1. 链式任务
class ChainedOperations {
    func demonstrateChain() {
        let queue = OperationQueue()
        
        // 任务1
        let op1 = BlockOperation {
            print("任务1执行")
            sleep(1)
        }
        
        // 任务2
        let op2 = BlockOperation {
            print("任务2执行")
            sleep(1)
        }
        
        // 任务3
        let op3 = BlockOperation {
            print("任务3执行")
            sleep(1)
        }
        
        // 设置完成回调
        op1.completionBlock = {
            print("任务1完成")
        }
        
        op2.completionBlock = {
            print("任务2完成")
        }
        
        op3.completionBlock = {
            print("任务3完成")
            print("所有任务完成")
        }
        
        // 设置依赖
        op2.addDependency(op1)
        op3.addDependency(op2)
        
        // 添加到队列
        queue.addOperations([op1, op2, op3], waitUntilFinished: false)
    }
}

八、优先级

  1. 设置优先级
let queue = OperationQueue()

// 低优先级
let lowOp = BlockOperation {
    print("低优先级任务")
}
lowOp.queuePriority = .veryLow

// 普通优先级
let normalOp = BlockOperation {
    print("普通优先级任务")
}
normalOp.queuePriority = .normal

// 高优先级
let highOp = BlockOperation {
    print("高优先级任务")
}
highOp.queuePriority = .veryHigh

// 添加到队列
queue.addOperations([lowOp, normalOp, highOp], waitUntilFinished: false)

// 输出顺序:高 → 普通 → 低

  1. 优先级级别
// 优先级从高到低:
operation.queuePriority = .veryHigh    // 最高
operation.queuePriority = .high        // 高
operation.queuePriority = .normal      // 普通(默认)
operation.queuePriority = .low         // 低
operation.queuePriority = .veryLow     // 最低

  1. 服务质量(QoS)
let operation = BlockOperation {
    print("执行任务")
}

// 设置服务质量
operation.qualityOfService = .userInteractive  // 用户交互
operation.qualityOfService = .userInitiated    // 用户发起
operation.qualityOfService = .utility          // 实用工具
operation.qualityOfService = .background       // 后台

let queue = OperationQueue()
queue.qualityOfService = .userInitiated  // 队列级别的 QoS
queue.addOperation(operation)

九、队列控制

  1. 暂停和恢复
let queue = OperationQueue()

// 添加任务
for i in 1...10 {
    queue.addOperation {
        print("任务 \(i)")
        sleep(1)
    }
}

// 暂停队列
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    queue.isSuspended = true
    print("队列已暂停")
}

// 恢复队列
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
    queue.isSuspended = false
    print("队列已恢复")
}

// 注意:暂停不会停止正在执行的任务

  1. 等待完成
let queue = OperationQueue()

// 添加任务
for i in 1...5 {
    queue.addOperation {
        print("任务 \(i)")
        sleep(1)
    }
}

// 等待所有任务完成(阻塞当前线程)
queue.waitUntilAllOperationsAreFinished()
print("所有任务完成")

  1. 获取队列信息
let queue = OperationQueue()

// 添加任务
for i in 1...10 {
    queue.addOperation {
        sleep(1)
    }
}

// 获取队列信息
print("队列中的任务数: \(queue.operationCount)")
print("所有任务: \(queue.operations)")
print("是否暂停: \(queue.isSuspended)")

十、实战案例

案例1:批量图片下载

class ImageDownloader {
    let downloadQueue = OperationQueue()
    
    init() {
        downloadQueue.maxConcurrentOperationCount = 3
        downloadQueue.name = "ImageDownloadQueue"
    }
    
    func downloadImages(urls: [URL], completion: @escaping ([UIImage]) -> Void) {
        var images: [UIImage] = []
        let lock = NSLock()
        
        // 创建下载任务
        let downloadOperations = urls.map { url -> BlockOperation in
            return BlockOperation {
                if let image = self.downloadImage(from: url) {
                    lock.lock()
                    images.append(image)
                    lock.unlock()
                }
            }
        }
        
        // 创建完成任务
        let completionOperation = BlockOperation {
            DispatchQueue.main.async {
                completion(images)
            }
        }
        
        // 设置依赖
        for op in downloadOperations {
            completionOperation.addDependency(op)
        }
        
        // 添加所有任务
        downloadQueue.addOperations(downloadOperations, waitUntilFinished: false)
        downloadQueue.addOperation(completionOperation)
    }
    
    private func downloadImage(from url: URL) -> UIImage? {
        print("下载: \(url)")
        sleep(1)
        return UIImage()
    }
}

// 使用
let downloader = ImageDownloader()
let urls = (1...10).map { URL(string: "https://example.com/image\($0).jpg")! }

downloader.downloadImages(urls: urls) { images in
    print("下载完成,共 \(images.count) 张图片")
}

案例2:图片处理流水线

class ImagePipeline {
    let queue = OperationQueue()
    
    func processImage(_ image: UIImage, completion: @escaping (UIImage?) -> Void) {
        // 1. 下载操作
        let downloadOp = BlockOperation {
            print("1. 下载图片")
            sleep(1)
        }
        
        // 2. 解码操作
        let decodeOp = BlockOperation {
            print("2. 解码图片")
            sleep(1)
        }
        decodeOp.addDependency(downloadOp)
        
        // 3. 调整大小
        let resizeOp = BlockOperation {
            print("3. 调整大小")
            sleep(1)
        }
        resizeOp.addDependency(decodeOp)
        
        // 4. 应用滤镜
        let filterOp = BlockOperation {
            print("4. 应用滤镜")
            sleep(1)
        }
        filterOp.addDependency(resizeOp)
        
        // 5. 压缩
        let compressOp = BlockOperation {
            print("5. 压缩图片")
            sleep(1)
        }
        compressOp.addDependency(filterOp)
        
        // 6. 完成
        let completionOp = BlockOperation {
            print("6. 处理完成")
            DispatchQueue.main.async {
                completion(image)
            }
        }
        completionOp.addDependency(compressOp)
        
        // 添加所有操作
        queue.addOperations([
            downloadOp,
            decodeOp,
            resizeOp,
            filterOp,
            compressOp,
            completionOp
        ], waitUntilFinished: false)
    }
}

// 使用
let pipeline = ImagePipeline()
pipeline.processImage(UIImage()) { processedImage in
    print("图片处理完成")
}

案例3:可取消的搜索

class SearchManager {
    let searchQueue = OperationQueue()
    var currentSearchOperation: Operation?
    
    init() {
        searchQueue.maxConcurrentOperationCount = 1
    }
    
    func search(keyword: String, completion: @escaping ([String]) -> Void) {
        // 取消之前的搜索
        currentSearchOperation?.cancel()
        
        // 创建新的搜索任务
        let operation = BlockOperation {
            // 检查取消
            guard !Thread.current.isCancelled else {
                print("搜索被取消: \(keyword)")
                return
            }
            
            print("搜索: \(keyword)")
            sleep(2)  // 模拟网络请求
            
            // 再次检查取消
            guard !Thread.current.isCancelled else {
                print("搜索被取消: \(keyword)")
                return
            }
            
            let results = ["结果1", "结果2", "结果3"]
            
            DispatchQueue.main.async {
                completion(results)
            }
        }
        
        currentSearchOperation = operation
        searchQueue.addOperation(operation)
    }
}

// 使用
let searchManager = SearchManager()

// 快速输入
searchManager.search(keyword: "a") { results in
    print("结果: \(results)")
}

searchManager.search(keyword: "ab") { results in
    print("结果: \(results)")
}

searchManager.search(keyword: "abc") { results in
    print("结果: \(results)")  // 只有这个会执行
}

相关文章

网友评论

      本文标题:iOS多线程详解(四)之NSOperation

      本文链接:https://www.haomeiwen.com/subject/hdwtqstx.html