Created
June 22, 2020 16:29
-
-
Save ahmedk92/5fab0e77e322b8e11d121d433828a833 to your computer and use it in GitHub Desktop.
Simultaneous accesses to X, but modification requires exclusive access.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class ProgressManager { | |
| init(progressStore: ProgressStoring) { | |
| self.progressStore = progressStore | |
| self.progressStore.didReceiveNewProgress = { [weak self] progress in | |
| guard let self = self else { return } | |
| self.progress = progress | |
| } | |
| } | |
| private var progress: Float = 0 { | |
| didSet { | |
| guard progress != oldValue else { return } | |
| progressStore.store(progress: progress) | |
| } | |
| } | |
| private var progressStore: ProgressStoring | |
| } | |
| // Making this protocol conform to AnyObject solves the problem. | |
| protocol ProgressStoring/*: AnyObject*/ { | |
| func store(progress: Float) | |
| var didReceiveNewProgress: ((Float) -> Void)? { get set } | |
| } | |
| class ProgressStore: ProgressStoring { | |
| func store(progress: Float) { | |
| guard progress != self.progress else { return } | |
| self.progress = progress | |
| } | |
| var didReceiveNewProgress: ((Float) -> Void)? { | |
| didSet { | |
| didReceiveNewProgress?(progress) | |
| } | |
| } | |
| private var progress: Float = 1 | |
| } | |
| let progressStore = ProgressStore() | |
| let progressManager = ProgressManager(progressStore: progressStore) |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In a nutshell: without explicitly stating
ProgressStoringis a reference type, Swift will just assume theprogressStoreproperty inProgressManagerto be a value type, even if the implementer is a class. So, it will enforce exclusive access protection for it. The protection if cannot be done in compile-time, Swift guarantees it will be done in run-time, which happens in our case. Exclusive access violation happens when a write access is granted (i.e. mutation begins), and in meanwhile a read access is requested. In detail:The write access is granted as just when we assign
didReceiveNewProgress:The conflicting read access is requested deep a little in the call stack when we assign the received progress value
self.progress = progress. This triggers thedidSetof theprogressproperty. If we inspect thedidSetimplementation:Here we call
progressStore.store(progress: progress)which is a read access toprogressStore. But we're already in the call stack of the closure passed to the mutating assignment ofdidReceiveNewProgressonprogressStorewhich means we're already granted a write access and the writing is not finished. This means a violation = crash.