Class Methods Used Many Times Cause Leaks? - iphone

Let's say I'm developing a game. I run the following class method thousands of times:
NSBundle *bundle=[NSBundle mainBundle];
I do not create an autorelease pool and release the objects that call the above class method all the time. I create an object, it calls the above class method, I release it, and on, and on, thousands of times.
3 questions:
Is memory leaked?
If I ran:
NSAutoReleasePool *pool=[[NSAutoReleasePool alloc] init];
[pool drain];
would it then effectively release all those NSBundles created in the class method?
if I wrote:
pool=nil;
instead of writing:
[pool drain];
Would the same effect be achieved? Is this good practice?
The three questions are correlated and I will boost the one that clarifies them :)

No, memory is not leaked. [NSBundle mainBundle] returns an autoreleased object, so the autorelease pool will take care of it. However, if you're going to call it a bunch of times in one function or something, you'd be better off either getting a reference to it once and just holding on to it while you need it, or creating your own autorelease pool.
Not exactly. Everything after NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; that is autoreleased will go into pool, and after you drain the pool, they'll be released. Note: [pool drain]; also releases the pool, so don't call [pool drain]; [pool release]; or you will send the release message to the object twice and probably crash.
No. pool = nil; would just lose the reference to pool and the actual pool object would be leaked. You have to call either [pool drain]; or [pool release]; and good practice would be to set pool = nil; afterwards.

If you do not create an autorelease pool, and one does not already exist, any objects that are autoreleased (either by you, or by other libraries you are using) will be leaked.
setting pool=nil; does nothing except leak the whole pool, perhaps if the pool was set in a property i.e.
#property(nonatomic, retain) NSAutoreleasePool *pool;
self.pool = [[[NSAutoreleasePool alloc] init] autorelease];
//now, self is the sole retainer of pool, so if self abandons it it gets drained:
self.pool = nil;
//pool's retain count drops to zero, pool drains.
If you are doing this thousands of times, it might slow things down to drain the pool every time, consider doing it every 5 or ten times, set a counter in the loop and when it hits some decided number, do this:
self.pool = [[[NSAutoreleasePool alloc] init] autorelease];
//this will get rid of the old pool and put in a new one, so you only have to get rid of the one remaining at the end of the loop.

Related

iPhone: Confusion about NSAutoreleasePool

I read about NSAutoReleasePool and understand that it makes memory management easier on iPhone. It is available on NSObject so autorelease message available for every object instance. Moreover, I shouldn't use autorelease a lot as NSAutoReleasePool uses cache memory and you might runout of memory if there are plenty autoreleased objects. Is my understanding correct?
One thing I didn't understand is what is the purpose of creating NSAutoreleasePool explicitly like it's done in following method? What its purpose here? Is it like releasing imgData, img objects automatically? Because I could see that these objects are not released in this method.
- (void)loadImage {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *img = [UIImage imageNamed: #"default_user.png"];
if(![[imgURL absoluteString] isEqualToString: #"0"]) {
NSData *imgData = [NSData dataWithContentsOfURL: imgURL];
img = [UIImage imageWithData: imgData];
}
if([target respondsToSelector: action])
[target performSelectorOnMainThread: action withObject: img waitUntilDone: YES];
[pool release];
}
Thanks.
You can use local autorelease pools to help reduce peak memory
footprint. When your pool is drained, the temporary objects are
released, which typically results in their deallocation thereby
reducing the program’s memory footprint.
I can suggest this explicit autorelease pool was created for manage image loading. Probably that image has a large size (in Mb) and this pool can guarantee that memory will released asap. All autorelease pools organized in stack, so this inner pool will drain early than main pool.
The idea behind auto-release to keep the memory usage of the app low. You see if you haven't used this auto release then this data would have gone to the main autorelease of the app So even if you don't need this image further it still stays in the memory and increases its footprint. Creating a new auto release frees the memory straight away.(the size of the image can be huge.)
The rule is that you must create an autorelease pool in every thread that will use autorelease. In your example, img variable is autoreleased, and assuming the loadImage method is threaded, you must declare a new pool before using any autoreleased memory.

How to handle leaks when using a multiple threads in iphone?

I have called a method in separate thread in viewdidload method
[NSThread detachNewThreadSelector:#selector(callWebService) toTarget:self withObject:nil];
-(void)callWebService{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[NSThread detachNewThreadSelector:#selector(loadImages) toTarget:self withObject:nil];
[pool release];
}
-(void)loadImages{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(reloadTable) withObject:nil waitUntilDone:false];
[pool release];
}
-(void)reloadTable
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[myTableView reloadData];
[pool release];
}
How to handle leaks while using thread? I want to use threads.
Errors
*** __NSAutoreleaseNoPool(): Object 0x604b830 of class NSCFString autoreleased with no pool in place - just leaking
*** -[NSAutoreleasePool release]: This pool has already been drained, do not release it (double release).
The autorelease pool must be drain, not release. So I think if you change it to [pool drain] it should be working fine.
Don't know if you found your answer yet, but as I had hit something similar to your problem, I did some looking. It seems that when you drain a pool, that is (effectively) equivalent to releasing it. The documentation on the class says:
Since you cannot retain an autorelease pool (or autorelease it—see
retain and autorelease), draining a pool ultimately has the effect of
deallocating it.
So, you should only drain a pool once. If you need another context after that point, you should generate a new pool in the same way you generated one earlier in your code.
If there is no pool available, then you can end up leaking (as you mentioned). However, autorelease calls should log a warning message in that instance. Regarding threads, the documentation has this to say
The Application Kit creates an autorelease pool on the main thread at
the beginning of every cycle of the event loop, and drains it at the
end, thereby releasing any autoreleased objects generated while
processing an event. If you use the Application Kit, you therefore
typically don’t have to create your own pools. If your application
creates a lot of temporary autoreleased objects within the event loop,
however, it may be beneficial to create “local” autorelease pools to
help to minimize the peak memory footprint.
Hope this helps.

__NSAutoreleaseNoPool(): Object 0x753c2f0 of class General autoreleased with no pool in place - just leaking

I haven't noticed my console output for a while and I've suddenly noticed lots of weird errors.
__NSAutoreleaseNoPool(): Object 0x753c2f0 of class General autoreleased with no pool in place - just leaking
__NSAutoreleaseNoPool(): Object 0x753c300 of class __NSArrayM autoreleased with no pool in place - just leaking
I've no idea where this happening?
Edit..
I use this
[self performSelectorInBackground:#selector(startupStuff) withObject:sender];
With statupStuff I have this
General *rdb = [[General alloc] autorelease];
[rdb refreshDBData];
The Errors happen shortly after code in the refreshDBData method.
Autorelease pools are tied to threads. If you create a thread through performSelectorInBackground then you need to create and destroy an autorelease pool for yourself. So you need startupStuff to look like this:
- (void)startupStuff:(id)sender
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// ... everything else you were doing ...
[pool drain]; //see comment below
}
Addition: Richard below makes the point that drain is preferable to release to acknowledge that (on the desktop, not yet on iOS) you may be running with a garbage collector. Apple's specific words are (source):
In a garbage-collected environment, sending a drain message to a pool triggers garbage collection if necessary; release, however, is a no-op. In a reference-counted environment, drain has the same effect as release. Typically, therefore, you should use drain instead of release.
So I've corrected my example. Suffice to say, this specific question is to do with the iPhone and currently there is no garbage collection on that device. So the originating poster is in the "drain has the same effect as release" camp, not the "drain ... triggers garbage collection if necessary; release, however, is a no-op" camp.
This:
General *rdb = [[General alloc] autorelease];
Is wrong. There should always be a call to an initializer; to -init, at the least.
Try this
[self performSelectorInBackground:#selector(startupStuff) withObject:sender];
-(void)startupStuff:(id)sender
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
General *rdb = [[General alloc] init];
[rdb refreshDBData];
[rdb release];
[pool release];
}
If its not working too then you need to check refreshDBData method.. you are doing something wrong there

With obj-c on the iPhone, is there any harm in autoreleasing everything instead of releasing?

With obj-c on the iPhone, is there any harm in autoreleasing everything instead of releasing?
Eg, this code:
NSString *recipe = [[NSString alloc] initWithUTF8String:sqlite3_column_text(dbps,0)];
[arr addObject:recipe];
[recipe release];
Can be shortened to two lines by autoreleasing the recipe nsstring when i create it:
NSString *recipe = [[[NSString alloc] initWithUTF8String:sqlite3_column_text(dbps,0)] autorelease];
[arr addObject:recipe];
Are there any drawbacks to this? I find it suits my coding style better. Thanks all.
The drawback is that the objects get released later. In most cases this won't be a problem but if you allocate and autorelease thousands of objects in a tight loop this overhead can add up to a point where it impacts performance or even causes your app to run out of memory.
In any case, you might want to use:
[NSString stringWithUTF8String:sqlite3_column_text(dbps,0)];
instead of
[[[NSString alloc] initWithUTF8String:sqlite3_column_text(dbps,0)] autorelease];
If the object does not have a scope outside the function it is used, you should always release it in order to minimize the amount of memory consumed by your application.
If you let the object stick around until the autorelease pool kicks in, a lot of unneeded memory may need to be allocated. This is especially important with iOS 4 and fast app switching, where memory is also allocated to apps in the background.
The lower your own memory footprint is, the bigger a chance there is that your application will not be terminated when in the background in case memory gets tight.
Here is my understanding on the subject, I'm sure I'll get downvoted to death if I say anything wrong ;)
Generally speaking, in tight loops, init + release will be faster than autorelease. Most of the time performance is not an issue
You may not actually want to autorelease at all times. If you want an object to stick around, you'll need to manually release or stick your autoreleased obj in a property with a retain so that you can get at it later otherwise your obj will be released as soon as it goes out of scope
With manual init + release you have more fine grained control over what is happening, this may be useful for advanced multi-threaded scenarios (especially now that GCD is pervasive)
autorelease is probably fine most of the time so long as you understand how reference counting works, but do not treat it like a silver bullet. You can still leak plenty of memory using autorelease incorrectly.
I'll turn the question around: The main disadvantage with release is that it's very easy to forget. Assignment to a local or to an instance variable look pretty much identical (they're entirely identical if you don't assign in an initializer):
Foo * a = [[Foo alloc] init];
myFoo = [[Foo alloc] init];
This, and similar issues, leads to a bunch of potential problems:
When you copy-and-paste code, will you forget a release?
When you change between local and instance variables, will you remember to add/remove the release?
If an exception is thrown, does the code leak? (This is almost always true, but it is traditional to not care about exceptions in Obj-C.)
Personally, the overhead of a memory leak when you refactor code is worse than the overhead of a few autoreleased objects. I occasionally refactor code so it looks like this:
FooView * v = [[[FooView alloc] initWithFrame:CGRectZero] autorelease];
// set up v...
myFoo = [v retain];
Local variables are marginally more efficient.
If you decide that you don't need the instance variable anymore, you just need to comment out one line.
Views are pretty heavyweight, so the autorelease overhead isn't going to matter much
For completeness, if you're doing a bunch of things in a loop (e.g. test code), you can autorelease at the end of every iteration with something like this:
for (size_t n = 100000; n--; ) {
NSAutoreleasePool * pool = [NSAutoreleasePool new];
// ... allocate and autorelease some stuff ...
[pool release]; pool = nil;
}
If you're worried about efficiency, you can gain significant speedups using (e.g.) CFString functions — the Objective-C overhead is pretty significant for small strings.
Problem is when you do autorelease you're not actually releasing the object but adding it to the current autorelease pool. Object will be effectively released when you release that pool which is done every time through the main loop .
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
You can create you're own autorelease pool but I'm not sure it would be better than simply use release.

Attempt to pop an unknown autorelease pool

Does anyone know what this error means?
*** attempt to pop an unknown autorelease pool
I am seeing this in my app that uses NSOperations in NSOperationQueue. Each operation has it's own Autorelease pool. Since each operation is parsing xml and pushing information into Core Data, I periodically 'drain' my autorelease pool and re-create it. If I don't drain the pool, I don't see these errors.
UPDATE:
Here's my code:
In the main of my NSOperation, I alloc the pool and assign it to an 'assign' property like this:
self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
at the end of my main I release it like this:
[self.downloadAndParsePool release];
self.downloadAndParsePool = nil;
If the operation does a lot of parsing and inserting into Core Data, it will call my drain method periodically:
- (void) drainAutoreleasePool
{
// drain and re-create autorelease pool...this method is called periodically to keep memory down during parsing of large trees
if (self.downloadAndParsePool)
{
[self.downloadAndParsePool drain];
self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
}
}
It seems that the issue is caused by the fact that the drain is called outside of the method that allocated the pool even if on the same thread. Here is what NSAutoreleasePool Class Reference says.
You should always drain an autorelease
pool in the same context (invocation
of a method or function, or body of a
loop) that it was created.
The message you see is triggered by your call to drain in drainAutoreleasePool:
[self.downloadAndParsePool drain];
NOTE: XMLPerformance sample app uses the same pattern of periodically draining the pool. It also produces this message since iOS4.0
How To Actually Fix This
The accepted answer here is correct, but here are some more details on how to fix it because it's not a good idea to just live with issues that are filling up your console. A log statement is being printed and there is a reason for it. Something is awry.
Turns out this code is from Apple's own example project called XMLPerformance, found here: http://developer.apple.com/library/ios/#samplecode/XMLPerformance/Introduction/Intro.html
In the source file, LibXMLParser.m, simply change this line:
self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
to this:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
at the beginning of the - (void)downloadAndParse:(NSURL *)url method and then change:
[downloadAndParsePool release];
self.downloadAndParsePool = nil;
to this:
[pool release], pool = nil;
at the end of that method. Now remove the ivar called downloadAndParsePool. Once you've done all of that, the messages will go away.
Magically delicious.
It sounds like you've lost a pool somewhere. Pools are objects and its possible for them to be over released. They also stack in a fashion. IIRC, when you have nested pools, draining an outer pool automatically drains the inner pools. I think you've got a pool object that dies without being drained. When the outer pool tries to drain it, it never gets a response.
Just a guess.
It would be very helpful if you could show the code that does the creation of the pool, the drain of the pool and the re-creation of it. Otherwise we are just guessing at answers.
update
First, pools like that should be a local variable inside of the -main instead of an iVar like that. Second, how is your property defined? Is it an assign or a retain?
Update2
I would still recommend against using an iVar to store that pool. Nevertheless since it is an assign that should be fine.
When you drain the pool, are you also saving and resetting the NSManagedObjectContext associated with that operation? NSManagedObjectContext makes very heavy use of the autorelease pool and if you are not resetting it at the same time as the drain that could be causing an error.
Update3
Ok, then we are left with TechZen's suggestion that you are losing a pool somewhere. I would put log statements in after you create a pool and spit out its pointer address and put a log statement in just before the drain that also spits out the pointer address. Then match them up. If they match up then the leak is somewhere else.
If you create a pool with NSAutoReleasePool
So that it automatically releases
there is no need for release in dealloc
The app sometimes crashes if you use NSAutoReleasePool
and also release the pool in dealloc