Hi everyone
I am working with GCD, an NSOutlineView and coredata this winterbreak. I am using GCD to load data from disk to store it into a coredata model. Since this takes a long time, I am using GCD. The problem though are the NSAssertion messages from NSOutlineView.
*** Assertion failure in -[MyOutlineView _expandItemEntry:expandChildren:startLevel:], /SourceCache/AppKit/AppKit-1038.35/TableView.subproj/NSOutlineView.m:969
(null) should not be expanded already!
The cause, I think, is either:
NSOutlineView once in a while updates its view while GCD is still loading data.
Or:
My GCD enabled code is modifying the datasource outside of the main thread.
Is there any way to keep the GCD code and have it add data to coredata without NSOutlineView complaining/crashing? The NSOutlineview is already disabled to prevent the user from changing the selection or the data while adding data.
This code represents the basic idea I want implement.
I appreciate all your input and suggestions.
Edit:
If the data is added to coredata or the datasource in general on the main queue, then everything works fine (so far at least).
Inspired by this link
I am working with GCD, an NSOutlineView and coredata this winterbreak. I am using GCD to load data from disk to store it into a coredata model. Since this takes a long time, I am using GCD. The problem though are the NSAssertion messages from NSOutlineView.
*** Assertion failure in -[MyOutlineView _expandItemEntry:expandChildren:startLevel:], /SourceCache/AppKit/AppKit-1038.35/TableView.subproj/NSOutlineView.m:969
(null) should not be expanded already!
The cause, I think, is either:
NSOutlineView once in a while updates its view while GCD is still loading data.
Or:
My GCD enabled code is modifying the datasource outside of the main thread.
Is there any way to keep the GCD code and have it add data to coredata without NSOutlineView complaining/crashing? The NSOutlineview is already disabled to prevent the user from changing the selection or the data while adding data.
This code represents the basic idea I want implement.
I appreciate all your input and suggestions.
Code:
#import "DataTree.h" //subclass of NSTreeController
#import "DataAdding.h" //class that does all the heavy lifting, does not interact directly with coredata
@implementation DataTree
-(IBAction) test:(id)sender
{
[self addManagedObject:@"MyTreeNode"];
DataAdding *da = [[DataAdding alloc] init];
//coredata is accessed only via blocks such as these
void (^processingBlock) (NSURL *, id, NSError *) = ^(NSURL *url, id importedData, NSError *err)
{
//data has been obtained, now add it to the document (i.e. coredata)
id newTreenode = [self treeNode:@"MyTreeNode"
withData:importedData
fromURL:nil];
};
[da runConcurrentForBlock:processingBlock];
[da release];
}
-(NSTreeNode *) treeNode:(NSString *) entityName
withData:(id) data
fromURL:(NSURL *) url
{
/**
entityName and url (filelocation) are not an intrinsic part of the data and should therefore be handled separately
*/
__block NSTreeNode *newTreeNode = nil;
id newDataNode = nil;
@try //Validity of entityName is not guaranteed. Furthermore processData" may or may not be implemented.
{
//create new datanode
newDataNode = [NSEntityDescription insertNewObjectForEntityForName:entityName
inManagedObjectContext:[self managedObjectContext]]; //Add a new node into the tree.
//process data
// [newDataNode processData:data]; //not used in this test example
[newDataNode setValue:data //data in this test example is a NSString
forKey:@"title"];
//locate corresponding rootNode
void (^identify_loop)(id, NSUInteger, BOOL *) = ^(id rootNode, NSUInteger index, BOOL *stop)
{
if ([[rootNode representedObject] isEqual:newDataNode])
{
newTreeNode = rootNode;
*stop = YES;
}
};
[[[self arrangedObjects] childNodes] enumerateObjectsWithOptions:(NSEnumerationConcurrent & NSEnumerationReverse)
usingBlock:identify_loop];
}
@catch (NSException * e)
{
NSLog(@"Caught exception: Invalid entityName \"%@\" or does not implement \"processData\"",entityName);
if (newDataNode)
[[self managedObjectContext] deleteObject:newDataNode];
newTreeNode = nil;
}
return newTreeNode;
}
@end
Code:
#import "DataAdding.h"
@implementation DataAdding
-(void) runConcurrentForBlock:(void (^)(NSURL *,id, NSError *)) processingBlock
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{ //don't wait for me
//simulate a long process: many objects that take some processing time
for (int i =0; i<55; i++)
{
sleep(1); //data reading/processing which takes a long time is handled on a global queue.
[B] dispatch_async(dispatch_get_main_queue(),^{ //finishing up, don't wait for me => Part of edit[/B]
processingBlock(nil,[NSString stringWithFormat:@"%i",i],nil); //adding results to the datasource is done on the main queue
[B]}); => Part of edit[/B]
}
});
}
@end
Edit:
If the data is added to coredata or the datasource in general on the main queue, then everything works fine (so far at least).
Inspired by this link
Last edited: