Одновременный доступ к 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
В моем случае я изменил высоту таблицы в процессе сборки проекта. В то время мое устройство было подключено к сети. Я удалил полученные данные, и это решило проблему для меня.