NSOperation和NSOperationQueue学习总结

swift4.0版

1. NSOperation、NSOperationQueue 简介

NSOperation、NSOperationQueue 是苹果提供给我们的一套多线程解决方案。实际上 NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。

GCD vs NSOperation Queue
GCD vs NSOperation Queue.png

我们可以看到,NSOperation Queue 作为高级 API,有很多 GCD 没有的功能,如需要支持:控制并发数、取消、添加依赖关系等需要使用 NSOperation Queue。
另外,由于 block 可复用性没有 NSOperation 好,对于独立性强、可复用性高的任务建议使用 NSOperation 实现。
当然,NSOperation 在使用时需要 sub-classing,工作量较大,对于简单的任务使用 GCD 即可
NSThread就更少用了,对于实时性要求很高的任务则可以使用 NSThread

2. NSOperation、NSOperationQueue 操作和操作队列

既然是基于 GCD 的更高一层的封装。那么,GCD 中的一些概念同样适用于 NSOperation、NSOperationQueue。在 NSOperation、NSOperationQueue 中也有类似的任务(操作)队列(操作队列) 的概念。

  • 操作(Operation)
    在 GCD 中是放在 block 中的。在 NSOperation 中,我们使用 NSOperation 子类 NSInvocationOperation、NSBlockOperation,或者自定义子类来封装操作。
  • 操作队列(Operation Queues)
    1.用来存放操作的队列。不同于 GCD 中的调度队列 FIFO(先进先出)的原则。NSOperationQueue 对于添加到队列中的操作,首先进入准备就绪的状态(就绪状态取决于操作之间的依赖关系),然后进入就绪状态的操作的开始执行顺序(非结束执行顺序)由操作之间相对的优先级决定(优先级是操作对象自身的属性)。
    2.操作队列通过设置 最大并发操作数(maxConcurrentOperationCount) 来控制并发、串行
    3.NSOperationQueue 为我们提供了两种不同类型的队列:主队列和自定义队列。主队列运行在主线程之上,而自定义队列在后台执行

3. NSOperation、NSOperationQueue 使用步骤

NSOperation 需要配合 NSOperationQueue 来实现多线程。因为默认情况下,NSOperation 单独使用时系统同步执行操作,配合 NSOperationQueue 我们能更好的实现异步执行。
NSOperation 实现多线程的使用步骤分为三步:

  • 1.创建操作:先将需要执行的操作封装到一个 NSOperation 对象中。
  • 2.创建队列:创建 NSOperationQueue 对象。
  • 3.将操作加入到队列中:将 NSOperation 对象添加到 NSOperationQueue 对象中。

之后呢,系统就会自动将 NSOperationQueue 中的 NSOperation 取出来,在新线程中执行操作。

4.NSOperation 和 NSOperationQueue 基本使用

4.1 创建操作

NSOperation 是个抽象类,不能用来封装操作。我们只有使用它的子类来封装操作。我们有三种方式来封装操作:

  • 1.使用子类 NSInvocationOperation(swift已废弃)
  • 2.使用子类 NSBlockOperation
  • 3.自定义继承自 NSOperation 的子类,通过实现内部相应的方法来封装操作

在不使用 NSOperationQueue,单独使用 NSOperation 的情况下系统同步执行操作:

4.1.1 使用子类 NSBlockOperation
    /**
    * 使用子类 BlockOperation
    */
    func useBlockOperation(){
        let op = BlockOperation.init {
            for _ in 0...2{
                sleep(2)
                print("1---\(Thread.current)")
            }
        }
        
        op.start()
        print("main---\(Thread.current)")
    }

输出:
1---{number = 1, name = main}
1---{number = 1, name = main}
1---{number = 1, name = main}
main---{number = 1, name = main}

可以看到:在没有使用 NSOperationQueue、在主线程中单独使用 NSBlockOperation 执行一个操作的情况下,操作是在当前线程执行的,并没有开启新线程。

NSBlockOperation 还提供了一个方法addExecutionBlock:,通过addExecutionBlock:就可以为 NSBlockOperation 添加额外的操作。这些操作(包括 init 中的操作)可以在不同的线程中同时(并发)执行。只有当所有相关的操作已经完成执行时,才视为完成。

/**
    * 使用子类 BlockOperation
    * 调用方法 AddExecutionBlock:
    */
    @objc func useBlockOperationAddExecutionBlock(){
        
        //  1.创建 BlockOperation 对象
        let op = BlockOperation.init {
            for _ in 0...2{
                Thread.sleep(forTimeInterval: 2)
                print("1---\(Thread.current)")
            }
        }
        
        //  2.添加额外的操作
        op.addExecutionBlock {
            for _ in 0...2{
                Thread.sleep(forTimeInterval: 2)
                print("2---\(Thread.current)")
            }
        }
        
        op.addExecutionBlock {
            for _ in 0...2{
                Thread.sleep(forTimeInterval: 2)
                print("3---\(Thread.current)")
            }
        }

        op.addExecutionBlock {
            for _ in 0...2{
                Thread.sleep(forTimeInterval: 2)
                print("4---\(Thread.current)")
            }
        }
        
        op.start()
        print("current---\(Thread.current)")
    }

输出:
2---{number = 4, name = (null)}
1---{number = 1, name = main}
1---{number = 1, name = main}
2---{number = 4, name = (null)}
1---{number = 1, name = main}
2---{number = 4, name = (null)}
4---{number = 4, name = (null)}
3---{number = 1, name = main}
4---{number = 4, name = (null)}
3---{number = 1, name = main}
3---{number = 1, name = main}
4---{number = 4, name = (null)}
current---{number = 1, name = main}

如果一个 blockOperation 封装了多个操作,在主线程执行,系统只分配多一个线程与主线程来共同并发,且阻塞当前线程。这里与 objective-c 有差别,objective-c 是添加的操作的个数多,就会自动开启新线程。

如果是在其他线程上执行看下输出:

Thread.detachNewThreadSelector(#selector(useBlockOperationAddExecutionBlock), toTarget: self, with: nil)

输出:
2---{number = 6, name = (null)}
1---{number = 5, name = (null)}
2---{number = 6, name = (null)}
1---{number = 5, name = (null)}
2---{number = 6, name = (null)}
1---{number = 5, name = (null)}
3---{number = 6, name = (null)}
4---{number = 5, name = (null)}
3---{number = 6, name = (null)}
4---{number = 5, name = (null)}
3---{number = 6, name = (null)}
4---{number = 5, name = (null)}
current---{number = 5, name = (null)}

如果一个 blockOperation 封装了多个操作,在其他线程执行,系统只分配两条线程并发。

4.1.2 使用自定义继承自 NSOperation 的子类

如果使用子类 NSInvocationOperation、NSBlockOperation 不能满足日常需求,我们可以使用自定义继承自 NSOperation 的子类。可以通过重写 main 或者 start 方法 来定义自己的 NSOperation 对象。重写main方法比较简单,我们不需要管理操作的状态属性isExecutingisFinished等。当main执行完返回的时候,这个操作就结束了。
先定义一个继承自 NSOperation 的子类,重写main方法。

//
//  CustomOperation.swift
//

import UIKit

class CustomOperation: Operation {
    
    override func main() {
        if self.isCancelled == false{
            for _ in 0...2 {
                Thread.sleep(forTimeInterval: 2)
                print("1---\(Thread.current)")
            }
        }
    }

}

然后使用:

 /**
    * 使用自定义继承自 Operation 的子类
    */
    func useCustomOperation(){
        let op = CustomOperation.init()
        op.start()
    }

输出:
1---{number = 1, name = main}
1---{number = 1, name = main}
1---{number = 1, name = main}

另外一种方式是重写NSOperation的start方法,这种方法就需要你自己去控制NSOperation的完成,取消,执行等。通过官方文档可以知道,实现并行的自定义子类,需要重写以下几种方法或属性:

  • start:把需要执行的任务放在start方法里,并且要检查任务是否已取消,更新任务的状态等。(注意:任何时候都不能调用父类的start方法)
  • asynchronous:表示是否并发执行
  • executing:表示任务是否正在执行,需要手动调用KVO方法来进行通知,方便其他类监听了任务的该属性
  • finished:表示任务是否结束,需要手动调用KVO方法来进行通知,变量也需要监听改属性的值,用于判断任务是否结束
class CustomOperation: Operation {
    
    var _finished:Bool = false
    var _executing:Bool = false
    
    override var isExecuting: Bool{
        set{
            self.willChangeValue(forKey: "isExecuting")
            _executing = newValue
            self.didChangeValue(forKey: "isExecuting")
        }
        get{
            return _executing
        }
    }
    
    override var isFinished: Bool{
        set{
            self.willChangeValue(forKey: "isFinished")
            _finished = newValue
            self.didChangeValue(forKey: "isFinished")
        }
        get{
            return _finished
        }
    }
    
    override var isConcurrent: Bool{
        get{ return true }
    }
    
    override func start() {
        synchronized(self) {//线程安全
            if self.isCancelled{
                self.isFinished = true
                return
            }
            self.isExecuting = true//设置执行状态并调用KVO通知
            Thread.sleep(forTimeInterval: 2)//耗时操作:图片下载等.
            print("耗时操作: --- \(Thread.current)")
            self.isFinished = true//设置完成状态并调用KVO通知
        }
    }
    
     synchronized(_ lock: AnyObject, _ action: () -> Void){
        objc_sync_enter(lock)
        defer { objc_sync_exit(lock) }
        action()
    }
}
    func useCustomOperation(){
        let op1 = CustomOperation.init()
        let op2 = CustomOperation.init()
        
        let queue = OperationQueue.init()
        queue.addOperation(op1)
        queue.addOperation(op2)
    }

输出:
耗时操作: --- {number = 4, name = (null)}
耗时操作: --- {number = 5, name = (null)}

关于自定义NSOperation,可以阅读SDWebImage 的 SDWebImageDownloaderOperation 和 SDWebImageDownloader源码来学习更多知识

4.1 创建队列

NSOperationQueue 一共有两种队列:主队列、自定义队列

  • 主队列
    凡是添加到主队列中的操作,都会放到主线程中执行

      // 主队列获取方法
      let queue = OperationQueue.main
    
  • 自定义队列
    添加到这种队列中的操作,就会自动放到子线程中执行
    同时包含了串行、并发功能

      // 自定义队列创建方法
      let queue = OperationQueue.init()
    
4.3 将操作加入到队列中

NSOperation 配合 NSOperationQueue 来实现多线程
那么我们需要将创建好的操作加入到队列中去。总共有两种方法:
1.open func addOperation(_ op: Operation)需要先创建操作,再将创建好的操作加入到创建好的队列中去

    /**
    * 使用 addOperation: 将操作加入到操作队列中
    */
    func addOperationToQueue(){
        // 自定义队列创建方法
        let queue = OperationQueue.init()
        
        //  1.创建 BlockOperation 对象
        let op = BlockOperation.init {
            for _ in 0...2{
                Thread.sleep(forTimeInterval: 1)
                print("1---\(Thread.current)")
            }
        }
        
        let op1 = BlockOperation.init {
            for _ in 0...2{
                Thread.sleep(forTimeInterval: 1)
                print("1---\(Thread.current)")
            }
        }
        
        let op2 = BlockOperation.init {
            for _ in 0...2{
                Thread.sleep(forTimeInterval: 1)
                print("1---\(Thread.current)")
            }
        }
        
        queue.addOperation(op)
        queue.addOperation(op1)
        queue.addOperation(op2)
        print("current---\(Thread.current)")
    }

输出:
current---{number = 1, name = main}
1---{number = 5, name = (null)}
1---{number = 3, name = (null)}
1---{number = 6, name = (null)}
1---{number = 5, name = (null)}
1---{number = 6, name = (null)}
1---{number = 3, name = (null)}
1---{number = 5, name = (null)}
1---{number = 6, name = (null)}
1---{number = 3, name = (null)}

使用 NSOperation 子类创建操作,并使用 addOperation 将操作加入到操作队列后能够开启新线程,进行并发执行。

2.open func addOperation(_ block: @escaping () -> Void)无需先创建操作,在 block 中添加操作,直接将包含操作的 block 加入到队列中

    /**
    * 使用 addOperationWithBlock: 将操作加入到操作队列中
    */
    func addOperationWithBlockToQueue(){
        // 自定义队列创建方法
        let queue = OperationQueue.init()
        
        queue.addOperation {
            for _ in 0...2{
                Thread.sleep(forTimeInterval: 1)
                print("1---\(Thread.current)")
            }
        }
        
        queue.addOperation {
            for _ in 0...2{
                Thread.sleep(forTimeInterval: 1)
                print("2---\(Thread.current)")
            }
        }
        
        queue.addOperation {
            for _ in 0...2{
                Thread.sleep(forTimeInterval: 1)
                print("3---\(Thread.current)")
            }
        }
        print("current---\(Thread.current)")
    }

输出:
current---{number = 1, name = main}
1---{number = 4, name = (null)}
2---{number = 6, name = (null)}
3---{number = 5, name = (null)}
3---{number = 5, name = (null)}
2---{number = 6, name = (null)}
1---{number = 4, name = (null)}
2---{number = 6, name = (null)}
3---{number = 5, name = (null)}
1---{number = 4, name = (null)}

使用 addOperation 将操作加入到操作队列后能够开启新线程,进行并发执行

5.NSOperationQueue 控制串行执行、并发执行

NSOperationQueue是通过maxConcurrentOperationCount属性来控制串行执行、并发执行的
maxConcurrentOperationCount:叫做最大并发操作数。用来控制一个特定队列中可以有多少个操作同时参与并发执行。

  • maxConcurrentOperationCount 默认情况下为-1,表示不进行限制,可进行并发执行
  • maxConcurrentOperationCount 为1时,队列为串行队列。只能串行执行。
  • maxConcurrentOperationCoun 大于1时,队列为并发队列。操作并发执行,当然这个值不应超过系统限制,即使自己设置一个很大的值,系统也会自动调整为 min{自己设定的值,系统设定的默认最大值}。

注意:这里 maxConcurrentOperationCount 控制的不是并发线程的数量,而是一个队列中同时能并发执行的最大操作数。而且一个操作也并非只能在一个线程中运行。

    /**
    * 设置 MaxConcurrentOperationCount(最大并发操作数)
    */
    func setMaxConcurrentOperationCount(){
        // 自定义队列创建方法
        let queue = OperationQueue.init()
        queue.maxConcurrentOperationCount = 1//串行队列
//        queue.maxConcurrentOperationCount = 2//并发队列
        
        queue.addOperation {
            for _ in 0...2{
                Thread.sleep(forTimeInterval: 1)
                print("1---\(Thread.current)")
            }
        }
        
        queue.addOperation {
            for _ in 0...2{
                Thread.sleep(forTimeInterval: 1)
                print("2---\(Thread.current)")
            }
        }
        
        queue.addOperation {
            for _ in 0...2{
                Thread.sleep(forTimeInterval: 1)
                print("3---\(Thread.current)")
            }
        }
        print("current---\(Thread.current)")
    }

最大并发操作数为1 输出结果:
current---{number = 1, name = main}
1---{number = 4, name = (null)}
1---{number = 4, name = (null)}
1---{number = 4, name = (null)}
2---{number = 5, name = (null)}
2---{number = 5, name = (null)}
2---{number = 5, name = (null)}
3---{number = 5, name = (null)}
3---{number = 5, name = (null)}
3---{number = 5, name = (null)}

最大并发操作数为2 输出结果:
current---{number = 1, name = main}
2---{number = 5, name = (null)}
1---{number = 4, name = (null)}
1---{number = 4, name = (null)}
2---{number = 5, name = (null)}
2---{number = 5, name = (null)}
1---{number = 4, name = (null)}
3---{number = 4, name = (null)}
3---{number = 4, name = (null)}
3---{number = 4, name = (null)}

从输出可以看出,当maxConcurrentOperationCount为1时,操作是按顺序串行执行的,操作执行完一个才执行下一个。当maxConcurrentOperationCount大于1时,操作是并发执行的,同时有两个操作执行。而开启线程的数量是同系统决定的,不需要自己管理

6.NSOperation 操作依赖

NSOperationQueue可以通过addDependency方法来控制操作的执行顺序。关于依赖相关属性及方法有三个:

open func addDependency(_ op: Operation)// 添加依赖,使当前操作依赖于操作 op 的完成

open func removeDependency(_ op: Operation)//移除依赖,取消当前操作对操作 op 的依赖

open var dependencies: [Operation] { get }//在当前操作开始执行之前完成执行的所有操作对象数组
    /**
    * 操作依赖
    * 使用方法:addDependency:
    */
    func addDependency(){
        // 1.创建队列
        let queue = OperationQueue.init()
        
        // 2.创建操作
        let op = BlockOperation.init {
            for _ in 0...2{
                print("0---\(Thread.current)")
            }
        }
        
        let op1 = BlockOperation.init {
            for _ in 0...2{
                Thread.sleep(forTimeInterval: 1)
                print("1---\(Thread.current)")
            }
        }
        
        // 3.添加依赖
        op.addDependency(op1)// 让op 依赖于 op1,则先执行op1,在执行op,右先左后
        
        // 4.添加操作到队列中
        queue.addOperation(op)
        queue.addOperation(op1)
        print("current---\(Thread.current)")
    }

输出:
current---{number = 1, name = main}
1---{number = 5, name = (null)}
1---{number = 5, name = (null)}
1---{number = 5, name = (null)}
0---{number = 6, name = (null)}
0---{number = 6, name = (null)}
0---{number = 6, name = (null)}

7.NSOperation 优先级

NSOperation 提供了queuePriority(优先级)属性

public enum QueuePriority : Int {

    case veryLow

    case low

    case normal

    case high

    case veryHigh
}
    /**
    * 优先级使用
    * 使用属性:queuePriority
    */
    func queuePriority(){
        // 1.创建队列
        let queue = OperationQueue.init()
        
        // 2.创建操作
        let op = BlockOperation.init {
            print("veryLow---\(Thread.current)")
        }
        
        let op1 = BlockOperation.init {
            print("low---\(Thread.current)")
        }
        
        let op2 = BlockOperation.init {
            print("normal---\(Thread.current)")
        }
        
        let op3 = BlockOperation.init {
            print("high---\(Thread.current)")
        }
        
        let op4 = BlockOperation.init {
            print("veryHigh---\(Thread.current)")
        }
        
        let op5 = BlockOperation.init {
            print("normal5---\(Thread.current)")
        }
        
        op.queuePriority = .veryLow
        op1.queuePriority = .low
        op2.queuePriority = .normal
        op5.queuePriority = .normal
        op3.queuePriority = .high
        op4.queuePriority = .veryHigh
        
        queue.addOperation(op)
        queue.addOperation(op1)
        queue.addOperation(op2)
        queue.addOperation(op3)
        queue.addOperation(op4)
        queue.addOperation(op5)
        
    }

输出:
veryLow---{number = 4, name = (null)}
low---{number = 5, name = (null)}
normal---{number = 3, name = (null)}
high---{number = 3, name = (null)}
normal5---{number = 3, name = (null)}
veryHigh---{number = 4, name = (null)}

这里可能会有个误区,认为优先级就可以决定操作的执行顺序。以上输出可以看到并不能,这些优先级都是相对的,并不是说必须按高到低的优先级来执行。这里面并没有一个特别严格顺序。只是在分配资源上有倾向性。如果队列需要有严格的执行顺序,还是要添加依赖关系的

8. NSOperation、NSOperationQueue 线程间的通信

    /**
    * 线程间通信
    */
    func communication(){
        // 1.创建队列
        let queue = OperationQueue.init()
        
        queue.addOperation {
            sleep(1)                                //耗时操作 如:下载文件
            print("1---\(Thread.current)")
            OperationQueue.main.addOperation {
                print("更新操作---\(Thread.current)")
            }
        }
    }

输出:
1---{number = 5, name = (null)}
更新操作---{number = 1, name = main}

9. NSOperation、NSOperationQueue 线程同步和线程安全

  • 线程安全:若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作(更改变量),一般都需要考虑线程同步,否则的话就可能影响线程安全
  • 线程同步:可理解为线程 A 和 线程 B 一块配合,A 执行到一定程度时要依靠线程 B 的某个结果,于是停下来,示意 B 运行;B 依言执行,再将结果给 A;A 再继续操作

下面模拟一下火车票的售卖方式:

    /**
    * 非线程安全
    * 初始化火车票数量、卖票窗口、并开始卖票
    */
    func initTicketStatusNoSave(){
        print("mainThread---\(Thread.current)")
        
        let queue = OperationQueue.init()
        
        queue.addOperation {
            self.saleTickeSafe()//开始卖票
        }
        
        queue.addOperation {
            self.saleTickeSafe()//开始卖票
        }
    }
    /**
    * 售卖火车票(非线程安全)
    */
    func saleTickeNoSafe(){
        while true {
            if self.num > 0{
                self.num = self.num - 1
                print("剩余票数:\(self.num), 窗口:\(Thread.current)")
                Thread.sleep(forTimeInterval: 0.2)
            }else{
                print("所有火车票已卖完")
                break
            }
        }
    }

输出:
mainThread---{number = 1, name = main}
剩余票数:49, 窗口:{number = 3, name = (null)}
剩余票数:48, 窗口:{number = 6, name = (null)}
剩余票数:47, 窗口:{number = 6, name = (null)}
剩余票数:46, 窗口:{number = 3, name = (null)}
剩余票数:44, 窗口:{number = 3, name = (null)}
剩余票数:44, 窗口:{number = 6, name = (null)}
剩余票数:43, 窗口:{number = 3, name = (null)}
剩余票数:43, 窗口:{number = 6, name = (null)}
剩余票数:42, 窗口:{number = 3, name = (null)}
......中间省略一部分
所有火车票已卖完
剩余票数:0, 窗口:{number = 6, name = (null)}
所有火车票已卖完

输出可以看数据是错乱的,这是非线程安全
线程安全解决方案:可以给线程加锁,在一个线程执行该操作的时候,不允许其他线程进行操作。iOS 实现线程加锁有很多种方式。@synchronized、 NSLock、NSRecursiveLock、NSCondition、NSConditionLock、pthread_mutex、dispatch_semaphore、OSSpinLock、atomic(property) set/ge等等各种方式。iOS 线程同步方案学习总结,这里我们使用 NSLock 对象来解决线程同步问题。

    /**
    * 使用 NSLock 保证线程安全
    * 初始化火车票数量、卖票窗口、并开始卖票
    */
    func initTicketStatusSave(){
        print("mainThread---\(Thread.current)")
        
        let queue = OperationQueue.init()
        
        queue.addOperation {
            self.saleTickeSafe()//开始卖票
        }
        
        queue.addOperation {
            self.saleTickeSafe()//开始卖票
        }
    }
    /**
    * 售卖火车票
    */
    func saleTickeSafe(){
        while true {
            lock.lock()
            if self.num > 0{
                self.num = self.num - 1
                print("剩余票数:\(self.num), 窗口:\(Thread.current)")
                Thread.sleep(forTimeInterval: 0.2)
            }else{
                print("所有火车票已卖完")
                break
            }
            lock.unlock()
        }
    }

输出:
mainThread---{number = 1, name = main}
剩余票数:49, 窗口:{number = 4, name = (null)}
剩余票数:48, 窗口:{number = 4, name = (null)}
剩余票数:47, 窗口:{number = 4, name = (null)}
剩余票数:46, 窗口:{number = 4, name = (null)}
剩余票数:45, 窗口:{number = 4, name = (null)}
剩余票数:44, 窗口:{number = 4, name = (null)}
剩余票数:43, 窗口:{number = 4, name = (null)}
......中间省略一部分
剩余票数:1, 窗口:{number = 3, name = (null)}
剩余票数:0, 窗口:{number = 3, name = (null)}
所有火车票已卖完

输出可以看出,在保证线程安全的情况下,数据没有错乱。

10. NSOperation、NSOperationQueue 常用属性和方法归纳

10.1 NSOperation 常用属性和方法
  1. 取消操作方法:
    • (void)cancel; 可取消操作,实质是标记 isCancelled 状态。
  2. 判断操作状态方法:
    • (BOOL)isFinished; 判断操作是否已经结束。
    • (BOOL)isCancelled; 判断操作是否已经标记为取消。
    • (BOOL)isExecuting;判断操作是否正在在运行。
    • (BOOL)isReady;判断操作是否处于准备就绪状态,这个值和操作的依赖关系相关。
  3. 操作同步:
    • (void)waitUntilFinished;阻塞当前线程,直到该操作结束。可用于线程执行顺序的同步。
    • (void)setCompletionBlock:(void (^)(void))block;completionBlock 会在当前操作执行完毕时执行 completionBlock。
    • (void)addDependency:(NSOperation *)op;添加依赖,使当前操作依赖于操作 op 的完成。
    • (void)removeDependency:(NSOperation *)op;移除依赖,取消当前操作对操作 op 的依赖。
    • @property (readonly, copy) NSArray *dependencies;在当前操作开始执行之前完成执行的所有操作对象数组。
10.2 NSOperationQueue 常用属性和方法
  1. 取消/暂停/恢复操作:
    • (void)cancelAllOperations; 可以取消队列的所有操作。
    • (BOOL)isSuspended; 判断队列是否处于暂停状态。 YES 为暂停状态,NO 为恢复状态。
    • (void)setSuspended:(BOOL)b; 可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列。
  2. 操作同步:
    • (void)waitUntilAllOperationsAreFinished; 阻塞当前线程,直到队列中的操作全部执行完毕。
  3. 添加/获取操作:
    • (void)addOperationWithBlock:(void (^)(void))block; 向队列中添加一个 NSBlockOperation 类型操作对象。
    • (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait; 向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操作结束
    • (NSArray *)operations; 当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除)。
    • (NSUInteger)operationCount; 当前队列中的操作数。
  4. 获取队列:
    • (id)currentQueue; 获取当前队列,如果当前线程不是在 NSOperationQueue 上运行则返回 nil。
    • (id)mainQueue; 获取主队列。

注意:
1.这里的暂停和取消(包括操作的取消和队列的取消)并不代表可以将当前的操作立即取消,而是当当前的操作执行完毕之后不再执行新的操作。
2.暂停和取消的区别在于:暂停操作之后还可以恢复操作,继续向下执行;而取消操作之后,所有的操作就清空了,无法再接着执行剩下的操作。

参考资料:
iOS多线程:『NSOperation、NSOperationQueue』详尽总结
细说 NSoperation

相关:
iOS GCD学习总结(一)
iOS GCD学习总结(二)
iOS 线程同步方案学习总结

你可能感兴趣的:(NSOperation和NSOperationQueue学习总结)