Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reset Core Data driven treeController content

I run my program that creates Core Data content that is displayed in a NSOutlineView using NSTreeController. The second time I run my program I want to clean the content of my NSTreeController and I run the method pasted below. The method either hangs for a long time (600 seconds) before it finishes or it crashes. If I have few entities (500-1000) in my NStreeController it takes much less time compared to if I have a lot (200,000) entities to pass this method, if it passes at all. What I need to know is if there is a better way to clear/refresh/reset the content of my NStreeController to clear my NSoutlineView before I re-run my program and fill up the NStreeController again. Specifically, I would like my NSOutlineView to respond quickly to changes to the contents of my NSTreeController, and I need the content of my Core Data driven NSTreeController to be able to be reset.

-(void) cleanSDRDFileObjects
{
    __weak __typeof__(self) weakSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^{     
        [weakSelf.outlineView collapseItem:nil collapseChildren:YES];
        [weakSelf.coreDataController._coreDataHelper.context  performBlockAndWait:^{

            NSEntityDescription *entityDescription = [NSEntityDescription
                                                  entityForName:@"SDRDFileObject"                                        inManagedObjectContext:weakSelf.coreDataController._coreDataHelper.context];

            NSFetchRequest *request = [[NSFetchRequest alloc] init];
            [request setEntity:entityDescription];

            NSArray * result = [weakSelf.coreDataController._coreDataHelper.context  executeFetchRequest:request error:nil];
            for (id fileobject in result){
                [weakSelf.coreDataController._coreDataHelper.context deleteObject:fileobject];
            }
            [weakSelf.coreDataController._coreDataHelper.context processPendingChanges];

            NSLog(@"Finished deleting all objects");
        }];
    });
 }

The managedobjectcontext (context) is run as type NSMainQueueConcurrencyType and the method is run on the main thread. Suggestions for improvements, or useful examples for the combination of reset/refreshing NSOutlineView + Core Data would be greatly appreciated. Thanks. Cheers, Trond

In response to @TomHarringtons question I took a picture of my Time Profiler. I really dont understand why it hangs on this method, however, after commenting this methods out (```processPendingChanges```), it still hangs and takes forever to finish (6 minutes). It seems the process gets stuck on the main thread and cant continue.

enter image description here

When I rerun the application, with processPendingChanges commented out its still hanging.

enter image description here

Update

I believe I solved this but I am slightly uncertain as to why this worked. It seems that my first method went into an indefinite loop that did not release its objects. The following simple solution worked:

     __weak __typeof__(self) weakSelf = self;
     dispatch_sync(dispatch_get_main_queue(), ^{
         [weakSelf.coreDataController._coreDataHelper.context reset];
      }); 

I was certain that to properly empty a managed object context I would have to delete each entity individually. The reset function seems pretty brute force and does it actually clean up memory and make sure everything is okay? If anyone wants to shed some light on this that would be appreciated.

like image 351
Trond Kristiansen Avatar asked Dec 06 '14 01:12

Trond Kristiansen


1 Answers

Looking at this again, you fetched all objects of a type in performBlockAndWait -- this blocks the main thread because you have mainQueueConcurrency and you used the andWait version of performBlock.

You then delete each object one-by-one. These objects are in a tree data structure with a outlineview attached (see the KVO messages in the stack trace). These objects have to-many relationships that need to be maintained by core data, hell, you could even have a cascading delete rule. (see propagateDelete and maintainInverseRelationship in the stack trace) In any event, you start requesting that both the data source and the view start doing a lot of work, on the main thread. You could try using a child MOC with privateQueueConcurrency if you wanted to iterate all objects in the background.

But, like the comments indicated:

NSManagedObjectContext's reset most definitely frees up memory, and it's fine for what you want to do here: blow everything away.

It begs the question why you load the model from the store on disk in the first place, though.

If you want Core Data, but not persistence between the times you run the program, you can initialize the persistentStoreCoordinator with a store of NSInMemoryStoreType rather than pointing it to a file URL.

like image 120
stevesliva Avatar answered Nov 17 '22 17:11

stevesliva