Hey experts, I'm having a little trouble with NSThread. Xcode keeps on giving me "* __NSAutoreleaseNoPool(): Object 0x5694dc0 of class NSCFString autoreleased with no pool in place - just leaking" errors.
I'm correctly declaring the pool with the line
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
then at the end of my loop I use:
[pool release];
Is it because I'm using a delegate method as the performSelectorInBackground?
Thanks stackoverflow.
- (void)preFetch { //process filenames to be downloaded and assign types to each one
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *regions = [NSArray arrayWithObjects: #"dr_national", #"ds_ir", #"conus_FL360", #"FL360_conus", #"dr_nw", #"dr_nc", #"dr_ne", #"dr_sw", #"dr_sc", #"dr_se", #"ds_ir_nw", #"ds_ir_nc", #"ds_ir_ne", #"ds_ir_sw", #"ds_ir_sc", #"ds_ir_se", nil];
NSError* error;
for (NSString *regionDir in regions) {
NSLog(#"region now: %#", regionDir); foo = 0;
NSString *regUrl = [NSString stringWithFormat:#"http://someUrl/%#/index.lst", regionDir ];
NSString* text1 = [NSString stringWithContentsOfURL:[NSURL URLWithString:regUrl ] encoding:NSASCIIStringEncoding error:&error];
NSArray *listItems = [text1 componentsSeparatedByString:#"\n"];
for (int k=0; k<[listItems count]; k++) {
if ([[listItems objectAtIndex:k] length] != 0){
NSString *newpath = [NSString stringWithFormat:#"http://someUrl/%#", [listItems objectAtIndex:k]];
NSLog(#"newpath: %#",newpath);
[self performSelectorInBackground:#selector(moveProgressBar) withObject:nil];
[self fetchImages:newpath:type]; //pass multiple arguments to fetchImages, newpath and type
}
}
}
[pool release];
}
- (void)moveProgressBar{
[delegate increaseAmount];
}
You should just set up an autorelease pool in your method, since that's being called on a different thread.
- (void)moveProgressBar
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[delegate increaseAmount];
[pool drain];
}
Edit
Having said that, looking at the code itself, it seems that you might be trying to update the UI from a background thread? Any code that does that should be executed on the main thread.
If you have a long running process that you want to run which doesn't lock the UI, and keeps the user updated on progress, the typical pattern would be to do the processing itself on a background thread, and periodically update the UI using performSelectorOnMainThread:.
Related
In objective-c manual it is written that something like
return [[[SomeClass alloc] init] autorelease];
can be done and then release is not necessary at any point, even not in the function that received this object. Who owns this object then? when will it be released?
The current NSAutoreleasePool does and will take care of the releasing when drained.
IBAction calls get wrapped into an NSAutoreleasePool that gets drained after the call.
For all non-IBAction calls the following would apply:
Say, you have these methods:
- (id)foo {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
SomeClass *bar = [self bar]; //bar still exists here
//do other stuff
//bar still exists here
//do other stuff
//bar still exists here
[pool drain]; //bar gets released right here!
//bar has been released
}
- (id)bar {
return [[[SomeClass alloc] init] autorelease]; //bar will still be around after the return
}
Consider another scenario:
- (void)foo {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//do other stuff
[self bar];
//do other stuff
[pool drain]; //the string from blee would be released right here;
}
- (void)bar {
[self baz];
}
- (void)baz {
NSString *string = [self blee];
}
- (id)blee {
return [NSString string]; //autoreleased
}
As you can see the autoreleased string object did not even have to be used in or get returned to the scope in which the pool was created.
NSAutoreleasePools exist on a stack (one per thread) and autoreleased objects get owned by the pool that's topmost at the time of the call to autorelease.
Update:
If you are dealing with a tight loop and want to keep the memory moderately low while not slowing down your loop, consider doing something like this:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for (NSUInteger i = 0; i < 1000000000; i++) {
[NSString stringWithString:#"foo"];
if (i % 1000 == 0) {
[pool drain];
pool = [[NSAutoreleasePool alloc] init];
}
}
[pool drain];
However NSAutoreleasePool is highly optimized, so one pool per iteration usually isn't much of a problem.
To fully understand how NSAutoreleasePools work read this excellent article by Mike Ash
OK, I have a memory management problem that is driving me up a wall. At one point I swear this worked with no problems, but now it's leaking memory everywhere and I can't figure out why.
To begin with I'm starting an NSTask and then running a loop while the task is running.
NSTask *encodingTask = [[NSTask alloc] init];
NSFileHandle *taskStdout = [NSFileHandle fileHandleForWritingAtPath:encodingOutput];
[encodingTask setStandardOutput:taskStdout];
[encodingTask setStandardError:taskStdout];
NSString argString = [NSString stingWithString: #"some arguments"];
[encodingTask setArguments:taskArgs];
[encodingTask setLaunchPath:somePath];
[encodingTask launch];
while ([encodingTask isRunning]){
sleep(1);
[self encodeProgressTimer];
}
The encodeProgessTimer method is grabbing the last line from the stdOut and placing that in the menu bar:
- (void)encodeProgressTimer
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"menuProgress"]) {
// Read the last line
NSString *fileData = [NSString stringWithContentsOfFile:encodingOutput encoding:NSASCIIStringEncoding error:nil];
NSArray *lines = [fileData componentsSeparatedByString:#"\r"];
NSString *lastLine = [lines objectAtIndex:[lines count] - 1];
NSString *percent;
NSString *eta;
BOOL dataFound = NO;
if ([lastLine length] == 71) {
dataFound = YES;
percentRange = (NSRange) {23,5};
etaRange = (NSRange) {61,9};
percent = [lastLine substringWithRange:percentRange];
eta = [lastLine substringWithRange:etaRange];
}
else if ([lastLine length] == 72) {
dataFound = YES;
percentRange = (NSRange) {23,5};
etaRange = (NSRange) {62,9};
percent = [lastLine substringWithRange:percentRange];
eta = [lastLine substringWithRange:etaRange];
}
else if ([lastLine length] == 70) {
dataFound = YES;
percentRange = (NSRange) {23,5};
etaRange = (NSRange) {60,9};
percent = [lastLine substringWithRange:percentRange];
eta = [lastLine substringWithRange:etaRange];
}
if (dataFound) {
NSMutableString *bottomStr = [[NSMutableString alloc]
initWithFormat:#"Encoding: %#%% - ETA %#", percent, eta];
[appDelegate setMenuTop:topString andBottom:bottomStr];
[bottomStr release];
}
}
}
It's my understanding that anything I'm not specifically allocating and initializing should be auto released when the method has completed, but that isn't the case. Memory usage goes up exponentially every second when this is called. If I look at my memory allocations the number of living CFstings goes through the roof. If I turn of encodeProgressTimer my problems go away. I tried adding an autorelease pool to encodeProgressTimer which made memory usage very stable, however after 20 minutes or so of running I get a EXC_BAD_ACCESS. Turning on Zombies turns that into:
*** -[NSConcreteAttributedString _drawCenteredVerticallyInRect:scrollable:]: message sent to deallocated instance 0x2bc756e0
I actually went through and changed each variable declaration into it's alloc/init counterpart and manually released them, but that didn't solve the problem either. At this point I'm pretty stumped.
Also for the sake of completeness the [appDelegate setMenuTop: andBottom:] method looks like this:
-(void) setMenuTop: (NSString *) top andBottom: (NSString *) bottom
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"menuProgress"]) {
[statusItem setImage:nil];
NSMutableParagraphStyle *lineHeight = [[NSMutableParagraphStyle alloc] init];
[lineHeight setMaximumLineHeight:10.5];
[lineHeight setLineBreakMode:NSLineBreakByTruncatingMiddle];
OperationQueue *opQueue = [OperationQueue sharedQueue];
NSString *sBuffer = [[NSMutableString alloc] initWithFormat: #"%# (%i More)\n%#", top, [opQueue queueCount] - 1, bottom];
attributes = [[NSDictionary alloc] initWithObjectsAndKeys:[NSFont menuFontOfSize:9], NSFontAttributeName, lineHeight, NSParagraphStyleAttributeName, nil];
if (statusTitle)
[statusTitle release];
statusTitle = [[NSAttributedString alloc] initWithString: sBuffer attributes: attributes];
[statusItem setAttributedTitle: statusTitle];
[lineHeight release];
[sBuffer release];
[attributes release];
}
}
There will be loads of stuff in the autorelease pool, but you need to explicitly drain it for the memory to go away. Change your while loop as follows:
while ([encodingTask isRunning]){
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
sleep(1);
[self encodeProgressTimer];
[pool drain];
}
Other stuff: if you are running this on a thread, you can't update user interface items directly. You need to use something like performSelectorOnMainThread: to actualy update the UI. If you are not running this on a thread, you need to rethink your design. The whole UI of your application will freeze while the loop is running.
You may want to use properties here or nil out the reference.
if (statusTitle) {
[statusTitle release];
statusTitle = nil;
}
In one of my applications for iPad I am building up the db remotely using a json string then converted to NSArray to be insert in core data and then I donwload around 600Mb of images on the ipad. All this is created in a background thread causing from the beginning some memory issue.
I get hold of the problem nesting 3 different NSAutoreleasePool in the operation and releasing each of them at a convenient point. I got no error, nor leak, nor warning. I was just wondering if it is a good way of doing it or I just miss something.
Here a schematic example (the real code is quite long):
- (void)main{
#try {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // first pool
[managedOC lock];
NSArray *results = [self loadEntitiesWithGroupName:#"Project" andName:#"projects"];
NSAutoreleasePool *prjPool; // second pool
for (NSDictionary *thisResult in results) {
prjPool = [[NSAutoreleasePool alloc] init];
Project *prj = [[Project alloc] initWithEntity:[NSEntityDescription entityForName:#"Project" inManagedObjectContext:self.managedOC] insertIntoManagedObjectContext:self.managedOC];
prj.name = [thisResult objectForKey:#"name"];
[prj saveThumbnail:[thisResult objectForKey:#"thumbnail"]];
//Slides. Those are the images that take so mutch space.
NSArray *slides = [thisResult objectForKey:#"slides"];
NSAutoreleasePool *slidePool; // third pool
if(slides != kCFNull){
slidePool = [[NSAutoreleasePool alloc] init];
for(NSDictionary *slide in slides){
Slide *thisSlide = [[Slide alloc] initWithEntity:[NSEntityDescription entityForName:#"Slide" inManagedObjectContext:self.managedOC] insertIntoManagedObjectContext: self.managedOC];
thisSlide.path = prj.path;
[thisSlide saveFile:[slide objectForKey:#"file"]];
[prj addSlidesObject:thisSlide];
[thisSlide release];
[slidePool drain];
}
}
[prj release];
[result release];
[prjPool drain];
}
[self.managedOC unlock];
[totResult release];
[pool drain];
}
#catch (NSException * e) {
}
I'm surprised your code doesn't crash. -drain behaves the same as -release in the reference counted environment, so the following over releases the pool
slidePool = [[NSAutoreleasePool alloc] init]; // this is in the wrong place
for(NSDictionary *slide in slides){
Slide *thisSlide = [[Slide alloc] initWithEntity:[NSEntityDescription entityForName:#"Slide" inManagedObjectContext:self.managedOC] insertIntoManagedObjectContext: self.managedOC];
thisSlide.path = prj.path;
[thisSlide saveFile:[slide objectForKey:#"file"]];
[prj addSlidesObject:thisSlide];
[thisSlide release];
[slidePool drain];
}
Unless there is only one object in the slides collection. You need this:
for(NSDictionary *slide in slides){
slidePool = [[NSAutoreleasePool alloc] init]; // this is in the right place
Slide *thisSlide = [[Slide alloc] initWithEntity:[NSEntityDescription entityForName:#"Slide" inManagedObjectContext:self.managedOC] insertIntoManagedObjectContext: self.managedOC];
thisSlide.path = prj.path;
[thisSlide saveFile:[slide objectForKey:#"file"]];
[prj addSlidesObject:thisSlide];
[thisSlide release];
[slidePool drain];
}
Other than that, you have the right general idea.
You should drain your outermost pool in the finally block of your exception handler so that it is not skipped if an exception is raised i.e. you should do this:
- (void)main{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // first pool
#try {
// Do al your stuff
}
#catch (NSException * e) {
}
#finally
{
[pool drain];
}
}
I have noted several other threads on this topic and have tried wrapping my threaded code with:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[pool release];
but the errors still come.
I am using a static method to instantiate a dictionary of words.
Here is some code:
-(id)init
[NSThread detachNewThreadSelector:#selector(loadDictionary:) toTarget:[IntroScreen class] withObject:nil];
[NSThread setThreadPriority:1.0];
return self;
}
+(void)loadDictionary:(id)param
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[[SimpleAudioEngine sharedEngine] preloadEffect:#"click.wav"];
[[SimpleAudioEngine sharedEngine] preloadEffect:#"pop.wav"];
[[SimpleAudioEngine sharedEngine] preloadEffect:#"dink.wav"];
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:#"musicloop.wav"];
[WordDictionary configDictionary];
[pool release];
}
+(void)configDictionary
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Serializer * mySerializer = [[Serializer alloc] init];
[WordDictionary setDictionary:[mySerializer readApplicationPlist:#"x"]];
NSString * string;
NSString *filePath = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:#"x.txt"];
NSString *info = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
NSArray *arrayOfLines = [info componentsSeparatedByString:#"\r\n"];
[WordDictionary setDictionary:[[NSMutableDictionary alloc] init]];
[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
int len = [arrayOfLines count];
for(int i = 0; i < len; i++)
{
string = [arrayOfLines objectAtIndex:i];
NSString * blankString = [NSString stringWithString:#""];
[[WordDictionary dictionary] setObject:blankString forKey:string];
double calc = ((double)i / (double)len) * 100.0;
[WordDictionary setProgress:(int)calc];
}
[mySerializer writeApplicationPlist:[WordDictionary dictionary] toFile:#"s"];
[WordDictionary setProgress:100];
[pool release];
}
Is there something I should know about using static class methods with new selector threads?
Thank you for your help
First, there are no static methods in Objective-C. There are class methods.
Secondly, your code shows both methods wrapped in autorelease pools. The warning must be coming from somewhere else.
Finally, your code leaks like a sieve. You aren't following the memory management rules. And there are some nonsense statements in there.
Specifically:
[WordDictionary setDictionary:[[NSMutableDictionary alloc] init]];
Unless +setDictionary: is breaking the memory management rules, the above leaks.
This statement [NSMutableDictionary dictionaryWithContentsOfFile:filePath]; effectively does nothing unless you do something with the return value.
Also, mySerializer is leaking.
Try running the analyzer over your code and fixing the problem. You should also read this and this.
Ah the [NSMutableDictionary dictionaryWithContentsOfFile:filePath]; was part of an experiment I was attempting to make the dictionary access faster. I should have removed it from this example.
I have just read the memory management rules, and understand that
[WordDictionary setDictionary:[[NSMutableDictionary alloc] init]]; appears to be poorly planned instantiation because I have no way to release it from within configDictionary as the reference is lost. But actually I don't ever want to release it, it lives for the entire lifetime of my application. Probably bad practice just the same.
mySerializer should definitely be released at the bottom.
I was just wondering if class methods had any special rules regarding autorelease pools and memory.
I will look over the documents you sent me and try to figure out the Analyzer, thank you for your help.
I would like to send an object back to the main thread from worker thread. However do auto-release pools work between threads? Is there anything wrong with the following code:
-(void)mainThreadReceiveResult:(id)response
{
[response retain];
/* Do some stuff with response */
[response release];
}
-(void)workerThreadDoWork
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
response * response = [[[response alloc] init] autorelease];
response->someData = [self getSomeData];
[delegate performSelectorOnMainThread:#selector(receiveResult:) withObject:response waitUntilDone:NO];
[pool release];
}
Seems to work fine. However is it possible that the worker thread could reach [pool release] before the main thread is able to retain it?
Your code shouldn't crash: performSelectorOnMainThread: retains its arguments until after the selector finishes, so your retain/release pair is superfluous.
See the documentation:
This method retains the receiver and the arg parameter until after the selector is performed.
Also: you should probably [pool drain] instead of [pool release].
This may answer your question.
Here's what he did to solve the problem. The explanation is given in that link.
- (void)runSomethingThatWillFail:(NSError **)error {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *directoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:#"/BOGUS" error:error];
[*error retain];
[pool release];
[*error autorelease];
}