My app uses a tableView
with a data source that can be updated asynchronously by multiple threads.
When the data source is changed, the tableView
is updated, not reloaded, using
tableView.performBatchUpdates({
tableView.deleteRows(at: deletions, with: .automatic)
tableView.insertRows(at: insertions, with: .automatic)
for (from, to) in movements {
tableView.moveRow(at: from, to: to)
}
}, completion: { (finished) in
if !finished {
self.tableView.reloadData()
} else {
// some cleanup code
}
completion(finished)
}
where completion(finished)
is some completion block.
Typically, the tableView
update takes 0.25 sec. During this time, the data source must not be changed. What is the best method to ensure this?
I can imagine that I acquire an NSCondition
before tableView.performBatchUpdates
, and release it in the completion block, of course also for every other read and write access to the data source, as suggested here. But most posts on SO suggest not to use such low-level sync mechanism, but use GCD instead.
One suggestion is to use a thread-safe SynchronizedArray
using a concurrent queue with sync reads and async barrier writes, which allows concurrent reads. But I don’t know how I could lock out writes during tableView.performBatchUpdates
when I use GCD.
Is there a standard solution to this problem?
EDIT:
I was not happy with the solution I provided below, since it does not allow concurrent reads.
Thus I came up with the following better solution, a
WriteLockableSynchronizedArray
.It is based much closer on Basem Emara's SynchronizedArray (thanks again, Basem), i.e. it does allow concurrent reads, and has the following features:
WriteLockableSynchronizedArray
from anArray
, and a readonly propertyarray
that returns the underlying array.lockArray()
andunlockArray()
that can be called before and after batch tableView operations.Sequence
protocol, so that aWriteLockableSynchronizedArray
can be used e.g. in statements likefor element in writeLockableSynchronizedArray {}
.WriteLockableSynchronizedArray
is write locked, all writes are delayed until it is unlocked again. After unlocking, all delayed writes are executed in order.Here is the new solution (recommended):
Here is my previous solution (no longer recommended):
LockableArray
, a modification of Basem Emara's (thanks!)SynchronizedArray
that uses a recursive lock to synchronize accesses to the array.LockableArray
from anArray
, and a readonly property that returns the underlyingArray
.lockArray()
andunlockArray()
that can be called before and after batch tableView operations.Sequence
protocol, so that aLockableArray
can be used e.g. in statements likefor element in lockablaArray {}
.A disadvantage of this solution is that multiple reads can not be executed concurrently as in Basem Emara's
SynchronizedArray
.Here is the implementation: