PDA

View Full Version : NSTreecontroller, selectedobjects, coredata, ghost object causes crash (caching)




MrFusion
Sep 1, 2008, 03:32 PM
I have a bunch of NSManagedobjects, controlled by a NSTreecontroller.
Based on the selection in the NSTreecontroller, I load extra data into the node.
When the selection is changed, I want to unload this extra data.
Thus I am working on caching part of the data.

This is the code


- (void)outlineViewSelectionDidChange:(NSNotification *)notification {
int i;
NSMutableArray *oldItems = [NSMutableArray arrayWithArray:selectedItems];
//load cache
selectedItems = [self selectedObjects];
for (i=0; i< [selectedItems count]; i++) {
id node = [selectedItems objectAtIndex:i];
if ([node respondsToSelector:@selector(enablecache)]){
[node enablecache];
if ([[node dataColumn] intValue] == -1) {
if ([oldItems containsObject:node])
printf("found"); //nothing happens, can't find the node in the oldItems array, while it really is there (I think)
[oldItems removeObjectIdenticalTo:node];
int nofdc = [node determineNumberofDatacolumns]; //get number of datacolumns (includes x)
if (nofdc > 2) {//x=1 en y=2
//copy characteristics (if necessary)
NSString *filepath = [node valueForKey:@"filepath"];
NSManagedObjectContext *moc = [self managedObjectContext];
int i;
//get parent node
id parent = [node parent];
//create replacement node
id replacementNode = [NSEntityDescription insertNewObjectForEntityForName:@"MeasuredData"
inManagedObjectContext:moc];
[replacementNode setValue:[node valueForKey:@"title"]
forKey:@"title"];
[parent addChildrenObject:replacementNode];
[replacementNode enablecache]; //original node+cache removed, no data to display, will crash
//thus load cache (calculate averages from children data) from replacement node

for (i=1; i<nofdc;i++) {//0 is x column
id child = [NSEntityDescription insertNewObjectForEntityForName:@"specialfiletype"
inManagedObjectContext:moc];
[child setValue:filepath
forKey:@"filepath"]; //set filepath
[child setValue:[NSNumber numberWithInt:i]
forKey:@"dataColumn"];
[child setValue:[NSString stringWithFormat:@"%@ [%i]",[node valueForKey:@"title"],i]
forKey:@"title"];
[replacementNode addChildrenObject:child];//add children to replacement node
}
//remove old node
[moc deleteObject:node];
[moc processPendingChanges];
} else if (nofdc == 2){
//copy characteristics (if necessary)
NSString *filepath = [node valueForKey:@"filepath"];
NSManagedObjectContext *moc = [self managedObjectContext];
//get parent node
id parent = [node parent];
//create replacement node
id replacementNode = [NSEntityDescription insertNewObjectForEntityForName:@"anotherFiletype"
inManagedObjectContext:moc];
[replacementNode setValue:[node valueForKey:@"title"]
forKey:@"title"];
[replacementNode setValue:filepath
forKey:@"filepath"]; //set filepath
[replacementNode setValue:[NSNumber numberWithInt:1]
forKey:@"dataColumn"];
[parent addChildrenObject:replacementNode];
//remove old node
[moc deleteObject:node];
[moc processPendingChanges];
}
}
}
}



//empty cache
NSMutableSet *oldset = [NSMutableSet setWithArray:oldItems];
NSMutableSet *newset = [NSMutableSet setWithArray:selectedItems];
[oldset minusSet:newset]; //should be removed
NSEnumerator *numenor = [oldset objectEnumerator];
id oldnode;
while (oldnode = [numenor nextObject]){
if(oldnode){
if ([oldnode respondsToSelector:@selector(disablecache)])
//[node disablecache]; //crashes because node no longer exists, but still has a presence in array
NSLog(@"%@",[oldnode valueForKey:@"title"]);
#warning node might no longer exist due to replacement.
}
}

//update GUI
[dataview reloadData]; //x,y table
[[NSNotificationCenter defaultCenter] postNotificationName:@"didChangeSelection"
object:nil]; //update plot
}



The problem with this code is that one node needs to be replaced by a new type of node with a buch of children nodes. How many depends on the datafile, thus the progam needs to read the file first. This can't be done while loading the files for the first time, because there are to many and loading would take too long. Caching is required. I only have so much memory.
But for some reason, when the old is replaced by a new node with children, the old node still has a presence in the "oldItems" array. Even though I try to remove it from the array. Because the node itself no longer exists (title for example will give (null)), it will crash the progam. The node is also not found in the oldItems (see printf statement), while nothing has changed except the selection.

In summary, I need to remove node from oldItems because it no longer exists and will crash otherwise when trying to unload no longer existing cache data.

It's not so nice code anymore, because I have been trying to fix it by changing and moving things around. It will need some cleaning up, once it's working.

If you can show me the way of my errors, it will be much appreciated. Thanks.

PS. despite the printf statements, I do try to use the debugger as much as possible



MrFusion
Sep 2, 2008, 04:00 PM
I have a bunch of NSManagedobjects, controlled by a NSTreecontroller.
Based on the selection in the NSTreecontroller, I load extra data into the node.
When the selection is changed, I want to unload this extra data.
Thus I am working on caching part of the data.

This is the code


- (void)outlineViewSelectionDidChange:(NSNotification *)notification {
int i;
NSMutableArray *oldItems = [NSMutableArray arrayWithArray:selectedItems];
//load cache
selectedItems = [self selectedObjects];
for (i=0; i< [selectedItems count]; i++) {
id node = [selectedItems objectAtIndex:i];
if ([node respondsToSelector:@selector(enablecache)]){
[node enablecache];
if ([[node dataColumn] intValue] == -1) {
if ([oldItems containsObject:node])
printf("found"); //nothing happens, can't find the node in the oldItems array, while it really is there (I think)
[oldItems removeObjectIdenticalTo:node];
int nofdc = [node determineNumberofDatacolumns]; //get number of datacolumns (includes x)
if (nofdc > 2) {//x=1 en y=2
//copy characteristics (if necessary)
NSString *filepath = [node valueForKey:@"filepath"];
NSManagedObjectContext *moc = [self managedObjectContext];
int i;
//get parent node
id parent = [node parent];
//create replacement node
id replacementNode = [NSEntityDescription insertNewObjectForEntityForName:@"MeasuredData"
inManagedObjectContext:moc];
[replacementNode setValue:[node valueForKey:@"title"]
forKey:@"title"];
[parent addChildrenObject:replacementNode];
[replacementNode enablecache]; //original node+cache removed, no data to display, will crash
//thus load cache (calculate averages from children data) from replacement node

for (i=1; i<nofdc;i++) {//0 is x column
id child = [NSEntityDescription insertNewObjectForEntityForName:@"specialfiletype"
inManagedObjectContext:moc];
[child setValue:filepath
forKey:@"filepath"]; //set filepath
[child setValue:[NSNumber numberWithInt:i]
forKey:@"dataColumn"];
[child setValue:[NSString stringWithFormat:@"%@ [%i]",[node valueForKey:@"title"],i]
forKey:@"title"];
[replacementNode addChildrenObject:child];//add children to replacement node
}
//remove old node
[moc deleteObject:node];
[moc processPendingChanges];
} else if (nofdc == 2){
//copy characteristics (if necessary)
NSString *filepath = [node valueForKey:@"filepath"];
NSManagedObjectContext *moc = [self managedObjectContext];
//get parent node
id parent = [node parent];
//create replacement node
id replacementNode = [NSEntityDescription insertNewObjectForEntityForName:@"anotherFiletype"
inManagedObjectContext:moc];
[replacementNode setValue:[node valueForKey:@"title"]
forKey:@"title"];
[replacementNode setValue:filepath
forKey:@"filepath"]; //set filepath
[replacementNode setValue:[NSNumber numberWithInt:1]
forKey:@"dataColumn"];
[parent addChildrenObject:replacementNode];
//remove old node
[moc deleteObject:node];
[moc processPendingChanges];
}
}
}
}



//empty cache
NSMutableSet *oldset = [NSMutableSet setWithArray:oldItems];
NSMutableSet *newset = [NSMutableSet setWithArray:selectedItems];
[oldset minusSet:newset]; //should be removed
NSEnumerator *numenor = [oldset objectEnumerator];
id oldnode;
while (oldnode = [numenor nextObject]){
if(oldnode){
if ([oldnode respondsToSelector:@selector(disablecache)])
//[node disablecache]; //crashes because node no longer exists, but still has a presence in array
NSLog(@"%@",[oldnode valueForKey:@"title"]);
#warning node might no longer exist due to replacement.
}
}

//update GUI
[dataview reloadData]; //x,y table
[[NSNotificationCenter defaultCenter] postNotificationName:@"didChangeSelection"
object:nil]; //update plot
}



The problem with this code is that one node needs to be replaced by a new type of node with a buch of children nodes. How many depends on the datafile, thus the progam needs to read the file first. This can't be done while loading the files for the first time, because there are to many and loading would take too long. Caching is required. I only have so much memory.
But for some reason, when the old is replaced by a new node with children, the old node still has a presence in the "oldItems" array. Even though I try to remove it from the array. Because the node itself no longer exists (title for example will give (null)), it will crash the progam. The node is also not found in the oldItems (see printf statement), while nothing has changed except the selection.

In summary, I need to remove node from oldItems because it no longer exists and will crash otherwise when trying to unload no longer existing cache data.

It's not so nice code anymore, because I have been trying to fix it by changing and moving things around. It will need some cleaning up, once it's working.

If you can show me the way of my errors, it will be much appreciated. Thanks.

PS. despite the printf statements, I do try to use the debugger as much as possible


For those interested/prosperity.
The solution was staring me in the face, but I didn't see it.

cacheditems and keepitems is a NSMutableArray.
in pseudocode:

loop selecteditems
if selecteditem needs replacement
replace selecteditem
else
add selecteditem to keepitems

remove keepitems from cacheditems
loop cacheditems
disable cache for each cacheditem

set cacheditems to keepitems


it works, but it's one step back since it removes all old data without checking if it might still be needed.