I came across a problem that seems to be called "drowning" in autorelease pools.
My code creates and destroys objects correctly. However, I use some class methods that autorelease several variables without my knowing about it. Considering they loop thousands and thousands of times every minute... I find myself drowning in thousands of unreleased objects lost somewhere in the program.
2 questions:
So either I do not use class methods AT ALL - is that a permanent solution, or, even so, will some instance methods (Foundation, UIKit) still autorelease objects without my knowing about it?
or I can alloc an autorelease pool prior to calling class methods and draining it after calling the class methods - would this be a permanent solution?
Just because you don't use a class method doesn't mean you're going to avoid autoreleased objects. For example: NSString * path = [anotherString stringByAppendingPathComponent:#"foo"] returns an autoreleased object, yet no class methods were involved. As far as I know, the only way to avoid autoreleased objects is to not use objects. If you really want to go that route, check out CoreFoundation. (I do not recommend going that route.)
If you do have some crazy factory method that creates tons of autorelease pools, then I'd probably do something like this:
+ (id) crazyFactoryMethodWithFoo:(id)foo {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
id returnValue = nil;
//generate gobs of autoreleased objects
returnValue = [something retain];
[pool release];
return [returnValue autorelease];
}
By explicitly retaining the return value before draining the pool, you ensure that the return value will live beyond the destruction of the autorelease pool. You then balance that retain by autoreleasing the return value and returning it.
Dave DeLong has already answered your questions, but usually the only times when you fill the pool too high is when you're in a very tight loop. In this case, just create another autorelease pool for each iteration of the loop.
for (NSUInteger i = 0; i < 1000000000; i++)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// do the work
[pool release];
}
Objects are automatically added to the most recently created autorelease pool for the current thread. By creating one for each iteration of the loop, you can get rid of the unwanted objects quickly rather than letting them pile up in the run loop's pool.
Related
I tried something out in my code to see the effect on memory utilization. I wanted to find out if the line inside the loop was leaking. Running this loop took utilization up to 100MB and it didn't go back down again. Does this indicate a memory leak? If so why? (I'm using ARC)
for (i = 0; i < 10000000; i++)
{
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
(accounts is an array of AccountSummary objects which implements NSCopying like this: name city state phone are all NSStrings, isLocal is BOOL)
- (id)copyWithZone:(NSZone *)zone {
AccountSummary *newAccount = [[AccountSummary allocWithZone:zone] init];
newAccount.name = [self.name copyWithZone:zone];
newAccount.city = [self.city copyWithZone:zone];
newAccount.state = [self.state copyWithZone:zone];
newAccount.phone = [self.phone copyWithZone:zone];
newAccount.isLocal = self.isLocal;
return newAccount;
}
There's no leak here that I can see. There will, however, be quite a bit of peak memory usage.
The exact behaviour of things that should logically release memory varies. Quite often, instead of releasing the memory it's autorelased. (With MRR, there used to be a method called autorelease.) When you autorelease something, it isn't really released but is instead scheduled for release later, when your code is finished because it's returned to the main event loop.
If part of this is being autoreleased — and my guess is that the property assignment is autoreleasing, because autorelease is "safer" than hard releasing — that memory won't be deallocated until your next autoreleasepool flush. Code on the main thread has an autoreleasepool set up by the OS itself, so each time you return to the main event loop everything that's been autoreleased gets flushed out. Here, that probably means that all 10,000,000 copies are kept in memory until you return to the main event loop. Darn right that'll crash a real device. :)
(That's assuming you're on the main thread; if you're not, you may not even have an autorelasepool set up, which means you probably will get a leak. But I think you get warnings to console in this case, so you'd already have a hint about which way to go.)
You can reduce this peak memory usage by using #autoreleasepool:
for (i = 0; i < 10000000; i++) #autoreleasepool {
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
What will happen now is that the memory scheduled for release later in each iteration of the loop will actually be released each iteration of the loop. That should solve your immediate problem.
That said, i can't imagine why you're doing this except to check the behaviour. And if that's the case, this is unlikely your core problem.
Assuming your core problem is a leak, with ARC you're not really looking for leaks. You're looking for circular references. That means your answer likely lies elsewhere in your code. If you're sure it's self's accounts rather than dsl's accounts that are leaking, look for self being involved in a circular loop.
Also, keep in mind that calling copyWithZone: on a NSString will probably not copy the string. (There's no need to copy a read-only string, as the read-only string can't be changed. Both "copies" can be the same object.) So if you're leaking just strings, they could be associated with the original objects.
When creating lots of objects inside a loop, you should do that inside an auto release pool.
#autoreleasepool {
for (i = 0; i < 10000000; i++) {
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
}
or, more likely in the real world...
for (i = 0; i < 10000000; i++) {
#autoreleasepool {
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
}
Here we have some examples from About Memory Management
In the first example
- (NSString *) fullName {
NSString *string = [[[NSString alloc] initWithFormat:#"%# %#", self.firstName, self.lastName] autorelease];
return string;
}
In this example is how the above method is called
{
Person *aPerson = [[Person alloc] init];
NSString *name = aPerson.fullName;
[aPerson release];
}
So I assume that the *name is autoreleased after code flow reaches the closing curly braces.
Is that true?
And in general autorelease of an object, depends on the scope and lifetime of the variable which references that object.
Are there any criteria which manage the autorelease pool of objects in a Objective-C program?
Thanks.
Release of an autoreleased object takes place when the autorelease pool which the object has been pushed to by autorelease is released/drained explicitly, provided that the object's retain count at that moment is 0+ (that is, nobody else but the autorelease pool is retaining it).
An object is not getting autoreleased just because it has gone out of scope. In your example we can only say for sure that it won't get released before the closing curly brace, but as H2CO3 said, without the relevant source code we cannot predict when it is actually cleaned up. In Cocoa (Touch) apps, threads with runloops have a loop-level autorelease pool which they drain at the end of each runloop iteration. If your method is called from the runloop (e.g. as part of an event handler callback), autoreleased objects will be released shortly after the handler code returns; otherwise there is no such guarantee.
Note that the above holds for non-ARC environment; others may confirm whether it's still valid or not when using ARC.
While I realize one is a class method and the other an instance method, they have the exact same description in the Apple docs so I'm not quite understanding the different syntax/use cases.
Their functionality is equivalent, but the class method returns an autoreleased object. That is, it's probably implemented along the lines of:
+ (NSFetchRequest *)fetchRequestWithEntityName:(NSString *)entityName {
return [[[NSFetchRequest alloc] initWithEntityName:entityName] autorelease];
}
This is a fairly common pattern in Objective-C libraries known as Class Factory methods.
Based on what you said, if they really have the same use case, the only difference has to do with the garbage collection on objective-C, according to the memory management design Apple uses.
Whenever you call an init method over an object, you own it and you are responsible for releasing it when you no longer need the object.
When you call any other kind of method that returns an object, that object is added to a NSAutoreleasePool, and it is autoreleased when the pool gets drained.
You can get more insights here.
So, following Apple's way, if you don't want so save the object for further use, you can call fetchRequestWithEntityName and not worry about releasing the object at the end of the method call. If you want to save it as an instance variable, you call the initWithEntityName method . They can, of course be interchanged, but this approach follows apple guidelines in what comes to memory management.
-(void)myMethod {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj1 = [[Object alloc] initWithEntityName:...];
id obj2 = [Object fetchRequestWithEntityName:...];
//obj1 retain count is +1
//obj2 retain count is +0 (likely autoreleased)
[pool drain];
//obj1 still exists
//obj2 is no longer valid; may or may not have been deallocated
}
So basically, fetchRequestWithEntityName is achieved by:
+(id)fetchRequestWithEntityName:... {
return [[[self class] alloc] initWithEntityName:...] autorelease];
}
The main difference between those two API is as below:
fetchRequestWithEntityName will give you an autorelease object, so once the event loop is over it will be deallocated from the memory until you retain it.
But initWithEntityName will give you object which is to be released by you otherwise there will be memory leak.
after having spent few months in trying to master the syntax and rules, I am going deeper in memory management rules.
One thing I do not understand and causing me confusion is how one creates objects.
Based on what stated in apple memory management guide, the following is a valid approach:
– (NSArray *)sprockets {
NSArray *array = [NSArray arrayWithObjects:mainSprocket,auxiliarySprocket, nil];
return array;
}
because I am not causing any memory leaks.
The reason why is that it's not using alloc for creating array and therefore sprockets is not the owner.
However I am wondering now what's inside arrayWithObjects.
Because it happens that in my apps I often have factory for creating custom objects using something similiar to:
return [[MyObject alloc] initWithParameter:(id)params]; // possible leak
If I want to change with a static method like:
return [MyObject initWithParameter:(id)params];
what could be in initWithParameter for adhere to memory rules ? And what if MyObject extends some other object ? I also find out that method naming rules are important to properly advise programmer, what are this rule ?
Also could you point out a web link where this is explained (I am not yet good in finding docs on apple web site).
thanks
This is the page you're looking for: Memory Management Rules. It all comes down to adhering to the rules of ownership for an object.
If you create an object using alloc then you own that object and must release it. For example:
NSString* str = [[NSString alloc] init];
//do something with your str object
[str release];
str = nil; //don't forget to set it to nil, it's still
//pointing to the (now unallocated) block of memory
If you create an object using a factory method, for example:
NSString* str = [NSString stringWithString:#"blah"];
Then what is happening here is that it is creating the string object using alloc for you and then returning the object but first adding it to the local autorelease pool. To use the example in your question.
return [[MyObject alloc] initWithParameter:(id)params]; // possible leak
return [[[MyObject alloc] initWithParameter:(id)params] autorelease]; //now autoreleased -- no leak
Here is the page on Autorelease Pools. Apple say it better than me.
An autorelease pool is an instance of NSAutoreleasePool that “contains” other objects that have received an autorelease message; when the autorelease pool is deallocated it sends a release message to each of those objects. An object can be put into an autorelease pool several times, and receives a release message for each time it was put into the pool. Thus, sending autorelease instead of release to an object extends the lifetime of that object at least until the pool itself is released (the object may survive longer if it is retained in the interim).
Method naming rules are important as it gives a strong indication as to whether the method returns an autoreleased object. In this example I would name the method:
[MyObject myObjectWithParameter:(id)param]
Hope this has helped.
It is standard practice for alloc and creation methods to return a retained object. Anything that starts with init, create, or the object name will clue other programmers into the purpose of the method.
Even if you create with alloc, if the purpose of your method is to pass on ownership, you should not release the object.
I'm just starting out (reading up a lot for the past couple of days). Here's some questions that I have stacked up, hopefully someone can answer them.
1. the (self != nil) check in initializer code. Why do it? To prevent accidental access to some "run-only-once" code that's wrapped up in there? Where from could this accidental access come from? Doing such checks suggest that I don't have control over what's going on.
- (id)init {
self = [super init]
if (self != nil) {
// Code..
}
return self;
}
2. How is it that you don't have to free up anything that static methods return? (or this is the idea I've got)
3. How is str = #"Hi there!" different from
str = [[NSString alloc] initWithString:#"Hi there!"];
As I understand, you have to release str in aquired with second method, but not with first? If so, when does the first one get released? Which one is preferable (not minding the typing length)?
4. What is autorelease, if iphone has no garbage collection? I've noticed something called "an autorelease pool" being created in main.m. Is [myObject autorelease]; a way of adding myObject to the nearest wrapping "autorelease pool", which will release it? Basically, some magic to avoid releasing it yourself? Why use it?
Well, thats it for now. Thanks for any answers!
In Objective-C, it's possible to return an instance other than self from -init. Classes do this, for example, to enforce a singleton instance, or in the case of class clusters. NSNumber, for example, returns a subclass depending on the type of value passed to its initializer. So when you call [[NSNumber alloc] initWithLong:long_value], NSNumber's -initWithLong: initializer is called after NSNumber's +alloc, but a subclass of NSNumber may be returned to the oringial caller. Thus the pattern
self = [super init];
which reassigns self to the value of [super init] so that self points to the actual instance that [super init] returned. If +alloc or the super's init method fails, the result of [super init] may be nil. To avoid, side effects in the case of a failed initialization, the pattern then becomes
- (id) init {
if(self = [super init]) {
// do initialization of instance variables etc.
}
return self;
}
Note that you must return self (or nil or an other instance) from the init method. You should assign self to [super init] and you may check for nil before doing more work.
You may have to release the return value of a staic method. You should read the Cocoa memory management guide. The rule is generally quite simple: If the method you call has "new", "alloc", or "copy" in its signature, the result belongs to the caller and the caller must call -release on that instance or there will be a memory leak. Of course you should call -retain on anything else (i.e. not from an "alloc","new" or "copy" method) you want to keep a reference to and then call -release or -autorelease when you are done with that instance.
str = #"Hi there!", assuming str was declared as NSString *str; assigns the address of the string constant #"Hi there!" to the value of thestrvariable. You do not need to retain or release string constants.str = [[NSString alloc] initWithString:#"Hi there!"];allocates a new string instance. The value ofstrwill be the address of this instance. Each call ofstr = [[NSString alloc] initWithString:#"Hi there!"];again will allocate a new instance. So afterstr2 = [[NSString alloc] initWithString:#"Hi there!"];,str != str2, while afterstr2 = #"Hi There!", str==str2. See this answer as well.
-autorelease adds the receiver to the current NSAutoreleasPool. When the pool is drained (usually at the end of the current run loop iteration, or when the pool is manually drained), the pool calls -release on all instances in the pool. If this -release drops the retain count to 0, the object is deallocated (and -dealloc called) just as with any other -release. Using an autorelease pool is generally frowned upon on the iPhone because it may cause you to accumulate many unused instances in the pool before it is drained at the end of the run loop iteration. If you can use -release instead of -autorelease, you generally should. Again, see the Cocoa memory management guide for more info.
There is a school of thought that in most cases, allocating the self pointer is something that the system should do, and not the programmer.
Also, many people prefer to keep the main line of program flow as un-indented as possible. In which case the initialisation code could be re-written as:
- (id)init {
if (![super init]) {
return nil; // There is a problem so bail early.
}
// Initialisation code here.
return self
}
Will Shipley explains this much better than I do.
1: This check is to ensure that the super constructor returned a new object.
2: Static methods don't refer to an instance
3:
str = #"Hi there!"
This assigns the address of the constant string "Hi there!" to the pointer str
str = [[NSString alloc] initWithString:#"Hi there!"];
This allocates a string and copies "Hi There!" to it. This means that a) str is modifiable and b) needs to be deallocated when you are done with it.
calling
self = [super init];
May return nil if the superclass cannot initialize itself for some reason or other, including memory being unavailable, or certain prerequisites have not been met. If that is the case, you don't want to be trying to set variables of self, or set self as a delegate, or add self to an array, if self is nil.
The autorelease pool is something created upon every event the iPhone sends your application. It is created before any code runs, and released after all your code is done, for each event. Any objects that you call autorelease on will be put into the current autorelease pool. Any objects in the autorelease pool will be released as many times as they were added, after your code completes. In this way, you don't have to worry about who's responsible for releasing an object created by one method and returned to another method.
You can create your own autorelease pools as necessary.
str = [[NSString alloc] initWithString:#"Hi there!"];
This line creates a string that is not in an autorelease pool, so you have to release it manually. Simply writing
#"Hi there!";
returns a string that you don't have to worry about releasing. Extending your previous example:
str = [[[NSString alloc] initWithString:#"Hi there!"] autorelease];
would be another method of creating a string you don't need to worry about releasing.
One difference between garbage collection and autorelease pools is that garbage collection works with circular references. Using autorelease pools, you don't want to have two objects that retain each other and hope that once nothing else refers to them, they will go out of existence; they won't.
If self is nil after the super initialisation then you're probably out of memory. Your only reasonable course of action is to return nil and hope things get handled gracefully further up the stack.
Static methods aren't allowed allocate on the heap, therefore there's nothing to free.
In the first instance, the string is compiled into the data segment of your app and cannot be freed. In the second instance, you are allocating memory from the heap and copying your static string (from the data segment) into it.
It's simple garbage collection. As to why to use it, the simple answer is don't. It's not recommended to use autorelease on the iPhone due to limited resources.