swift4.0版
1. NSOperation、NSOperationQueue 简介
NSOperation、NSOperationQueue
是苹果提供给我们的一套多线程解决方案。实际上 NSOperation、NSOperationQueue
是基于 GCD 更高一层的封装,完全面向对象。
GCD vs NSOperation Queue
我们可以看到,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
方法比较简单,我们不需要管理操作的状态属性isExecuting
和 isFinished
等。当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 常用属性和方法
- 取消操作方法:
-
(void)cancel;
可取消操作,实质是标记 isCancelled 状态。
-
- 判断操作状态方法:
-
(BOOL)isFinished;
判断操作是否已经结束。 -
(BOOL)isCancelled;
判断操作是否已经标记为取消。 -
(BOOL)isExecuting;
判断操作是否正在在运行。 -
(BOOL)isReady;
判断操作是否处于准备就绪状态,这个值和操作的依赖关系相关。
-
- 操作同步:
-
(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 常用属性和方法
- 取消/暂停/恢复操作:
-
(void)cancelAllOperations;
可以取消队列的所有操作。 -
(BOOL)isSuspended;
判断队列是否处于暂停状态。 YES 为暂停状态,NO 为恢复状态。 -
(void)setSuspended:(BOOL)b;
可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列。
-
- 操作同步:
-
(void)waitUntilAllOperationsAreFinished;
阻塞当前线程,直到队列中的操作全部执行完毕。
-
- 添加/获取操作:
-
(void)addOperationWithBlock:(void (^)(void))block;
向队列中添加一个 NSBlockOperation 类型操作对象。 -
(void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait;
向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操作结束 -
(NSArray *)operations;
当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除)。 -
(NSUInteger)operationCount;
当前队列中的操作数。
-
- 获取队列:
-
(id)currentQueue;
获取当前队列,如果当前线程不是在 NSOperationQueue 上运行则返回 nil。 -
(id)mainQueue;
获取主队列。
-
注意:
1.这里的暂停和取消(包括操作的取消和队列的取消)并不代表可以将当前的操作立即取消,而是当当前的操作执行完毕之后不再执行新的操作。
2.暂停和取消的区别在于:暂停操作之后还可以恢复操作,继续向下执行;而取消操作之后,所有的操作就清空了,无法再接着执行剩下的操作。
参考资料:
iOS多线程:『NSOperation、NSOperationQueue』详尽总结
细说 NSoperation
相关:
iOS GCD学习总结(一)
iOS GCD学习总结(二)
iOS 线程同步方案学习总结