2012-09-26 2 views
4

Je viens « résolu » ce qui semble être une impasse ou un problème de synchronisation avec:Utiliser + [sommeil NSThread:] pour résoudre un problème de blocage

[NSThread sleepForTimeInterval:0.1]; 

dans une application qui se fixe MPMediaItem (musique/images) propriété références de la bibliothèque IPOD aux instances d'objets, et ces objets sont stockés en retour via CoreData. Mon intérêt ici est de comprendre exactement ce qui se passe et quelle est la meilleure pratique dans cette situation. Ici va:

La recette de reproduire ce à chaque fois est la suivante:

  1. utilisateur crée un nouveau projet.

    doc = [[UIManagedDocument alloc] initWithFileURL:docURL]; 
    
    if (![[NSFileManager defaultManager] fileExistsAtPath:[docURL path]]) { 
        [doc saveToURL:docURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) { 
        if (success) { 
         completionBlock(doc); 
        } 
        else { 
         DLog(@"Failed document creation: %@", doc.localizedName); 
        } 
    }]; 
    
  2. Plus tard, le managedObjectContext permet d'associer les instances d'objet et hydrate le modèle CoreData

    TheProject *theProject = [TheProject projectWithInfo:theProjectInfo 
               inManagedObjectContext:doc.managedObjectContext]; 
    
  3. L'utilisateur crée plus tard un objet « CustomAction », ajoute un « ChElement » à lui et associés un "MusicElement" avec le ChElement. (Ce sont des pseudonymes pour les objets du modèle CoreData). Le MusicElement est ajouté via la bibliothèque IPOD.

    #define PLAYER [MPMusicPlayerController iPodMusicPlayer] 
    
  4. L'utilisateur enregistre ce projet, puis passe à un projet existant qui a déjà un objet CustomAction créé, avec un ChElement et un MusicElement.

  5. L'utilisateur sélectionne ChElement à partir de tableView et accède à un détail detail. Lorsque vous naviguez loin de la ChElementTVC (une sous-classe d'une classe CoreData TableViewController semblable à celle trouvée dans docs Apple), cela est nécessaire:

    - (void)viewWillDisappear:(BOOL)animated 
    { 
        [super viewWillDisappear:animated]; 
        self.fetchedResultsController.delegate = nil; 
    } 
    
  6. Dans le détail Voir, l'utilisateur change un attribut de l'objet ChElement et enregistre le projet. Le detailView appelle son délégué (ChElementTVC) pour effectuer la sauvegarde. L'enregistrement est sur l'instance UIManagedDocument qui contient NSManagedObject.

    #define SAVEDOC(__DOC__) [ProjectDocumentHelper saveProjectDocument:__DOC__] 
    
    // Delegate 
    
    - (void)chAddElementDetailViewController:(ChDetailViewController *)sender didPressSaveButton:(NSString *)message 
    { 
        SAVEDOC(THE_CURRENT_PROJECT_DOCUMENT); 
    
        [self.navigationController popViewControllerAnimated:YES]; 
    } 
    
    
    // Helper Class 
    
    + (void)saveProjectDocument:(UIManagedDocument *)targetDocument 
    { 
        NSManagedObjectContext *moc = targetDocument.managedObjectContext; 
        [moc performBlockAndWait:^{ 
         DLog(@" Process Pending Changes before saving : %@, Context = %@", targetDocument.description, moc); 
    
         [moc processPendingChanges]; 
         [targetDocument saveToURL:targetDocument.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:NULL]; 
        }]; 
    } 
    
  7. Depuis le délégué (ChElementTVC) est passé du DetailView de la pile de navigation, son viewWillAppear est appelé, et la fetchedResultsController.delegate est rétablie.

    - (void)viewWillAppear:(BOOL)animated 
    { 
        [super viewWillAppear:animated]; 
    
        if (!self.fetchedResultsController.delegate) { 
    
         DLog(@"Sleep Now %@", self); 
    
         //http://mobiledevelopertips.com/core-services/sleep-pause-or-block-a-thread.html 
    
         [NSThread sleepForTimeInterval:0.1]; 
    
         DLog(@"Wake up %@", self); 
    
         [self fetchedResultsControllerWithPredicate:_savedPredicate]; // App Hangs Here ... This is sending messages to CoreData objects. 
    
         [self.tableView reloadData]; 
    } 
    

Sans les [NSThread sleepForTimeInterval:0.1]; les blocages de l'application. Lorsque j'envoie un SIGINT via Xcode, je reçois le débogueur et révèle ce qui suit:

(LLDB) bt

* thread #1: tid = 0x1c03, 0x30e06054 libsystem_kernel.dylib semaphore_wait_trap + 8, stop reason = signal SIGINT 
    frame #0: 0x30e06054 libsystem_kernel.dylib semaphore_wait_trap + 8 
    frame #1: 0x32c614f4 libdispatch.dylib _dispatch_thread_semaphore_wait$VARIANT$mp + 12 
    frame #2: 0x32c5f6a4 libdispatch.dylib _dispatch_barrier_sync_f_slow + 92 
    frame #3: 0x32c5f61e libdispatch.dylib dispatch_barrier_sync_f$VARIANT$mp + 22 
    frame #4: 0x32c5f266 libdispatch.dylib dispatch_sync_f$VARIANT$mp + 18 
    frame #5: 0x35860564 CoreData _perform + 160 

(LLDB) sélectionnez l'image 5

frame #5: 0x35860564 CoreData _perform + 160 
    CoreData _perform + 160: 
    -> 0x35860564: add sp, #12 
     0x35860566: pop {r4, r5, r7, pc} 

    CoreData -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]: 
     0x35860568: push {r4, r5, r6, r7, lr} 
     0x3586056a: add r7, sp, #12 

(LLDB) désassembler - f

CoreData _perform: 
     0x358604c4: push {r4, r5, r7, lr} 

    ... snipped ... 

     0x35860560: blx 0x35938bf4    ; symbol stub for: dispatch_sync_f 

    -> 0x35860564: add sp, #12 
     0x35860566: pop {r4, r5, r7, pc} 

Une autre solution de contournement est possible. Coder le fetchedResultsController.La restauration de délégué dans -[ChElementTVC viewDidAppear:] retarde également efficacement ce paramètre dans la file d'attente principale.

Un travail autour supplémentaire est d'exécuter la pop de navigation dans un bloc d'achèvement après l'enregistrement du projet est terminée:

#define SAVEDOCWITHCOMPLETION(__DOC__,__COMPLETION_BLOCK__)[ProjectDocumentHelper saveProjectDocument:__DOC__ completionHandler:__COMPLETION_BLOCK__] 

    void (^completionBlock)(BOOL) = ^(BOOL success) { 
     [self.navigationController popViewControllerAnimated:YES]; 
    }; 

    SAVEDOCWITHCOMPLETION(THE_CURRENT_PROJECT_DOCUMENT, completionBlock); 

Je pense que l'opération de sauvegarde fonctionne en arrière-plan en même temps que la restauration des délégués sur la principale file d'attente, mais je ne sais pas comment examiner/prouver/réfuter cette théorie.

Ainsi, avec cela, quelqu'un peut-il expliquer ce qui se passe et ce qui est la meilleure pratique dans cette situation? En outre, les références pour l'étude sont appréciées.

Répondre

1

Je fini par mettre en œuvre la troisième méthode, à savoir, l'enregistrement du document avec un bloc d'achèvement de sérialisation des transactions en interaction avec le magasin CoreData.