Ok, I know the answer to this question should be obvious, but I need a little push in the right direction.
I find myself writing a fair number of methods that follow the following pattern:
-(NSThing*)myMethod{
NSThing *thing = [[NSthing alloc] init];
// do some stuff with the thing
return thing;
}
My question is, how do I handle the release of this object? Clearly I can't release it within the method.
usually you would autorelease it
-(NSThing*)myMethod{
NSThing *thing = [[NSthing alloc] init];
// do some stuff with the thing
return [thing autorelease];
}
Autoreleasing is the easy way to get out of this, as newacct said. However, you should take into consideration the "ownership" of the object you're returning.
In Objective-C, the general rule of thumb is that any method with alloc, new, or copy in its name returns an object that is not autoreleased, while other methods (like class methods) pre-autorelease the object to be returned. So these three are not equivalent in terms of autorelease (although the compiler may do some magic and reduce them all to string constants anyway):
// Autoreleased
NSString *string1 = [NSString stringWithString:#"aString"];
NSString *string2 = [[[NSString alloc] initWithString:#"aString"] autorelease];
// NOT autoreleased
NSString *string3 = [[NSString alloc] initWithString:#"aString"];
Your code can take a similar approach, where you consider who owns the object you're returning. In the specific example you provided, the method is the one allocing and initing the object, so in general you're responsible for autoreleaseing [sic] it within your own method. However, if you were to write a method that takes a preexisting object and modifies it in some way, you would not own that object and would not be responsible for autoreleasing the returned object. (Doing so could actually cause problems down the road when the autorelease pool to which the object belongs gets drained.)
See also (thanks to Peter and Quinn in the comments!):
Memory Management Rules
Related
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.
Using ARC, is it now OK to assign a string value like this:
self.userName = [[NSString alloc] initWithString:self.currentParsedCharacterData];
Or does that still cause a memory leak, thus requiring me to assign it like this:
NSString *tmpString = [[NSString alloc] initWithString:self.currentParsedCharacterData];
self.userName = tmpString;
The first snippet is just fine under ARC, and is the better of the two ways. Apple has an example like this in the Transitioning to ARC guide:
- (void)contrived {
Person *aPerson = [[Person alloc] init];
[aPerson setFirstName:#"William"];
[aPerson setLastName:#"Dudney"];
[aPerson setYearOfBirth:[[NSNumber alloc] initWithInteger:2011]];
NSLog(#"aPerson: %#", aPerson);
}
and says:
ARC takes care of memory management so that neither the Person nor the NSNumber objects are leaked.
The setYearOfBirth: message with an alloced NSNumber corresponds to your snippet.
The compiler understands that, in the first case, aPerson is going out of scope and needs to be released before that happens, and, in the second case, that there is no explicit reference to the NSNumber object and that it must be either released or put into the autorelease pool. It takes care of both these requirements on your behalf.
Yes, with ARC it is ok. In fact, I would recommend the first way you have it implemented.
Either of your examples is fine. There is no difference between the two as far as memory management/ARC goes.
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.
could the following code lead to problems?
- (void) method1 {
NSMutableArray *myArray = [[[NSMutableArray alloc] init] autorelease];
... fill the array
[someObject someMethod:myArray]; // someObject does work on that array (without retain!)
}
A sometimes-appearing crash in my app looks like it IS a problem; but I would not understand that... shouldn't myArray stay alive at least until the end of method1?
Thanks a lot for your help!
So my questions besides "can that be a problem" are:
- would it be enough to remove autorelease and make a release at the end of the method?
- if not: do I have to make a retain/release in "someMethod"?
EDIT: but this can be a problem, am I right?
- (void) method1 {
NSMutableArray *myArray = [self getArray];
... fill the array
[someObject someMethod:myArray]; // someObject does work on that array (without retain!)
}
- (NSMutableArray) method2 {
return [[[NSMutableArray alloc] init] autorelease];
}
I'm going to assume for a moment that you meant this to be NSMutableArray rather than NSArray. If you really mean NSArray here, then it is all impossible. You can't "fill the array" on an immutable array.
Yes, this should work. Your problem is likely elsewhere. A common mistake here would be over-releasing something you put into myArray.
Looks like you're missing a couple of open brackets on the line where you allocate your array. Should be [[[NSMutableArray alloc] init] autorelease];
Your code is completely correct. Do not listen to people telling you to remove the autorelease and manually release the array after the call to someMethod:.
In 99% of cases using an autorelease'd object has absolutely no negative performance impact on your application. The only time you want to worry about it is in loops.
The [[[Foo alloc] init] autorelease] pattern is just the same as using a built-in helper method like [NSString stringWithFormat:...]. They both return an autoreleased object, but you probably don't worry about performance with the latter. Again, only worry about autorelease in large loops, or when Instruments tells you you have a problem.
Using the [[[Foo alloc] init] autorelease] style also keeps all the memory management on a single line. If you get used to typing [[[ you won't forget the release. If you are copying and pasting code, or moving code around, you won't accidentally lose the corresponding release because it's all on the same line.
It's also easier to review code that uses the [[[Foo alloc] init] autorelease] style because you don't have to go around hunting for the release further down the method to make sure the memory management is correct.
So in terms of readability and safety and correctness, your code is absolutely fine and good. This applies to both your original snippet and the follow up you added below. Performance of autorelease only becomes an issue when you have large loops.
Your crashing issue must be caused by some other factor that is not evident in the code you posted.
I also suggest you read the Memory Management Programming Guideline if you have not already. Basically, and autorelease'd object is guaranteed to remain valid until the enclosing pool is released. In your case, the autorelease pool exists higher up in the call stack (probably the main runloop) so you can be safe in the knowledge that your autorelease'd array will remain valid for the duration of any calls you make.
One last point. Your array allocation code could also make use of the array helper constructor:
NSMutableArray *myArray = [NSMutableArray array];
This is even simpler, cleaner and shorter than your original.
Best thing to do will be something like that:
- (void) method1 {
NSMutableArray *myArray = [[NSMutableArray alloc] init];
... fill the array
[someObject someMethod:myArray]; // someObject does work on that array (without retain!)
[myArray release];
}
and it's best from two different angles. One way you are holding your array till you have no need for it, so it will no wipe from memory until you say so. Second is the same sentence, only the reason is that you will clean up memory from no more needed objects as soon as it could be done.The last may need some more explanation... Autoreleased objects should be autoreleased as soon as you have no need for it, but they are released on two basic rules: at the end of block(not always) or at the memory pressure after end of the block(yes always). In other words NSAutoreleasePool doesn't always release stuff at the end of some block of the code, and it's not even close to be rare that it delays those releases to later point.Anyway you should always check for over-releasing your object as it will lead to crash when you'll try to reach such object at your code.
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.