Simultaneous accesses to 0x1c0a7f0f8, but modification requires exclusive access error on Xcode 9 beta 4

33,200

Solution 1

I think this 'bug' may be a Swift 4 'feature', specifically something they call 'Exclusive access to Memory'.

Check out this WWDC video. Around the 50 minute mark, the long-haired speaker explains it.

https://developer.apple.com/videos/play/wwdc2017/402/?time=233

You could try turning the thread sanitizer off in your scheme settings if you're happy to ignore it. However, the debugger is trying to tell you about a subtle threading issue so it's probably a better use of your time to try to figure out why you've got something writing to your array at the same time it's being read from.

Solution 2

Under the target's Build Settings. Select No Enforcement for Exclusive Access to Memory from Swift Compiler - Code Generation

Solution 3

In Swift 5.0, this will be the default behavior when running your application in Release mode. Before 5.0 (Swift 4.2.1 as of today and lower) this behavior is only running when in Debug mode.

Your application will fail in release mode if you ignored this error.

Consider this example:

func modifyTwice(_ value: inout Int, by modifier: (inout Int) -> ()) {
  modifier(&value)
  modifier(&value)
}

func testCount() {
  var count = 1
  modifyTwice(&count) { $0 += count }
  print(count)
}

What is the value of count, when the print(count) line is printed? Well I don't know either and the compiler gives unpredicatable results when you run this code. This isn't allowed in Swift 4.0 in debug mode and in Swift 5.0 it crashes even in runtime.

Source: https://swift.org/blog/swift-5-exclusivity/

Solution 4

Only in Swift 4 and when using .initial option for your KVO Settings

If you check your context in observeValue method, just make your context variable static. This blog post describes this bug in detail.

Solution 5

Swift 5 here. I was calling a function from a property didSet and testing another property from the same object and I got this error.

I fixed it by calling my function from within another thread:

DispatchQueue.global(qos: .userInitiated).async {
    // do something
}

Basic fix but it works.

Share:
33,200
Francis F
Author by

Francis F

I am an iOS developer with over six years’ experience writing highly readable, clean, maintainable source code. I make it my goal to create software with the user in mind, creating applications with a usable and intuitive user interface experience and I also stand for quality. I am constantly striving to learn new technologies and look to ways to better myself in this rapidly changing industry.

Updated on July 09, 2022

Comments

  • Francis F
    Francis F almost 2 years

    my project uses both Objective-C and Swift code. When a user logs in, it calls a set of apis for user preference, I have a DataCoordinator.swift class which schedules the API operation and I make this calls from UserDetailViewController.m class to load user preferences. This use to work fine before I migrated my code to Swift 4 using Xcode 9 beta 4. Now when I login it crashes by giving me this error in my DataCoordinator class. Below is a sample of my DataCoordinator and Viewcontroller class.

    DataCoordinator.swift
    
    import UIKit
    
    @objcMembers
    
    class DataCoordinator: NSObject {
    
        //MARK:- Private
        fileprivate var user = myDataStore.sharedInstance().user
        fileprivate var preferenceFetchOperations = [FetchOperation]()
    
        fileprivate func scheduleFetchOperation(_ operation:FetchOperation, inFetchOperations operations:inout [FetchOperation]) {
            guard  operations.index(of: operation) == nil else { return }
            operations.append(operation)
        }
    
        fileprivate func completeFetchOperation(_ fetchOperation:FetchOperation, withError error:Error?, andCompletionHandler handler:@escaping FetchCompletionHandler) {
    
            func removeOperation(_ operation:FetchOperation, fromOperations operations:inout [FetchOperation]) {
                if operations.count > 0 {
                    operations.remove(at: operations.index(of: fetchOperation)!)                 
                  handler(error)
                }
            }
    
            if preferenceFetchOperations.contains(fetchOperation) {
                removeOperation(fetchOperation, fromOperations: &preferenceFetchOperations)
            }
    
        }
    
        fileprivate func schedulePreferencesFetchOperation(_ serviceName:String, fetch:@escaping FetchOperationBlock){
            let operation = FetchOperation(name: serviceName, fetch: fetch);
            scheduleFetchOperation(operation, inFetchOperations: &preferenceFetchOperations)
        }
    
    
        fileprivate func runOperationsIn(_ fetchOperations:inout [FetchOperation]) {
            for  var operation in fetchOperations {
                guard operation.isActivated == false else { continue }
                operation.isActivated = true
                operation.execute()
            }
        }
    
    
        //MARK:- Non-Private
        typealias FetchCompletionHandler = (_ error:Error?)->Void
    
        var numberOfPreferencesFetchCalls:Int {
            get { return preferenceFetchOperations.count }
        }
    
    
        // MARK: -
        func fetchPreferences(_ completionHandler:@escaping FetchCompletionHandler) -> Void {
            defer {
                runOperationsIn(&preferenceFetchOperations)
            }
    
            schedulePreferencesFetchOperation("com.fetchPreferences.type1") {[unowned self] (operation:FetchOperation) in
                WebServiceManager.getType1Detail(for: user) {[unowned self] (error) in
                    self.completeFetchOperation(operation,  withError: error, andCompletionHandler: completionHandler)
                }
    
            }
    
            schedulePreferencesFetchOperation("com.fetchPreferences.type2") {[unowned self] (operation:FetchOperation) in
                WebServiceManager.getType2Detail(for: user) {[unowned self] (error) in
                    self.completeFetchOperation(operation,  withError: error, andCompletionHandler: completionHandler)
                }
    
            }
    
            schedulePreferencesFetchOperation("com.fetchPreferences.type3") {[unowned self] (operation:FetchOperation) in
                WebServiceManager.getType3Detail(for: user) {[unowned self] (error) in
                    self.completeFetchOperation(operation,  withError: error, andCompletionHandler: completionHandler)
                }
    
            }
    
            schedulePreferencesFetchOperation("com.fetchPreferences.type4") {[unowned self] (operation:FetchOperation) in
                WebServiceManager.getType4Detail(for: user) {[unowned self] (error) in
                    self.completeFetchOperation(operation,  withError: error, andCompletionHandler: completionHandler)
                }
    
            }
        }
    
    }
    
    
    // MARK:- Fetch Operation Struct
    private typealias FetchOperationBlock = (_ operation:FetchOperation)->Void
    
    private struct FetchOperation:Hashable {
        fileprivate var runToken = 0
        fileprivate let fetchBlock:FetchOperationBlock
    
        let name:String!
        var isActivated:Bool {
            get {
                return runToken == 0 ? false : true
            }
    
            mutating set {
                if runToken == 0 && newValue == true {
                    runToken = 1
                }
            }
        }
    
        fileprivate var hashValue: Int {
            get {
                return name.hashValue
            }
        }
    
        func execute() -> Void {
            fetchBlock(self)
        }
    
        init (name:String, fetch:@escaping FetchOperationBlock) {
            self.name = name
            self.fetchBlock = fetch
        }
    }
    private func ==(lhs: FetchOperation, rhs: FetchOperation) -> Bool {
        return lhs.hashValue == rhs.hashValue
    }
    

    //This is how I call it in my viewcontrollers viewDidLoad method

    __weak UserDetailViewController *weakSelf = self;
    [self.dataCoordinator fetchPreferences:^(NSError * _Nullable error) {
                    if (error == nil) {
                        [weakSelf didFetchPrefrences];
                    }
                    else {
                        // handle error
                    }
                }];
    
    //completion response
    - (void)didFetchPrefrences {
    
        //when api calls complete load data
        if (self.dataCoordinator.numberOfPreferencesFetchCalls == 0) {
    
            //Load details
    
         }
    
    }
    

    I'm not sure how to proceed on this, I saw a bug report at https://bugs.swift.org/browse/SR-5119 but it seems to be fixed in Xcode 9 beta 3. Any help is appreciated

  • CouchDeveloper
    CouchDeveloper about 6 years
    Just be aware, that this does not automatically fix actual data race issues. Thread Sanitizer may however find false positives - which quite often is the case if the mutating functions have their own synchronisation primitive set up.
  • Tsyvarev
    Tsyvarev about 6 years
    @PauloMattos: The phrase "make your context variable static" is definitely an answer. If you find it wrong, you may downvote the post. See that meta post - meta.stackexchange.com/questions/225370/… - for differentiate between NAA(Not-an-answer) and an answer.
  • Paulo Mattos
    Paulo Mattos about 6 years
    @Tsyvarev Yep, short but looks like it really fits our current guidelines. I removed my comment and thanks for the heads up ;)
  • BaseZen
    BaseZen over 5 years
    This has some remarkable subtleties. First of all if you have a didSet observer on a property of a struct, write access is still enforced as active. Second, if this is on a property of a class, write access is not enforced due to some reference semantics that sidesteps the issue. Finally, if the mutation occurs through a protocol type, it depends if the protocol type is class bound or not. When not, it acts with the struct level of restriction even if the runtime type is a reference type after all. I don't expect ppl to follow w/o example code.
  • Robert
    Robert over 5 years
    @BaseZen I found your comment really useful in my case 👍. I have question, do you have any paper which covers this topic which you mentioned: "...it depends if the protocol type is class bound or not. When not, it acts with the struct level of ..."
  • tuttu47
    tuttu47 over 5 years
    Thanks! If your code is crashing observeValue around the context variable, this definitely is the answer!
  • luxo
    luxo over 4 years
    If you are receiving this error and it stems from a func inside a default protocol implementation that modifies another protocol variable, simply make the protocol class bound by having it inherit from AnyObject. This solved it in my specific scenario.
  • LinusGeffarth
    LinusGeffarth over 3 years
    Unfortunately, in Xcode 12, you can only select Compile-time Enforcement Only.
  • Anshuman Singh
    Anshuman Singh over 3 years
    This worked in my case, as I was updating the property via the didSet as well as calling the setNeedsUpdate on the main thread. Thereby updating the property on the global thread worked for me.
  • Edudjr
    Edudjr over 2 years
    That was exactly my case. Just a reminder though: class is deprecated, we should use AnyObject instead
  • Saravanan V
    Saravanan V almost 2 years
    this is the solution that worked for me but i dont understand why, explanation please, here my code: var b = 1 func increment(_ a: inout Int) { a += b print(a) }