I receive data items from my API, on a specific serial queue, that I use to calculate the view models to display in a collection view. The simplified code is:
var viewModels: [ViewModel] = [] // read and written on main queue
var _viewModels: [ViewModel] = [] // written in background, read in main queue
func didReceive(items: [Item]) {
// called on the same background serial queue
_viewModels = items.map { ViewModel($0) }
DispatchQueue.main.async {
self.viewModels = self._viewModels
self.updateCollectionView {
// UI update has finished
}
}
}
didReceive
can be called at any time.
_viewModels
is written always on the same background queue. After that I switch to main queue to cache the computed view models and use it in data source for displaying.
Can self.viewModels = self._viewModels
lead to crashes? Do I have to use some kind of locking mechanism? If yes, how does it work when main thread is involved?
CodePudding user response:
Crashes not necessarily, but this can lead to inconsistency. As _viewModels
is declared outside the scope of didReceive
, it is a shared resource that can be modified each time you enter didReceive
. After dispatching this to the main thread, some other thread can enter in didReceive
and change your value. So when you assign it to self.viewModels
, you would get the "second" value.
I believe that declaring _viewModels
inside didReceive
would fix your issue, as you would have a reference for each run of didReceive
:
var viewModels: [ViewModel] = [] // read and written on main queue
func didReceive(items: [Item]) {
// called on the same background serial queue
let _viewModels = items.map { ViewModel($0) }
DispatchQueue.main.async {
self.viewModels = _viewModels
self.updateCollectionView {
// UI update has finished
}
}
}
If you are not allowed to do this, try taking a look at Actors: https://www.avanderlee.com/swift/actors/