This is a question related to Objective-C memory management.
On the About Memory Management page there are some examples
- (NSString *)fullName {
NSString *string = [[[NSString alloc] initWithFormat:#"%# %#",
self.firstName, self.lastName] autorelease];
return string;
}
and the second one
- (NSString *)fullName {
NSString *string = [NSString stringWithFormat:#"%# %#",
self.firstName, self.lastName];
return string;
}
The only difference is that in the first example an initializer is called, and in the second a class factory method.
Basic memory management rules section is said that after an alloc call I will own the object I allocated. So in the first example I allocate an object and at the same time initialize it. In this I own the object and have to release it. In the second example I don’t. But doesn’t the factory method stringWithFormat: do the same thing in one call, I mean allocating the object and initializing it?
So the main question is, why don’t I have to release the object in the second example?
are there any special memory management rules when implementing a class factory method?
By convention, a class factory method will return an object that is in the autorelease pool. It has done the alloc, init, autorelease for you, just like in the first example. Unless you retain it, when the pool is drained, it will be released.
In the second example, you didn't create the string. You only create objects by invoking methods that begin with alloc, new, copy, or mutableCopy.
You may take ownership, if you like, by invoking retain, but then you're also responsible for invoking release. In this example, there's no need. You don't need the string any more. What the caller does with it is their responsibility.
When invoking a factory method like this, you typically receive an autoreleased object. But you don't really need to think about that. Because you didn't create it (because you didn't use a method beginning with alloc, new, copy, or mutableCopy to get that string), all you need to do is think about the retain count delta -- do you need to retain it to keep it from disappearing from underneath you. Since it's just being returned to the caller, the answer in this example is no.
You are right, the factory method internally calls the alloc, and thus returns retained object, but it adds that object to the nearest autorelease pool, so when that pool will be drained, the object will be released.
Related
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.
Scenario- I have a method that returns an object retrieved from an NSMutableArray similar to the following code which has been simplified (assume that "myArray" and "currentIndex" are iVars for the class wherein this method resides):
- (MyObject *)getFromArray
{
return [myArray objectAtIndex:currentIndex];
}
Should I be tacking on a call to autorelease like so?
- (MyObject *)getFromArray
{
return [[myArray objectAtIndex:currentIndex] autorelease];
}
I know that autorelease is called on the object by the array as it returns said object, but my understanding is that the autoreleased object is only valid for the duration of the caller's scope, in this case, the method above. So, would tacking on the autorelease (as shown above in the 2nd code snippet) be the correct way to make sure the object in question is valid for the caller of my method? Just trying to make sure that my reasoning and understanding of how autorelease works is correct.
Thanks!
Should I be tacking on a call to autorelease?
No! Always follow the simple memory management rules: if you don't own it (by alloc, new, copy, or retain), you must not (auto)release it.
I know that autorelease is called on the object by the array as it returns said object
That assumption is wrong. The array just returns the object, it doesn't release it. (It might actually call [[... retain] autorelease] on the object but that's another matter.) The array will only release the object when you remove the object from the array or when the array itself gets deallocated.
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 am just curious, do I need to add a further [name release] elsewhere to match up with the retain here in the getter?
- (NSString *)name {
return [[name retain] autorelease];
}
gary
No, but you shouldn't need to do this at all since you are not allocating anything. You could simply return name and that should be fine. Was there a reason you needed to add this retain/autorelease?
A little more explanation, what is happening here is that your retain count goes up by one when you do a retain, and then down by 1 when the scope exists because of the autorelease.
I don't know how your variable definition is in your class but the rule is that in your getter you should return the object unchanged for the reference count. It's the responsability of the caller to call retain if it want to keep a reference on it.
- (NSString*) name {
return name;
}
// caller
NSString* name = object.name;
[name retain]; // if necessary. If the string is used only in the context of a method you do not have to retain it.
If you are using the returned value as a field in another class you should define your field like this:
#property(retain, nonatomic) NSString* name;
With this a retain will be called when you assign to the variable.
No, this is fine. autorelease will cause the value to be released when the current autorelease pool is drained.
Every retain must be matched with exactly 1 of either release or autorelease.
However, I believe both the retain and autorelease are unneeded here. Generally you want to use that autorelease idiom because you've alloc'ed something in the method.
No. The autorelease will balance it out. I don't think, however, that the retain and autorelease would be necessary. You can simply use return name.
As others have said, you do not need to retain or autorelease the property. Since callers of the 'getter' method did not create the object, they do not own it, and you are safe to assume that they won't tinker around with its retain count.
But, callers could potentially change the value of the variable returned by the getter, which would affect the object. Therefore, it would probably be a better idea to return a copy of your variable, especially since it is an NSString. (Getters for NSString objects often return a copy.)
- (NSString *)name {
return [[name copy] autorelease];
}
In this scenario, you are creating a copy of the variable, so you 'own' it. By autoreleasing it before it is returned, you ensure that it will survive long enough to be used in the caller's scope, and that any changes they make to the 'name' variable will not affect the underlying object.
I am just curious, do I need to add a further [name release] elsewhere to match up with the retain here in the getter?
- (NSString *)name {
return [[name retain] autorelease];
}
No, because you are already releasing it. autorelease just means “send yourself release later”.
I think you should review the memory-management rules.
I think I might have figured it out:
if [myString] is created outside the method then your safe to use ...
return myString;
if on the other hand [myString] is created inside the method and therefore needs to be released and returned, then you use.
myString = [[NSString alloc] initWithFormat: #"Send me home"];
return [myString autorelease];
This way the method sets [myString] to autorelease, Basically the object is created, set to autorelease and returned. The object will ultimately be released when the pool is destroyed.
I understand that any init... method initializes a new object and that NSString stringWithString makes a copy of the parameter string as a new object. I also understand that being the objects' owner, I can control the release/deallocation of any objects that I allocate. What I don't understand is when would I use the stringWithString method since any local variable assigned that way would have it's memory "owned" by NSString instead of the local class.
The "Programming in Objective C" book by Kochan (1st ed) uses the following code (see pages 342-344) to explain that the initWithString is preferable to stringWithString because the AddressCard class would own the name variable contents. Also, I don't get any errors making repeated calls to the setName version with the stringWithString method. TIA!!
//header file has appropriate declarations but not included here:
#import "AddressCard.h"
#implementation AddressCard;
-(NSString *) name
{
return name;
}
//Recommended code:
-(void) setName: (NSString *) theName
{
[name release]
name = [[NSString alloc] initWthString: theName];
}
//Incorrect code according to Kochan:
-(void) setName: (NSString *) theName
{
[name release]
name = [NSString stringWthString: theName];
}
//rest of class implementation code snipped
#end
What I don't understand is when would I use the stringWithString method since any local variable assigned that way would have it's memory "owned" by NSString instead of the local class.
What? No.
The rules are simple:
Any object returned by alloc, copy, copyWithZone, or new has a retain count of 1.
retain increases the receiving object's retain count.
release decreases the receiving object's retain count.
autorelease tells the current autorelease pool to send the receiving object the release message “later”.
Any factory method that doesn't have “new” or “copy” in the name (e.g., stringWithString:) returns an object that it has autoreleased on your behalf.
Or, digested a bit:
Any method whose name contains copy, alloc, retain, or new returns an object that you own.
Any method that doesn't, returns an object that you don't own.
To own an object, retain it.
The incorrect implementation of setName: that you show is incorrect because it stores an autoreleased object in an instance variable, when you mean to own the object. You should retain it or, in this case, copy it. One way is to simply use alloc and initWithString:, as in the correct example you show; the other way would be copy.
The Memory Management Programming Guide for Cocoa explains everything. Every Cocoa or Cocoa Touch programmer should read or re-read it from time to time.
Actually, both setters are wrong. The 'incorrect' one is wrong for general memory management reasons (which are well-expounded elsewhere). The 'recommended' one is wrong for 2 reasons:
if (theName == name), then you're
likely to deallocate your object in
the first line, and then attempt to
use the deallocated object as a
parameter to -initWithString: on the
second line, resulting in undefined
behavior.
-initWithString: does not handle being passed nil gracefully.
The 'correct' (IMHO) method is:
-(void) setName: (NSString *) theName
{
if (theName == name) return; // if they're equal, no need to do anything further
[name release];
name = [theName copy]; // sets name to nil if theName is nil
}
For most objects you'll actually want to -retain instead of -copy on that third line, but for strings it's almost always better to copy.
The difference between initWithString and stringWithString is that stringWithString returns an auto-released pointer. This means that you don't need to release it specifically, since that will be taken care of next time that the auto-release pool cleans up any auto-released pointers.
initWithString, on the other hand, returns a pointer with a retain count of 1 - you do need to call release on that pointer, or else it would result in a memory leak.
See https://stackoverflow.com/questions/193288/what-is-the-cost-of-using-autorelease-in-cocoa for some reasons as why you should use auto-release vs release.
In the Incorrect code above, the next time name is referenced after setName is called, you'll get an exception error, since the object will have been released. You can use either the "Correct" code, or wrap your stringWithString call in an explicit retain call:
name = [[NSString stringWithString: theName] retain];
What I don't understand is when would I use the stringWithString method since any local variable assigned that way would have it's memory "owned" by NSString instead of the local class.
A string created with stringWithString: isn't owned by the NSString, it is owned by the NSAutoreleasePool (although multiple places can retain an object, making ownership shared).
With stringWithString:, the string will become invalid when the autorelease pool is next processed (normally during the application's next event loop) because the NSAutoreleasePool will release its pointer. If you have not retained the string before then, any pointer you have to it (name in the case of your class) will be invalid (the variable name will still exist but it will point to garbage).
Autorelease is good, if you don't intend to keep any pointers to the NSString but since you do intend to keep a pointer, you'll need to retain the NSString. initWithString: gives you a retain count of 1 automatically.