NSTreecontroller, selectedobjects, coredata, ghost object causes crash (caching)

Discussion in 'Mac Programming' started by MrFusion, Sep 1, 2008.

  1. macrumors 6502a

    Joined:
    Jun 8, 2005
    Location:
    West-Europe
    #1
    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

    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
     
  2. thread starter macrumors 6502a

    Joined:
    Jun 8, 2005
    Location:
    West-Europe
    #2

    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:
    Code:
    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.
     

Share This Page