NSOperation and NSOperationQueue with maxConcurrentOperationCount = 1
Based on how I read Apple's documentation, the concurrent
property in NSOperation is readonly, and tells us if the operation will run asynchronously or not. If you plan to start operations manually, you need to make the your NSOperation return YES for asynchronous
in order to avoid blocking the thread you are starting your operations from. The concurrent
property is just used to monitor the state of the operation if you are running them manually
If you are adding your NSOperation
to an NSOperationQueue
, the queue will ignore the value of the asynchronous
property, and run operations according to the maxConcurrentOperationCount
.
So, to answer your question: If you run all your operations manually, and set asynchronous
to YES
, the number of operations running in parallel will depend on how big the delay is between each time you call start
on your operations, and how long it will take to finish them. If you add them to a queue, your queue will run the operations one by one, as a serial queue.
Wait for all Operations in queue to finish before performing task
You can use operation dependencies to initiate some operation upon the completion of a series of other operations:
let queue = OperationQueue()
let completionOperation = BlockOperation {
// all done
}
for object in objects {
let operation = ...
completionOperation.addDependency(operation)
queue.addOperation(operation)
}
OperationQueue.main.addOperation(completionOperation) // or, if you don't need it on main queue, just `queue.addOperation(completionOperation)`
Or, in iOS 13 and later, you can use barriers:
let queue = OperationQueue()
for object in objects {
queue.addOperation(...)
}
queue.addBarrierBlock {
DispatchQueue.main.async {
// all done
}
}
NSOperation vs Grand Central Dispatch
GCD
is a low-level C-based API that enables very simple use of a task-based concurrency model. NSOperation
and NSOperationQueue
are Objective-C classes that do a similar thing. NSOperation
was introduced first, but as of 10.5 and iOS 2, NSOperationQueue
and friends are internally implemented using GCD
.
In general, you should use the highest level of abstraction that suits your needs. This means that you should usually use NSOperationQueue
instead of GCD
, unless you need to do something that NSOperationQueue
doesn't support.
Note that NSOperationQueue
isn't a "dumbed-down" version of GCD; in fact, there are many things that you can do very simply with NSOperationQueue
that take a lot of work with pure GCD
. (Examples: bandwidth-constrained queues that only run N operations at a time; establishing dependencies between operations. Both very simple with NSOperation
, very difficult with GCD
.) Apple's done the hard work of leveraging GCD to create a very nice object-friendly API with NSOperation
. Take advantage of their work unless you have a reason not to.
Caveat:
On the other hand, if you really just need to send off a block, and don't need any of the additional functionality that NSOperationQueue
provides, there's nothing wrong with using GCD. Just be sure it's the right tool for the job.
How to reload UITableView when OperationsQueue is done with all operations
There are two possible approaches:
Create a new operation that you want to execute when all the others are done, which I'll call the "completion operation". Then every time you create one of the existing individual operations, add that operation as a dependency to your "completion operation". Then, when you're done adding all of your individual operations (and adding each as a dependency to your "completion operation), then add your completion operation to the main queue. It won't fire until all of the other, individual operations are finished.
For example:
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 4
let completionOperation = BlockOperation {
print("done")
}
for _ in 0 ..< 10 {
let operation = ...
completionOperation.addDependency(operation)
queue.addOperation(operation)
}
OperationQueue.main.addOperation(completionOperation)Note, this assumes that you've created your operations carefully so that the operations don't complete until the task inside the individual operations are done. Notably, if your operation is starting a task that is, itself, an asynchronous task (e.g. a network request), make sure that you define the
Operation
subclass to be an "asynchronous" operation (isAsynchronous
returnstrue
) and make sure that it does theisFinished
/isExecuting
KVO correctly upon completion.The other approach is to use a dispatch group. Every time you create one of your individual operations,
enter
the group (not in the operation itself, but as you create those operations and add them to your operation queue). Then, as the last task in your individual operations,leave
the group. Then, when you're done adding all of your individual operations, you can make a dispatch groupnotify
, which you can schedule on the main queue. Then, when all of the individual operations, your dispatch group notify block will fire.For example:
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 4
let group = DispatchGroup()
for _ in 0 ..< 10 {
group.enter()
let operation = BlockOperation {
...
group.leave()
}
queue.addOperation(operation)
}
group.notify(queue: .main) {
print("done")
}
I'd lean towards option 1 (staying within the operation queue paradigm), but both approaches would work.
Related Topics
Swift Spritekit 3D Touch and Touches Moved
Xctest Unit Test Data Response Not Set in Test After Viewdidload
My Button Is Centered for iPhone 6 and 6 Plus, But Not for iPhone 5
How to Move a Model and Generate Its Collision Shape at the Same Time
Hiding Dividers in Nssplitview
Checkboxes in Uitableview State Persistence
Cicolorcontrols & Uislider W/ Swift 4
Uicollectionviewcell Calling Functions Over and Over
Why Reloaddata Is Showing Error for My Tableview Inside Viewcontroller
Can't Import Packages Using Swift 4 Package Manager
What the Difference of Keys and Values in Dictionary of Swift
How to Retrieve Audio File from Parse Swift
Why Does Classa Adopting Protocolb Not Satisfy the Protocolb Requirement
Can the Byte Order of Double Be Safely Reversed
Is There a Neat Way to Represent a Fraction as an Attributed String
Retrieve an Image from Firebase to an Uiimage Swift5