I posted a GCD article about asynchronous processing before, GCD is implemented in the core library libdispatch.
Similarly, the core library Foundation is also a class that realizes asynchronous processing.
There are Operation
class and OperationQueue
class.
The Operation class is an encapsulation of the task to be executed and its information.
** Instances of this Operation class are queued and executed sequentially. At this time, the OperationQueue class plays the role of a queue. ** **
At the time of GCD, the task was written in the closure, When using Operation, define it as a subclass of Operation class.
Representing it as a class is a little troublesome, but it also has advantages. It can provide an easy-to-use interface.
The processing content is described in the main () method.
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
class SomeOperation: Operation {
let value: Int
init(value: Int) {
self.value = value
}
override func main() {
//Stop the thread for 1 second
Thread.sleep(forTimeInterval: 1)
print(value)
}
}
Now that you have defined the task, Then you need to queue this task.
But you have to create a queue before you can queue it.
Create an instance of ** OperationQueue class ** that will be the queue to execute the task.
The OperationQueue class can be instantiated with a no-argument initializer. After instantiation, make various settings via properties.
import Fountation
let queue = OperationQueue()
** · name property ** You can give the queue a name by setting the name property. It is common to use ** reverse DNS format ** for this value.
** · maxConcurrentOperationCount property ** The number of tasks that a particular queue can run at the same time is well determined by system conditions, You can specify that number by using the maxConcurrentOperationCount property.
Obviously, setting a large number does not mean that it will be faster. Please note that it may be rather slow.
** ・ QualityOfService type ** GCD was able to determine execution priority by QoS, This is the OperationQueue class version of it.
The role of a value of type QualityOfService is similar to QoS in GCD.
import Foundation
let queue = OperationQueue()
queue.name = "com.example.my_operation_queue"
queue.maxConcurrentOperationCount = 2
queue.qualityOfService = .userInitiated
Now that we have created cues and tasks You need to add a task to the queue.
To add a task, use the addOperation (_ :)
method of the OperationQueue class.
Pass the value of Operation class as an argument.
Also, the OperationQueue class is a method for passing multiple Operation classes,
addOperations (_: waitUntilFinished :)
is also available.
The type of the first argument is [Operation] type.
In the following sample code Five SampleOperation type values are created and added to the queue.
If true is given to the second argument waitUntilFinished
,
** Block the calling thread until all tasks have been executed. ** **
Since false is given this time, the next process is executed as it is without waiting for the task to finish.
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
class SampleOperation: Operation {
let value: Int
init(value: Int) {
self.value = value
}
override func main() {
Thread.sleep(forTimeInterval: 1)
print("value: \(value)")
}
}
let queue = OperationQueue()
queue.name = "com.example.my_operation_queue"
queue.maxConcurrentOperationCount = 2
queue.qualityOfService = .userInitiated
var operations: [SampleOperation] = []
for i in 0..<5 {
operations.append(SampleOperation(value: i))
}
queue.addOperations(operations, waitUntilFinished: false)
print("A task has been added to the Operation Queue.")
Execution result
A task has been added to the Operation Queue.
value: 0
value: 1
value: 2
value: 3
value: 4
Because it is specified as maxConcurrentOperationCount = 2
Execute two tasks every second.
If you comment out Thread.sleep (forTimeInterval: 1)
The execution result will be as follows.
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
class SampleOperation: Operation {
let value: Int
init(value: Int) {
self.value = value
}
override func main() {
// Thread.sleep(forTimeInterval: 1)
print("value: \(value)")
}
}
let queue = OperationQueue()
queue.name = "com.example.my_operation_queue"
queue.maxConcurrentOperationCount = 2
queue.qualityOfService = .userInitiated
var operations: [SampleOperation] = []
for i in 0..<5 {
operations.append(SampleOperation(value: i))
}
queue.addOperations(operations, waitUntilFinished: false)
print("A task has been added to the Operation Queue.")
Execution result
value: 0
value: 1
A task has been added to the Operation Queue.
value: 2
value: 3
value: 4
The Operation class has a cancel ()
method that has the ability to cancel a task.
The following sample code adds the cancelel () method to the previous code.
You can see that value: 2 does not exist by operations [2] .cancel ()
.
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
class SampleOperation: Operation {
let value: Int
init(value: Int) {
self.value = value
}
override func main() {
print("value: \(value)")
}
}
let queue = OperationQueue()
queue.name = "com.example.my_operation_queue"
queue.maxConcurrentOperationCount = 2
queue.qualityOfService = .userInitiated
var operations: [SampleOperation] = []
for i in 0..<5 {
operations.append(SampleOperation(value: i))
}
queue.addOperations(operations, waitUntilFinished: false)
operations[2].cancel()
Execution result
value: 0
value: 1
value: 3
value: 4
** The Operation class allows you to set dependencies between multiple tasks. ** **
By defining dependencies Only after a task has finished will this task be executed! You can do such processing.
To specify a task that should be executed before that,
Use the addDependency (_ :)
method of the Operation class.
In the argument, enter the task to be executed first.
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
class FirstOperation: Operation {
override func main() {
print("This is FirstOperation")
}
}
class SecendOperation: Operation {
override func main() {
print("This is SecondOperation")
}
}
let queue = OperationQueue()
queue.name = "com.example.my_operation_queue"
queue.maxConcurrentOperationCount = 1
queue.qualityOfService = .userInteractive
var operations: [Operation] = []
operations.append(FirstOperation())
operations.append(SecendOperation())
operations[0].addDependency(operations[1])
queue.addOperations(operations, waitUntilFinished: false)
Execution result
This is SecondOperation
This is FirstOperation
FirstOperation () was supposed to be executed first,
In the part of operations [0] .addDependency (operations [1])
It was executed from SecondOperation () because it defines a dependency.
Operation, OperationQueue class It is an object-oriented abstraction of asynchronous processing.
Besides simple thread switching, Because it has task dependencies and cancel functions, Suitable for ** more complicated processing than GCD. ** **
Also, since the Operation and OperationQueue classes use GCD internally, ** What can be defined in GCD can also be defined in Operation and OperationQueue classes. ** **
GCD can represent tasks in closures, You can use global queues without creating a queue.
Operation and OperationQueue classes need to define tasks and generate queues, You can perform complicated processing accordingly.
** Therefore, simple asynchronous processing is defined in GCD, Let's define cancellation and dependency in Operation and OperationQueue classes! ** **
There is another Thread class for asynchronous processing, ** I will not introduce it because it seems that there is almost no opportunity to use it. ** **
If I had a chance to use it, I would like to share it at that time.
Thank you for watching until the end.
Recommended Posts