I just “solved” what seems like a deadlock or synchronization issue:
[NSThread sleepForTimeInterval:0.1]
in an application that attaches MPMediaItem property links (music / images) from the IPOD library to object instances, and these objects are stored back through CoreData. My interest here is to understand exactly what is happening and what is the best practice in this situation. Here:
The recipe for repeating this conversation is as follows:
The user creates a new project.
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); } }];
ManagedObjectContext is later used to bind object instances and hydrate the CoreData model p>
TheProject *theProject = [TheProject projectWithInfo:theProjectInfo inManagedObjectContext:doc.managedObjectContext];
The user then creates a "CustomAction" object, adds a "ChElement" to it, and associates the "MusicElement" with the ChElement. (These are aliases for CoreData model objects). MusicElement is added through the IPOD library.
#define PLAYER [MPMusicPlayerController iPodMusicPlayer]
The user saves this project, then switches to an existing project that already has one CustomAction created using ChElement and MusicElement.
The user selects a ChElement from the tableView and moves on to the detailView. When moving from ChElementTVC (a subclass of the CoreData TableViewController class, similar to that found in Apple docs), this is necessary:
- (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; self.fetchedResultsController.delegate = nil; }
In the detail view, the user changes the attribute of the ChElement object and saves the project. The detailView function calls its delegate (ChElementTVC) to save. A persistence is an instance of a UIManagedDocument that contains an NSManagedObject.
#define SAVEDOC(__DOC__) [ProjectDocumentHelper saveProjectDocument:__DOC__]
Since the delegate (ChElementTVC) pulled the detailView from the navigation stack, its viewWillAppear is called and the fetchedResultsController.delegate file is restored.
- (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]; }
Without [NSThread sleepForTimeInterval:0.1]; application freezes. When I send SIGINT via Xcode, I get a debugger and detect the following:
(lldb) bt
* thread
(lldb) frame selection 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) parse -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}
Another job is possible. The recovery encoding fetchedResultsController.delegate to -[ChElementTVC viewDidAppear:] also effectively delays this setting in the main queue.
Additional work is to navigate in the completion block after the project is saved:
#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);
I think that the save operation is performed in the background at the same time as the delegate is restored to the main queue, but I do not know how to verify / prove / refute this theory.
So, with this, can someone explain what is happening and what is the best practice in this situation? Research links are also welcome.