objective c - not - Acceso simultáneo a 0x1c0a7f0f8, pero la modificación requiere un error de acceso exclusivo en Xcode 9 beta 4
apple developer (3)
mi proyecto usa código Objective-C y Swift. Cuando un usuario inicia sesión, llama a un conjunto de apis para la preferencia del usuario, tengo una clase DataCoordinator.swift que programa la operación de la API y realizo estas llamadas desde la clase UserDetailViewController.m para cargar las preferencias del usuario. Este uso funciona bien antes de migrar mi código a Swift 4 usando Xcode 9 beta 4. Ahora, cuando inicio sesión, falla colocándome este error en mi clase DataCoordinator. A continuación hay una muestra de mi clase DataCoordinator y 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
}
// Así es como lo llamo en mi viewcontrollers método 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
}
}
No estoy seguro de cómo proceder con esto, vi un informe de error en https://bugs.swift.org/browse/SR-5119 pero parece estar corregido en Xcode 9 beta 3. Cualquier ayuda es apreciada
Creo que este ''error'' puede ser una ''característica'' de Swift 4, específicamente algo que ellos llaman ''Acceso exclusivo a la memoria''.
Mira este video de WWDC. Alrededor de los 50 minutos, el hablante de pelo largo lo explica.
https://developer.apple.com/videos/play/wwdc2017/402/?time=233
Podría intentar desactivar el desinfectante de hilos en la configuración de su esquema si está feliz de ignorarlo. Sin embargo, el depurador está tratando de informarle acerca de una emisión sutil de subprocesos, por lo que es probablemente un mejor uso de su tiempo para tratar de averiguar por qué tiene algo escrito en su matriz al mismo tiempo que se lee.
En mi caso, Swift 4 realmente descubrió un tipo de error que no habría notado hasta que comencé a llamar a una función desde más de un lugar. Mi función pasó una matriz global inout y hacía referencia tanto a ese parámetro como a su nombre global. Cuando cambié la función para hacer referencia solo al parámetro, el error de "acceso simultáneo" desapareció.
Swift 4: si comprueba su contexto en el método observeValue, simplemente establezca su variable de contexto. Esta publicación de blog describe este problema en detalle.