NSAutoreleaseNoPool error when using sortUsingDescriptors

Discussion in 'Mac Programming' started by jctj, Apr 8, 2010.

  1. jctj macrumors newbie

    Joined:
    Feb 24, 2010
    #1
    OK. I did a google search on "NSAutoreleaseNoPool sortUsingDescriptors" and got nothing but one broken link.

    I have an optimizer that uses the parent and children analogy. A NSMutableArray ("thePopulation") holds individual custom objects. When optimizing, I make another NSMutableArray called "theGenePool" using setArray:thePopulation. Then, in the runAGeneration method I take 2 objects from theGenePool, use them to make children (new objects, sharing attributes from each 'parent'), and add those children to thePopulation. The parents are removed from theGenePool once they are used.

    This way I can keep track of which 'parent' objects in thePopulation haven't been used yet, while leaving those same parents still in thePopulation array.

    My problem happens before I get to the runAGeneration method, however, so the details of that method are unimportant. In the code (below) I get lots of errors saying:
    Code:
    _NSAutoreleaseNoPool(): Object 0x1b7170 of class NSCFNumber autoreleased with no pool in place - just leaking
    Please note - I have an NSAutoreleasePool in both the calling View object and in the optimize method in the Controller object so there *is* a pool. Each of my custom objects that are in theGenePool use 3 arrays of (floats), with 21 floats in each array, so I am confused why I get only a single "NSNumber" being autoreleased with each object in theGenePool array. I assume that the sortUsingDescriptor method uses a NSNumber for each object in the array it is sorting. Also, to clarify, I use a breakpoint to stop the execution as it enters the optimize method. Then I watch the debugger console as I single step through the optimize method. I get no errors at all until I step over the sortUsingDescriptors line, at which point I get one error per object and 2 more to boot (20 objects = 22 errors, 40 objects = 42 errors, etc.).


    Here is my View object. I have 2 buttons, one of which runs the optimizer normally and the other runs the same optimizer (exactly) in a thread. That is the only difference between "runNormal" and "runThreaded". As you can see, the "runThreaded" method adds an NSAutoreleasePool but is otherwise identical to the "runNormal" method.
    Code:
    //	IBActions
    -(IBAction)runNormal: (id)sender
    {
    	// Clear a NSMutableArray instance variable before each run
    	if ([myHallOfFame count] > 0) 
    		[myHallOfFame removeAllObjects];
    	
    	// Update the GUI based on input from the user.
    	[self setupMeters];
    	[numOneView setIndi:nil];
    	[numTwoView setIndi:nil];
    	[numThreeView setIndi:nil];
    	[currentView setIndi:nil];
    	[hallOfFameView setIndi:nil];
    	
    	// This is my controller object - it gets its initial values from the GUI textFields (boxes)
    	myController = [[EvoOptimController alloc] initWithParents:[numOfParents intValue]
    														facets:[numOfFacets intValue]
    														maxPop:[maxPop intValue]
    													   initPop:[initPop intValue]
    													   warFreq:[warFreq intValue]
    														 MGWOI:[GWOI intValue]
    														minGen:[minGen intValue]
    													 minImprov:[minImprove floatValue]
    														 Delta:[delta floatValue]
    													   Environ: self];
    	
    	// Here is where the 'work' gets done, in the "optimize" method.
    	[myController optimize];
    	
    	// Released to balance the 'alloc' above.
    	[myController release];
    }
    
    -(IBAction)runThreaded: (id)sender
    {
    	// Autorelease pool used for threading
    	NSAutoreleasePool * viewReleasePool = [[NSAutoreleasePool alloc] init];
    	
    	// Clear a NSMutableArray instance variable before each run
    	if ([myHallOfFame count] > 0) 
    		[myHallOfFame removeAllObjects];
    	
    	// Update the GUI based on input from the user.	
    	[self setupMeters];
    	[numOneView setIndi:nil];
    	[numTwoView setIndi:nil];
    	[numThreeView setIndi:nil];
    	[currentView setIndi:nil];
    	[hallOfFameView setIndi:nil];
    	
    	myController = [[EvoOptimController alloc] initWithParents:[numOfParents intValue]
    														facets:[numOfFacets intValue]
    														maxPop:[maxPop intValue]
    													   initPop:[initPop intValue]
    													   warFreq:[warFreq intValue]
    														 MGWOI:[GWOI intValue]
    														minGen:[minGen intValue]
    													 minImprov:[minImprove floatValue]
    														 Delta:[delta floatValue]
    													   Environ: self];
    	
    	
    	// Same as "runNormal" but creates a thread for the optimize method.
    	[myController performSelectorInBackground:@selector(optimize) withObject:nil];		
    	
    	[myController release];
    	
    	[viewReleasePool release];
    }

    The optimize function is in the controller, aptly named "myController". The "fertilityAllocation" goes through the array and changes a single variable held by each object called "numOfKids" that determines how many children each parent can have. It has no other effect on the objects. Here is the code with the offending line in red:
    Code:
    -(void)optimize
    {
    	// Autorelease pool for threading.
    	NSAutoreleasePool * optimizeReleasePool = [[NSAutoreleasePool alloc] init];
    	
    	int				gen = 0,		// the generation of then new individuals
    	i;			// Counters 
    	
    	srandomdev();
    	GWOI = 0;	
    	[self setNumOne:[thePopulation objectAtIndex:0]];
    	[self setNumTwo:[thePopulation objectAtIndex:1]];
    	[self setNumThree:[thePopulation objectAtIndex:2]];
    	
    	do
    	{
    		gen++;
    		
    // Explanation: thePopulation is a mutable array of custom objects. theGenePool copies
    // thePopulation so it can remove objects (parents) as they get used, without directly changing
    // thePopulation itself. 
    		// Prep the genePool
    		[theGenePool setArray:thePopulation];
    		
    		// Set the optimizing variables 
    		baseDelta = [self determineBaseDelta];
    		deltaDelta = [self determineDD];				
    		
    		// Determine how many kids each parent can have based on ranking w/in the genePool
    		[self fertilityAllocation];
    				
    		// Sort theGenePool by Score
    		[COLOR="red"][theGenePool sortUsingDescriptors:sortBy];[/COLOR]
    		
    		// Run a single generation
    		[self runAGeneration: gen];
    		[optimizeReleasePool drain];
    		
    		// Compare new thePopulation with old ranking Indi's and increment GWOI if no new #1
    		// If a new #1 is found during valleyJumpingMode or Desperation, then the last #1 was
    		// a local hero and needs to be added to the list of localHeroes.
    		// Add new # 1 to both the Controller HoF AND the View's HoF list.
    		if ([[thePopulation objectAtIndex:0] score] - [numOne score] > minImprovement)	
    		{
    			if(GWOI > localPeakMode) [localHeroes addObject:numOne];
    			
    			GWOI = 0;
    			[self setNumOne:[thePopulation objectAtIndex:0]];
    			[myView updateHallOfFameList:numOne];
    			[hallOfFame insertObject:numOne	atIndex:0];
    		}	// numOne
    		else GWOI++;
    	}
    	while (GWOI < maxGenWithOutImprovement || gen < minGens );
    	
    	[optimizeReleasePool release];
    }	// optimize 
    The error I get in the debugger window is as follows. I include 8 messages to show that they are all the exact same message. I get one message per object in the array theGenePool when I hit the sortUsingDescriptors method:

    Code:
    2010-04-08 20:21:49.414 EvoOptim 5[1794:460b] *** _NSAutoreleaseNoPool(): Object 0x18e6b0 of class NSCFNumber autoreleased with no pool in place - just leaking
    Stack: (0x92612f4f 0x9251f432 0x925495cb 0x92549067 0x92576c5a 0x92576842 0x925c1b64 0x2b20 0x92525dfd 0x925259a4 0x915ec155 0x915ec012)
    2010-04-08 20:21:49.427 EvoOptim 5[1794:460b] *** _NSAutoreleaseNoPool(): Object 0x18e6c0 of class NSCFNumber autoreleased with no pool in place - just leaking
    Stack: (0x92612f4f 0x9251f432 0x925495cb 0x92549067 0x92576c5a 0x92576842 0x925c1b64 0x2b20 0x92525dfd 0x925259a4 0x915ec155 0x915ec012)
    2010-04-08 20:21:49.427 EvoOptim 5[1794:460b] *** _NSAutoreleaseNoPool(): Object 0x18e6d0 of class NSCFNumber autoreleased with no pool in place - just leaking
    Stack: (0x92612f4f 0x9251f432 0x925495cb 0x92549067 0x92576c5a 0x92576842 0x925c1b64 0x2b20 0x92525dfd 0x925259a4 0x915ec155 0x915ec012)
    2010-04-08 20:21:49.435 EvoOptim 5[1794:460b] *** _NSAutoreleaseNoPool(): Object 0x18e6e0 of class NSCFNumber autoreleased with no pool in place - just leaking
    Stack: (0x92612f4f 0x9251f432 0x925495cb 0x92549067 0x92576c5a 0x92576842 0x925c1b64 0x2b20 0x92525dfd 0x925259a4 0x915ec155 0x915ec012)
    2010-04-08 20:21:49.444 EvoOptim 5[1794:460b] *** _NSAutoreleaseNoPool(): Object 0x1610b610 of class NSCFNumber autoreleased with no pool in place - just leaking
    Stack: (0x92612f4f 0x9251f432 0x925495cb 0x92549067 0x92576c5a 0x92576842 0x925c1b64 0x2b20 0x92525dfd 0x925259a4 0x915ec155 0x915ec012)
    2010-04-08 20:21:49.444 EvoOptim 5[1794:460b] *** _NSAutoreleaseNoPool(): Object 0x1610b620 of class NSCFNumber autoreleased with no pool in place - just leaking
    Stack: (0x92612f4f 0x9251f432 0x925495cb 0x92549067 0x92576c5a 0x92576842 0x925c1b64 0x2b20 0x92525dfd 0x925259a4 0x915ec155 0x915ec012)
    2010-04-08 20:21:49.447 EvoOptim 5[1794:460b] *** _NSAutoreleaseNoPool(): Object 0x1610b630 of class NSCFNumber autoreleased with no pool in place - just leaking
    Stack: (0x92612f4f 0x9251f432 0x925495cb 0x92549067 0x92576c5a 0x92576842 0x925c1b64 0x2b20 0x92525dfd 0x925259a4 0x915ec155 0x915ec012)
    2010-04-08 20:21:49.453 EvoOptim 5[1794:460b] *** _NSAutoreleaseNoPool(): Object 0x1610b640 of class NSCFNumber autoreleased with no pool in place - just leaking
    Stack: (0x92612f4f 0x9251f432 0x925495cb 0x92549067 0x92576c5a 0x92576842 0x925c1b64 0x2b20 0x92525dfd 0x925259a4 0x915ec155 0x915ec012)
    
    Bottom line: how in the world do I debug this?!? I have an autorelease pool in place. I can't step into the sortUsingDescriptors method and the method works just fine (well - no errors in the debugger console, at any rate) when I run the normal method. I am using threading so I can update the GUI as progress is made.
     
  2. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #2
    [optimizeReleasePool drain]; is the problem. In a non-GC environment, it's equivalent to release. If you're going to use that, you need to re-create the autorelease pool before using any more autoreleased objects.
     
  3. jctj thread starter macrumors newbie

    Joined:
    Feb 24, 2010
    #3
    This is why I love you guys....

    I commented out that line, and it worked perfectly.

    I thought the drain command emptied the autorelease pool without releasing the pool itself, so that it was available for more use. It seemed to make sense that this would prevent the frequent creation/destruction of autorelease pools.

    But now I know!

    Thank you!
     
  4. autorelease macrumors regular

    Joined:
    Oct 13, 2008
    Location:
    Achewood, CA
    #4
    kainjow is right, I just wanted to post because of my username. :p

    Not sure if this is what you want to do, but if you want to drain the autorelease pool after each iteration of the do loop, just alloc/init a new NSAutoreleasePool at the start of the do loop and release it at the end of the loop. I wouldn't do this unless you're accumulating huge amounts of allocated memory in the optimize method. Instruments can help you figure out if you should do that. For most uses, it's fine to create a single autorelease pool at the start of a thread and release (drain) it before the thread finishes.
     

Share This Page