Main Thread & Collectionview.reloadData()

1.3k views Asked by At

I have a situation where the code change its collectionView data source then call reloadData() several times and i think this cause my app to crash due to the race condition as the data source change very fast, so I wrapped the snippet of code that (change data source then reload collection view) with DispatchQueue.main.async and the crash gone. So the question Does this make sense and solved the race condition, what i understand is wrapping this snippet of code with DispatchQueue.main.async will not execute this snippet again until the reloadData() function finishes, Is that right? if you have any other thoughts about how to solve this issue it will be great

Update:

For example if we have this code on main thread

self.collectionView.dataSource = self.dataSource
self.collectionView.reloadData()

and this snippet of code called twice one after another fast so the data source changed while the first reload in progress, as a solution i wrapped this snippet of code with async block to make sure the data source will not change until the first reload finish. like the below:

DispatchQueue.main.async {
       self.collectionView.dataSource = self.dataSource
       self.collectionView.reloadData()
}

Is this will guarantee that the data source will not change until the reloadData() finish ?

1

There are 1 answers

0
matt On

reloadData should always be called on the main thread. If you were not doing that, that was the crash.

Okay, but now let's talk about how the changing of the data and the call to reloadData are coupled. Perhaps you have a situation like this:

background queue:
=================
    <networking>
    update the data
    main queue:
    ===========
        reloadData

You can still be in trouble, because although no two calls to the main queue can overlap, two calls to a background queue can overlap, because it might be a concurrent queue. So there's your race condition; the updating of the data and the reloading of the data are decoupled. Remember, there are multiple data source methods called during the reloading of the data, and so they all need to be looking at the same data, or you will indeed crash with some sort of inconsistency problem if the data changes between calls. And with that architecture, that's a very real possibility.

But if the background queue is a serial queue, and if the step to the main queue is from that serial queue, then that kind of overlap, at least is impossible. So this would be a safer solution:

background queue:
=================
    <networking>
    single background *serial* queue:
    =================================
        update the data
        main queue:
        ===========
            reloadData

Nevertheless, we are still in a "shared data" situation. The danger remains. There is nothing to prevent the data from being touched on some other queue simultaneously with all this. To prevent that, you would need to write code around every access to the data, ensuring that access happens on just one queue. After all, your data structure might not, itself, be thread-safe! But we already know that the data source methods are going to be seeing the data only on the main thread, because that's how reloadData works. So the simplest approach is just to make a rule that every access to the data happens on the main queue:

background queue:
=================
    <networking>
    main queue:
    ===========
        update the data
        reloadData

So, to conclude, if that's what you are now doing, it's the right thing to do.