本文共 11338 字,大约阅读时间需要 37 分钟。
推荐阅读
一些概念性的东西可参考iOS Concurrency-Operation
Swift
在Swift中取代NSOperation
,取代NSBlockOperation
,取代NSOperationQueue
Operation
状态的改变的流程如下:
1.isReady
->isExecuting
->isCancelled
->isFinished
isReady
->isExecuting
->isFinished
原版本
简介,参见NSOperation的文档
An operation object is a single-shot object—that is, it executes its task once and cannot be used to execute it again. You typically execute operations by adding them to an operation queue (an instance of the OperationQueue class). An operation queue executes its operations either directly, by running them on secondary threads, or indirectly using the libdispatch library (also known as Grand Central Dispatch). For more information about how queues execute operations, see OperationQueue.
文档建议把operation放入OperationQueue中
If you do not want to use an operation queue, you can execute an operation yourself by calling its start() method directly from your code. Executing operations manually does put more of a burden on your code, because starting an operation that is not in the ready state triggers an exception. The isReady property reports on the operation’s readiness.
如果不想使用operation queue,你可以执行operation ,通过调用start()
方法。要确保operation在ready的state。
Asynchronous Versus Synchronous Operations
If you plan on executing an operation object manually, instead of adding it to a queue, you can design your operation to execute in a synchronous or asynchronous manner. Operation objects are synchronous by default. In a synchronous operation, the operation object does not create a separate thread on which to run its task. When you call the start() method of a synchronous operation directly from your code, the operation executes immediately in the current thread. By the time the start() method of such an object returns control to the caller, the task itself is complete.
如果想手动的执行一个operation对象,而不是把它添加到一个队列中,你可以设计你的operation是以同步或异步方式执行。Operation默认是synchronous,synchronous的operation并不会创建一个单独的thread ,当调用synchronous的operationstart()
方法,operation在当前的thread中执行。当对象的start()
方法返回控制给调用者的时候,任务本身就完成了。
When you call the start() method of an asynchronous operation, that method may return before the corresponding task is completed. An asynchronous operation object is responsible for scheduling its task on a separate thread. The operation could do that by starting a new thread directly, by calling an asynchronous method, or by submitting a block to a dispatch queue for execution. It does not actually matter if the operation is ongoing when control returns to the caller, only that it could be ongoing.
调用asynchronous的operation的start()
方法,在对应的task完成之前,就返回了。异步的operation对象负责在单独的线程上调度其任务。operation可以通过通过调用异步方法或将块提交到调度队列执行,来开启一个新的线程。
If you always plan to use queues to execute your operations, it is simpler to define them as synchronous. If you execute operations manually, though, you might want to define your operation objects as asynchronous. Defining an asynchronous operation requires more work, because you have to monitor the ongoing state of your task and report changes in that state using KVO notifications. But defining asynchronous operations is useful in cases where you want to ensure that a manually executed operation does not block the calling thread.
如果你想使用queue执行operation,把它们定义为synchronous会更简单点。如果想手动的执行operation,你可能会想把operation作为asynchronous。作为asynchronous,需要做更多的操作,原因是你需要监控task的状态,通过KVO来告知状态的改变。但是,如果想要确保手动执行的operation不会阻塞当前调用的线程,则定义异步operation是非常有用的
When you add an operation to an operation queue, the queue ignores the value of the
isAsynchronous
property and always calls thestart()
method from a separate thread. Therefore, if you always run operations by adding them to an operation queue, there is no reason to make them asynchronous.
当把operation添加到operation 队列中,queue会忽略isAsynchronous
属性,并总是在一个单独的线程中调用start()
方法。因此,如果你始终将operation添加到操作队列来运行operation,则没有理由使operation为异步。
Subclassing Notes
对于non-concurrent
的operation
,你只需要重写 Into this method, you place the code needed to perform the given task. Of course, you should also define a custom initialization method to make it easier to create instances of your custom class. You might also want to define getter and setter methods to access the data from the operation. However, if you do define custom getter and setter methods, you must make sure those methods can be called safely from multiple threads.
如果你要创建的是concurrent operation,需要重写如下的方法和属性:
NSOperation concurrent vs non-concurrent
参考
NSOperationQueue always executes operations concurrently, while taking dependencies into account.
A “non-concurrent” operation requires a separate thread in order to execute concurrently. NSOperationQueue is responsible for providing this thread. In other words, a non-concurrent operation depends on NSOperationQueue to make it a concurrent operation.
A “concurrent” operation is concurrent on its own; it doesn’t need NSOperationQueue to create a thread for it. An example would be an operation that uses asynchronous file IO.
If you want two or more operations to execute serially you need to use dependencies.
If you want an operation to block the main thread then don’t use NSOperationQueue; just run each operation one after the other on the main thread.
To manually set maximum of concurrent operations, use method on operationQueue setMaxConcurrentOperationCount:
NSOperation
NSOperation
表示了一个独立的计算单元。NSOperation有不同的状态,isReady → isExecuting → isFinished
和isCancelled
,具体请参考
NSBlockOperation
为NSOperation
的子类
如下创建NSBlockOperation
,在block中对两个数做加法
let summationOperation = BlockOperation { result = 2 + 3 sleep(3)}duration { summationOperation.start()}result
duration
用来计算运行时间,定义如下:
public func duration(_ block: () -> ()) -> TimeInterval { let startTime = Date() block() return Date().timeIntervalSince(startTime)}
如上,输出的当期的线程是<NSThread: 0x7fc610f06570>{number = 1, name = main}
,duration
时间大概是3s
所以上述的方法是在当前的线程中执行,并没有异步的执行
管理一个或多个block的并发执行,只有当所有的block都完成执行,operation才被认为finished
Blocks added to a block operation are dispatched with default priority to an appropriate work queue
添加到block operation的blocks将以默认优先级分派到适当的工作队列上
let multiPrinter = BlockOperation()multiPrinter.addExecutionBlock { print("Hello"); sleep(2) }multiPrinter.addExecutionBlock { print("my"); sleep(2) }multiPrinter.addExecutionBlock { print("name"); sleep(2) }multiPrinter.addExecutionBlock { print("is"); sleep(2) }multiPrinter.addExecutionBlock { print("wz"); sleep(2) }duration { multiPrinter.start()}
观察输出结果,可以发现控制台输出只用了4秒,而且并不能保证输出的顺序,运行在当前的线程。
还可以给BlockOperation
添加一个block完成后的回调:
multiPrinter.completionBlock = { print("Done")}
继承NSOperation
通过继承NSOperation
,可以更精确的控制operation
如下的例子,是给图片添加blur的效果,TiltShiftOperation
继承自Operation
,重写了其main
方法
class TiltShiftOperation: Operation{ var inputImage: UIImage? var outputImage: UIImage? override func main() { //处理图片 outputImage = tiltShift(image: inputImage) }}
使用方式如下:
let tsOp = TiltShiftOperation()tsOp.inputImage = inputImageduration { tsOp.start()}
给图片添加blur效果,大概花费了1.08s
,而且是在当前的线程中执行的。
NSOperationQueue
NSOperationQueue
is responsible for scheduling and running a set of operations, somewhere in the background.
class var main: OperationQueue
获取main队里maxConcurrentOperationCount
属性来控制并发任务的数量,为1的话相当于串行队列cancelAllOperations()
取消operationwaitUntilAllOperationsAreFinished()
会block当前的线程
操作可以当做闭包直接加入到queue中
let printerQueue = OperationQueue()duration { printerQueue.addOperation { sleep(3); print("Hello") } printerQueue.addOperation { sleep(3); print("my") } printerQueue.addOperation { sleep(3); print("name") } printerQueue.addOperation { sleep(3); print("is") } printerQueue.addOperation { sleep(3); print("sam") }}
上述的代码并不能保证输出的顺序,duration大概为0.001s
也可以添加多个Operation
到queue中,已上面的图片处理Operation为例
let filterQueue = OperationQueue()//串行队列let appendQueue = OperationQueue()appendQueue.maxConcurrentOperationCount = 1for image in images { let filterOp = TiltShiftOperation() filterOp.inputImage = image filterOp.completionBlock = { guard let output = filterOp.outputImage else { return } //添加处理完的图片到filteredImages中 appendQueue.addOperation({ filteredImages.append(output) }) } filterQueue.addOperation(filterOp)}
另外参考
暂停和继续queue
如果你想临时暂停Operations的执行,可以使用queue的setSuspended:方法暂停queue。不过暂停一个queue不会导致正在执行的operation在任务中途暂停,只是简单地阻止调度新Operation执行。你可以在响应用户请求时,暂停一个queue来暂停等待中的任务。稍后根据用户的请求,可以再次调用setSuspended:方法继续queue中operation的执行
// 暂停queue [queue setSuspended:YES]; // 继续queue [queue setSuspended:NO];
Asynchronous Operations
在上面的例子中,讲的都是sync
类型的task
,使用当前的线程来执行任务,直至task
完成才return
async
类型的方法却是在task
完成之前,就直接return
了,一个常见的场景是网络请求 1.最开始Operation
的isReady
、isExecuting
、isFinished
都被设置为false
isReady
变为true
3.start()
方法后,isExecuting
变为true
4.异步方法调用结束后,设置isFinished
为true
,isExecuting
设置为false
Operation类依赖KVO来发送state的通知:
var state = State.Ready { willSet { willChangeValueForKey(newValue.keyPath) willChangeValueForKey(state.keyPath) } didSet { didChangeValueForKey(oldValue.keyPath) didChangeValueForKey(state.keyPath) }}
为了简化代码,先创建一个AsyncOperation
基类,AsyncOperation
继承自Operation
,来自动的处理state的改变,如下:
1.operation的state是只读,所以使用自定义的state
2.asynchronous设置为true,告知系统手动管理state 3.重写isReady
、isExecuting
、isFinished
方法 4.重写start()
方法,更新状态 5.重写cancel()
方法 class AsyncOperation: Operation { //State枚举 enum State: String { case Ready, Executing, Finished fileprivate var keyPath: String { return "is" + rawValue } } var state = State.Ready { willSet { willChangeValue(forKey: newValue.keyPath) willChangeValue(forKey: state.keyPath) } didSet { didChangeValue(forKey: oldValue.keyPath) didChangeValue(forKey: state.keyPath) } }}extension AsyncOperation { override var isReady: Bool { return super.isReady && state == .Ready } override var isExecuting: Bool { return state == .Executing } override var isFinished: Bool { return state == .Finished } override var isAsynchronous: Bool { return true } override func start() { if isCancelled { state == .Finished return } main() state == .Executing } override func cancel() { super.cancel() state == .Finished }}
所以,在AsyncOperation
的基础上,包含一个异步函数的步骤如下:
1.继承AsyncOperation
main()
方法,调用异步方法 3.在异步的回调中改变state
状态为.Finished
如,有一个异步的网络下载。如下,模拟一个异步的图片下载:
public func simulateNetworkLoadImage(named: String?) -> UIImage? { sleep(1) guard let named = named else { return .none } return UIImage(named: named)}public func simulateAsyncNetworkLoadImage(named: String?, callback: @escaping (UIImage?) -> ()) { //异步下载图片 OperationQueue().addOperation { let image = simulateNetworkLoadImage(named: named) callback(image) }}
创建一个异步下载的ImageLoadOperation
,继承自AsyncOperation
class ImageLoadOperation: AsyncOperation { //输出图片的名称 var inputName: String? //下载的图片 var outputImage: UIImage? override func main() { simulateAsyncNetworkLoadImage(named: self.inputName) { [unowned self] (image) in self.outputImage = image self.state = .Finished } }}
使用方式如下,创建一个ImageLoadOperation
的实例,把它加入到OperationQueue
中即可:
let queue = OperationQueue()let imageLoad = ImageLoadOperation()imageLoad.inputName = "train_dusk.jpg"queue.addOperation(imageLoad)
运用
参考
转载地址:https://windzen.blog.csdn.net/article/details/52748014 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!