Одновременный доступ к 0x1c0a7f0f8, но для изменения требуется исключительная ошибка доступа на Xcode 9 beta 4

мой проект использует оба кода Objective-C и Swift. Когда пользователь входит в систему, он вызывает набор apis для пользовательских предпочтений, у меня есть класс DataCoordinator.swift, который планирует работу API, и я вызываю эти вызовы из класса UserDetailViewController.m для загрузки пользовательских настроек. Это использование отлично работает, прежде чем я перенес свой код в Swift 4 с помощью Xcode 9 beta 4. Теперь, когда я вхожу в систему, он сбой, предоставив мне эту ошибку в моем классе DataCoordinator. Ниже приведен образец моего класса DataCoordinator и Viewcontroller.

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
}

//Это то, как я называю это в методе viewcontrollers viewDidLoad

__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

     }

}

Я не уверен, как это сделать, я увидел отчет об ошибке на https://bugs.swift.org/browse/SR-5119, но, похоже, он исправлен в Xcode 9 beta 3. Любая помощь приветствуется

Ответы

Ответ 1

Я думаю, что эта "ошибка" может быть функцией Swift 4 ", в частности, что-то, что они называют" эксклюзивным доступом к памяти ".

Проверьте это видео WWDC. Об этом говорит длинношерстный динамик около 50-минутной отметки.

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

Вы можете попробовать отключить дезинфицирующее средство потока в настройках схемы, если вы с удовольствием проигнорируете его. Тем не менее, отладчик пытается рассказать вам о тонкой проблеме потоковой передачи, поэтому, вероятно, лучше использовать свое время, чтобы попытаться выяснить, почему у вас есть что-то, записывающее в ваш массив, в то же время, когда оно читается.

Ответ 2

Под целевыми настройками сборки. Выберите " No Enforcement для Exclusive Access to Memory от Swift Compiler - Code Generation

Ответ 3

Только в Swift 4 и при использовании опции .initial для ваших настроек .initial

Если вы проверяете свой контекст в методе наблюдаем, просто сделайте переменную контекста статической. Этот блог подробно описывает эту ошибку.

Ответ 4

В Swift 5.0 это будет поведение по умолчанию при запуске приложения в режиме выпуска. До версии 5.0 (Swift 4.2.1 на сегодняшний день и ниже) это поведение работает только в режиме отладки.

Ваше приложение может не работать в режиме выпуска, если вы проигнорировали эту ошибку.

Рассмотрим этот пример:

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

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

Каково значение счетчика при печати строки (счетчика)? Ну, я тоже не знаю, и компилятор выдает непредсказуемые результаты при запуске этого кода. Это не разрешено в Swift 4.0 в режиме отладки, а в Swift 5.0 происходит сбой даже во время выполнения.

Источник: https://swift.org/blog/swift-5-exclustivation/

Ответ 5

В моем случае Swift 4 действительно обнаружил какую-то ошибку, которую я бы не заметил, пока не начал называть функцию из более чем одного места. Моей функции был передан глобальный массив inout, и он ссылался как на этот параметр, так и на глобальное имя. Когда я изменил функцию, чтобы ссылаться только на параметр, ошибка "одновременного доступа" исчезла.

Ответ 6

Ответы @Mark Bridges и @geek1706 - хорошие ответы, но я хотел бы добавить свои 2 цента по этому вопросу и привести общий пример.

Как указано выше, это функция в Swift 4 SE-176.

Разумеется, реализация все же должна позволять обнаруживать одновременные конфликтующие доступы. Некоторые программисты могут захотеть использовать вместо этого механизм принудительного исполнения с поддержкой потоков, по крайней мере, в некоторых конфигурациях сборки.

Исключительный доступ гарантирует, что каждая мутация записи vars должна быть исключительной при доступе к этой переменной. В многопоточной среде несколько потоков, обращающихся к общему var, могут изменить его один или несколько.

Там нет ничего, как хороший пример: Если мы попытаемся изменить общее значение в многопоточной среде с помощью абстракции (мутация происходит по типу протокола) между двумя объектами, и Exclusive Access to Memory будет включен, наше приложение будет аварийно завершено.

protocol Abstraction {
  var sharedProperty: String {get set}
}

class MyClass: Abstraction {
  var sharedProperty: String

  init(sharedProperty: String) {
     self.sharedProperty = sharedProperty
  }

  func myMutatingFunc() {
     // Invoking this method from a background thread
     sharedProperty = "I've been changed"
  }
}


class MainClass {
   let myClass: Abstraction

   init(myClass: Abstraction) {
     self.myClass = myClass
   }

   func foobar() {
      DispatchQueue.global(qos: .background).async {
         self.myClass.myMutatingFunc()
      }
   }
}

let myClass = MyClass(sharedProperty: "Hello")
let mainClass = MainClass(myClass: myClass)
// This will crash
mainClass.foobar()

Поскольку мы не утверждали, что протокол Abstraction связан с class, во время выполнения внутри myMutatingFunc захват self будет обрабатываться как struct, даже если мы ввели фактический class (MyClass).

Экранирующие переменные обычно требуют динамического применения вместо статическое исполнение. Это потому, что Свифт не может рассуждать о том, когда будет вызываться экранирующее закрытие и, следовательно, когда переменная будет доступ.

Решение состоит в том, чтобы привязать протокол Abstraction к class:

protocol Abstraction: class

Ответ 7

Возврат нуля в numberOfSections переопределения numberOfSections вызовет этот сбой:

override func numberOfSections(in collectionView: UICollectionView) -> Int {
    // This causes a crash!    
    return 0
}

Простое решение - return 1 в функции выше и затем return 0 в функции collectionView(_:numberOfItemsInSection:).

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int

Ответ 8

Мой animator падал с тем же описанием при использовании с KVO:

private var animator: UIViewPropertyAnimator!

token = animator.observe(\.state) { [unowned self] animator, _ in
    if animator.state == .inactive { self.makeAnimator() }
}

Исправление было очень простым:

deinit {
    animator.stopAnimation(true)
}

Ответ 9

Что бы я сделал, это изменил FetchOperation на class вместо struct.

Ответ 10

В моем случае я изменил высоту таблицы в процессе сборки проекта. В то время мое устройство было подключено к сети. Я удалил полученные данные, и это решило проблему для меня.