web-dev-qa-db-fra.com

Accès simultanés à 0x1c0a7f0f8, mais la modification nécessite une erreur d’accès exclusif sur Xcode 9 beta 4

mon projet utilise à la fois les codes Objective-C et Swift. Lorsqu'un utilisateur se connecte, il appelle un ensemble d'apis pour ses préférences. J'ai une classe DataCoordinator.Swift qui planifie l'opération de l'API et j'effectue ces appels depuis la classe UserDetailViewController.m pour charger les préférences de l'utilisateur. Cette utilisation fonctionnait bien avant que je migre mon code vers Swift 4 avec Xcode 9 beta 4. Maintenant, lorsque je me connecte, il se bloque en me donnant cette erreur dans ma classe DataCoordinator. Vous trouverez ci-dessous un exemple de mes classes DataCoordinator et 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
}

// C'est comme ça que je l'appelle dans ma méthode viewDontLoad de viewcontrollers

__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

     }

}

Je ne suis pas sûr de savoir comment procéder, j’ai vu un rapport de bogue à l’adresse https://bugs.Swift.org/browse/SR-5119 , mais il semble que ce problème soit corrigé dans Xcode 9 beta 3. Toute aide est apprécié

16
Gamerlegend

Je pense que ce "bogue" est peut-être une "fonctionnalité" de Swift 4, quelque chose qu’ils appellent un "accès exclusif à la mémoire".

Regardez cette vidéo WWDC. Au bout de 50 minutes, l’orateur aux cheveux longs l’explique.

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

Vous pouvez essayer de désactiver le désinfectant pour fil dans vos paramètres de schéma si vous êtes prêt à l'ignorer. Cependant, le débogueur essaie de vous parler d'un problème de threads subtile, c'est donc probablement une meilleure utilisation de votre temps pour essayer de comprendre pourquoi vous avez écrit quelque chose dans votre tableau en même temps. 

19
Mark Bridges

Sous Paramètres de construction de la cible. Sélectionnez No Enforcement pour Exclusive Access to Memory à partir de Swift Compiler - Code Generation

7
geek1706

Seulement dans Swift 4 et en utilisant l'option .initial pour vos paramètres KVO

Si vous vérifiez votre contexte dans la méthode observeValue, créez simplement votre variable de contexte static. Ce article de blog décrit ce bogue en détail.

6
Ralf Hundewadt

Dans mon cas, Swift 4 a découvert une sorte de bogue que je n'aurais pas remarqué avant que je commence à appeler une fonction depuis plus d'un endroit. Ma fonction a été passée à un tableau global inout et elle faisait référence à la fois à ce paramètre et au nom global. Lorsque j'ai changé la fonction pour ne référencer que le paramètre, l'erreur "accès simultané" a disparu. 

2
KenM

Dans Swift 5.0, ce sera le comportement par défaut lors de l'exécution de votre application en mode Release. Avant la version 5.0 (Swift 4.2.1 à partir d’aujourd’hui et des versions inférieures), ce comportement ne s’exécutait qu’en mode Debug.

Votre application peut échouer en mode de publication si vous avez ignoré cette erreur.

Considérons cet exemple:

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

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

Quelle est la valeur de count lorsque la ligne print (count) est imprimée? Eh bien, je ne le sais pas non plus et le compilateur donne des résultats imprévisibles lorsque vous exécutez ce code. Cela n’est pas autorisé dans Swift 4.0 en mode débogage et dans Swift 5.0, il se bloque même au moment de l’exécution.

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

1
J. Doe

Ce que je fais est de changer FetchOperation en class au lieu de struct.

0
Tai Le

Renvoyer zéro dans la fonction numberOfSections override provoquera ce blocage:

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

Solution simple - return 1 dans la fonction ci-dessus, puis return 0 dans la fonction collectionView(_:numberOfItemsInSection:).

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