6
votes

UimanagedDocument Code Singleton OpenWithCompletionHandler appelé deux fois et se bloque

J'utilise la mise en place de Justin Driscoll sur Données de base avec un seul UIManageDDocument partagé . Tout allait bien dans mon application iPhone jusqu'à ce que je l'ai déplacé dans un storyboard iPad et un contrôleur SplitView pour l'application iPad. Le problème est OpenWithComplettionHandler est appelé deux fois, une fois de ma vue maîtresse dans ViewDidLoad et à nouveau dans ma vue détaillée Viewwillload. Les appels sont en succession rapide et car le document est toujours en uidocument constitué lorsque le deuxième appel est apporté à ma méthode d'exécution (ci-dessous) du singleton, l'application se bloque. J'ai regardé la réponse e_x_p pour post iOS5.1: Tâches de synchronisation ( Attendez une achèvement) mais @sychronized ne fonctionnera pas dans ce cas depuis que les performances ci-dessous sont appelées sur le même thread. Comment protégera-je contre plusieurs appels à OpenWithComplettionHandler? La seule façon de penser à protéger contre cela est de mettre en pause l'exécution de l'un des appels ci-dessus jusqu'à ce que je suis sûr que UidocumentStatenormal est vrai, puis relâchez. Que cependant gelerait le fil principal de l'UI qui n'est pas bon. Qu'est-ce que ce serait la meilleure façon d'envisager cela sans geler l'interface utilisateur?

du code UIMANageDDocumentsIngleton: xxx


0 commentaires

3 Réponses :


0
votes

Le bloc de code partagé entre NumberOfrowSection: code> et CellfrowroDIndexPath: code> doit être appelé qu'une seule fois. NumberofrowSectionSection code> sera toujours appelé avant que le TableView code> essaie de rendre les cellules. Vous devez donc créer un objet nsarray code> que vous pouvez stocker les résultats de la Approvisionnement de la requête dans, puis utilisez ce tableau lorsque vous rendant vos cellules:

@implementation FooTableViewController {
    NSArray *_privateArray;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    [[UIManagedDocumentSingletonHandler sharedDocumentHandler] performWithDocument:^(FCUIManagedDocumentObject *document) {
        NSManagedObjectContext * context =  document.managedObjectContext;

        NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"FCObject"];
        NSPredicate * searchStringPredicate = nil;
        if (searchFilterString)
        {
            searchStringPredicate = [NSPredicate predicateWithFormat:@"word BEGINSWITH[c] %@",searchFilterString];
        }
        request.predicate = searchStringPredicate;
        request.shouldRefreshRefetchedObjects = YES;
        NSError * error;
        _privateArray = [context executeFetchRequest:request error:&error];
        }];
    return _privateArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"FCCell";
    FCardCell *cell = (FCCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    // Configure the cell...
    FCManagedObject * fcc = [_privateArray objectAtIndex:indexPath.row];
    cell.isWordVisible.on = fcc.isUsed;
    cell.fWord.text = fcc.word;
    return cell;
}


2 commentaires

J'ai consolidé la demande de récupération à un seul appel initial que les cellulesFrowrodIndexPath et le numérosofrowsection utilisent désormais ne disposent donc plus le problème, mais le problème existe toujours. J'ai mis à jour le problème pour le simplifier.


Pourriez-vous @synchronized (DocumTObject) pour synchroniser sur l'objet lui-même? Sauf qu'est-ce que vous pouvez déléguer une autorité de maîtrise / détail afin que seul l'un d'entre eux doit apporter l'appel (ou si elles ont une ordonnance d'exécution garantie, vous pouvez transmettre un message entre eux (via @protocol susceptible) d'exécuter le second lorsque le premier est effectué.



1
votes

C'est intéressant et définitivement une faille dans mon code (désolé!). Ma première pensée consisterait à ajouter une file d'attente série en tant que propriété de votre classe de gestionnaire de documents et à effectuer le chèque sur cela.

dispatch_async(self.queue, ^{
    if (![[NSFileManager defaultManager] fileExistsAtPath... // and so on
});


0 commentaires

2
votes

Je l'ai fait comme Justin suggéré ci-dessus Strike> ci-dessous. Fonctionne bien dans l'une de mes applications pendant deux ans avec des utilisateurs ~ 20k. XXX PRE>

SWIFT STRAND> (pas beaucoup testé) P>

typealias OnDocumentReady = (UIManagedDocument) ->()

class SharedManagedDocument {

private let document: UIManagedDocument
private var preparingDocument: Bool

static let sharedDocument = SharedManagedDocument()

init() {
    let fileManager = NSFileManager.defaultManager()
    let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
    let documentsDirectory: NSURL = urls.first as! NSURL
    let databaseURL = documentsDirectory.URLByAppendingPathComponent(".database")
    document = UIManagedDocument(fileURL: databaseURL)
    let options = [NSMigratePersistentStoresAutomaticallyOption : true, NSInferMappingModelAutomaticallyOption : true]
    document.persistentStoreOptions = options
    preparingDocument = false
}

func performWithDocument(onDocumentReady: OnDocumentReady) {

    let onDocumentDidLoad:(Bool) ->() = {
        success in
        onDocumentReady(self.document)
        self.preparingDocument = false
    }
    if !preparingDocument {
        preparingDocument = true
        if !NSFileManager.defaultManager().fileExistsAtPath(document.fileURL.path!) {
            println("Saving document for first time")
            document.saveToURL(document.fileURL, forSaveOperation: .ForCreating, completionHandler: onDocumentDidLoad)
        } else if document.documentState == .Closed {
            println("Document closed, opening...")
            document.openWithCompletionHandler(onDocumentDidLoad)
        } else if document.documentState == .Normal {
            println("Opening document...")
            onDocumentDidLoad(true)
        } else if document.documentState == .SavingError {
            println("Document saving error")
        } else if document.documentState == .EditingDisabled {
            println("Document editing disabled")
        }
    } else {
        // wait until document is ready (opened or created by some other call)
        println("Delaying...")
        delay(0.5, closure: {
            self.performWithDocument(onDocumentReady)
        })
    }
}

private func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}
}


8 commentaires

"//" verrouillage ", donc personne d'autre ne pénètre ici" - c'est une très grosse erreur


@Adnako Si vous parlez de linguistique, je suis d'accord "LOCK" n'est pas un bon mot ici aussi longtemps que c'est juste un drapeau. Mais si vous pensez que cette approche a des côtés faibles, veuillez expliquer, je suis vraiment intéressé!


Nope, je parle de multithreading. Ce code devrait fonctionner en file d'attente unique uniquement. J'utilise ce modèle aussi et j'essaie maintenant de résoudre le problème avec appel [Self.Document OpenWithComplettionHandler: OndocumentDiDload]; à deux reprises.


@Adnako C'est un singleton, mis en œuvre avec Dispatch_Oronce de GCD (décrit dans l'objectif efficace-C 2.0, par exemple). Nous sommes donc garantis par iOS (vous pouvez discuter, mais ce n'est pas le problème ici) ici cette instance est une seule et unique avec le code DISPATCH_ONCE. Ce qui nous a laissé avec une seule et unique variable.


Tu ne comprends pas. Dispatch_Onece ne fonctionne que sur la création statique d'ivar. Tout ce qui souffre de problèmes de multithreading. Je vais vous envoyer un exemple simple plus tard soir.


Je ne peux pas vraiment faire un exemple qui se bloque toujours. Exécutez ce projet plusieurs fois, il devrait se bloquer après peu de démarrages. Github.com/pomozoff/coredataExample


Merci d'avoir pris du temps, mais j'ai ce code en production pendant une demi-année et des milliers de sessions par jour et il n'a jamais été écrasé. Ce bool est pour la création de documents / ouverture de sorte que quiconque s'y rend en premier, "verrouille" la porte et fait le travail.


Ce code doit fonctionner au besoin