Retain/Release through intermediary method - objective-c

I think I understand retain/release in objective-C for the most part. However, I have a specific case I am unsure about. Here is an example:
+ (NSString *)getPlayerNameByIndex:(NSInteger)globalIndex:(ABAddressBookRef)addressBook
{
...
Player *player = [PlayerHelper loadPlayer:globalIndex];
NSString *name = [PlayerHelper getPlayerName:player :addressBook];
[player release];
// 'retain' here?
return name;
}
+ (NSString *)getPlayerName:(Player *)player:(ABAddressBookRef)addressBook
{
...
NSString *name = [[[NSString alloc] initWithString:player.nickname] autorelease];
return name;
}
So then I call...
NSString *name = [PlayerHelper getPlayerNameByIndex:index:addressBook];
// name is 'autorelease'?
What I saw on random occasions is that the view sometimes shows the 'name' field as empty when it populates the table after coming back from another view. This could be another issue but I want to be sure of my use of 'autorelease'.
The core of my question is the use of 'autorelease' in getPlayerName. Does the 'autorelease' state of being get passed through method getPlayerNameByIndex to the caller?
Or, do I have to call 'retain' in the intermediary method? I am thinking 'autorelease' may be releasing in method getPlayerNameByIndex.
Hopefully my question is clear. Any help is appreciated.
Update: Some more info for clarification...
NSError *error = nil;
Player *player = nil;
NSArray *array = [appDelegate.managedObjectContext executeFetchRequest:request error:&error];
if ([array count] == 1)
{
player = [array objectAtIndex:0];
[player retain];
}
This is essentially the "loadPlayer" method which loads info from core data. From the answers it sounds like I do not need to call [player retain], since it is an autorelated object, and I can simply return "player" and use it? Thanks for the responses!

The core of my question is the use of 'autorelease' in getPlayerName. Does the 'autorelease' state of being get passed through method getPlayerNameByIndex to the caller?
The answer is yes.
Or, do I have to call 'retain' in the intermediary method?
whether you want to call retain depends on the semantics of your method.
In Obj-C/Cocoa, the following convention applies: a method whose name begins with “alloc” or “new” or contains “copy” will return a retained object; otherwise you can expect to get an autoreleased object, then it is the caller responsibility to retain it according to its needs.
I am thinking 'autorelease' may be releasing in method getPlayerNameByIndex.
autoreleased objects are released at the next point in time when the autorelease pool is drained; this is usually associated to going back to the main loop (though, no details are available about this); so you can be pretty sure that auto-releasing does not kick in in getPlayerNameByIndex...
Hope this helps clarifying the issue...

In getPlayerNameByIndex The line:
[player release];
is wong, remove it. You did not obtain ownership. Ownership is ob gained by calling a method with alloc or the method names starts with new, copy or an explicit retain. (NARC).
You do not need to release player because you did not obtain ownership, see above rule.
In getPlayerName:
can be simplified to:
return player.nickname;
The method name can be simplifies to:
+ (NSString *)getPlayerName:(Player *)player

Related

ARC autoreleases too early (?)

I have a method call in class A:
GDataXMLElement *infoElement = [self getElementFromFilePath:filePath];
NSString *testStringA = [infoElement attributeForName:#"someAttribute"].stringValue;
and the method implementation in class B:
-(GDataXMLElement*)getElementFromFilePath:(NSString*)filePath {
NSData *xmlData = [NSData dataWithContentsOfFile:filePath];
GDataXMLDocument *infoXMLDoc = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:nil];
NSArray *infoArray = [infoXMLDoc.rootElement elementsForName:#"test"];
GDataXMLElement *returnElement = (GDataXMLElement*)infoArray[0];
NSString *testStringB = [returnElement attributeForName:#"someAttribute"].stringValue;
return returnElement;
}
The returnElement at the end of the method in class B is perfectly initialized, and testStringB string contains the correct value.
But in Class A, the contents of InfoElement are gone, and testStringA is nil.
I suspect that ARC is releasing GDataXMLDocument too early, and was able to stop this behaviour by tying the document to a property in class B:
#property (nonatomic,strong) GDataXMLDocument *infoXMLDoc;
But I am a little unsatisfied with this solution. I'll never use that property again, I just need the element to parse it one time only. If it is possible to stop the release with a property, is there also a way to do this within the method? I tried the __strong qualifier like this:
GDataXMLDocument __strong *infoXMLDoc = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:&error];
but that didn't help. So my questions, assuming ARC is indeed responsible:
1. Is is possible to tell ARC within a method to not release an object?
2. What did I not understand about the behaviour here? I am using ARC for some time now, this is the first time that I am stuck.
The GDataXMLNode.h header says:
it is up to the code that created a document to retain it for as long
as any references rely on nodes inside that document tree.
The node you return from getElementFromFilePath depends on the parent (the GDataXMLDocument), but that is going to be released by ARC. You must retain the GDataXMLDocument somewhere for as long as you reference nodes and elements inside it. These are the semantics of the GDataXML* classes and you must follow them.
I can't compile so this's just an educated guessing, but I suspect the problem is that you return a pointer to an object that is allocated and released inside the method:
GDataXMLElement *returnElement = (GDataXMLElement*)infoArray[0];
As you see you don't alloc returnElement, so ARC have no way to understand that you need it. It simply release infoArray when you exit from the method.
If you copy the value (something like [(GDataXMLElement*)infoArray[0] copy] ) it should works.

Potential memory leak in NSData category

When using the XCode analyzer I get a message saying:
Potential leak of an object allocated
The code this is in my NSData(String) category, the code is:
- (NSString*) utf8String
{
return [[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding];
}
Now how can I solve this? When I change the statement to:
- (NSString*) utf8String
{
return [[[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding] autorelease];
}
My application crashes on the line where I call utf8String.
The cocoa naming conventions suggest that all methods return autoreleased objects, with the exception of methods whose names start with 'init', 'copy' or 'new'. The static analyzer knows and checks this.
You have two choices. You can rename the method to -newUTF8String, or you can return an autorelease object and retain it when you want to store the return value of this method.
I would prefer the latter, but both would be valid code.
I guess your application crashes because the variable is released before it is used. It is recommended to call retain if you do not use the return value right away but store it in a member variable.
...
myMemberVariable = [something utf8String];
[myMemberVariable retain];
...
To make sure that you do not produce a memory leak you have to release the member variable somewhere. A good place for that would be dealloc.
- (void)dealloc {
if (myMemberVariable) [myMemberVariable release];
[super dealloc];
}
I would also recommend having a look at Advanced Memory Management Programming Guide to get some detailed information about memory management of iOS.

Can I reuse my pointer after it's been added to a mutable array?

Let's say I've got an array with strings.
NSArray *names = [NSArray arrayWithObjects: #"One", #"Two", #"Three", nil];
What I want is to initiate objects of some custom class and them add them to a mutable array. I'm using a custom init method that takes a string argument.
To be more specific, I want to [SomeClass alloc] initWithName: aName] and add the resulting object to a NSMutableArray.
I'm thinking of using Objective-C fast enumeration. So what I get is:
NSMutableArray *objects = [NSMutableArray arrayWithCapacity: [names count];
for (NSString *name in names) {
[objects addObject: [[[SomeClass alloc] initWithName: name] autorelease]];
}
The problem is that I can't add nil to the array and I don't like exception handling. However, my initiation method may return nil. So I decide to check first before adding (prevention). My new for-in-loop is:
SomeClass *someObject;
for (NSString *name in names) {
someObject = [[[SomeClass alloc] initWithName: name] autorelease];
if (someObject) {
[objects addObject: someObject];
}
}
Now, instead of immediately passing the new object to the array, I'm setting up a pointer someObject first and then passing the pointer to the array instead.
This example raises a question to me. When I someObject = [[[SomeClass alloc] initWithName: name] autorelease] in the loop, do the existing objects (which are added using the same pointer) in the array change too?
To put it in other words: does the addObject: (id)someObject method make a new internal copy of the pointer I pass or do I have to create a copy of the pointer — I don't know how — and pass the copy myself?
Thanks a lot! :-)
It's fine to reuse someObject; if you think about it, you're already reusing name each time you go through the loop.
-addObject: may or may not copy the object that you pass in. (It doesn't -- it retains the object rather than copying it, but it's conceivable that some NSMutableArray subclass could copy instead.) The important thing is that this code really shouldn't care about what -addObject: does.
Also, don't lose sight of the distinction between a pointer and the object that it points to. Pointers are just references, and a pointer is copied each time you pass it into a method or function. (Like C, Objective-C passes parameters by value, so passing a pointer into a method results in putting the value of the pointer on the stack.) The object itself isn't copied, however.
Short answer: no, you don't have to worry about reusing someObject.
Slightly longer answer: the assignment—someObject = ... assigns a new pointer value to the someObject variable; addObject: is then getting that value, not the address of someObject itself.
I think you're getting confused in the concept of pointer here. When you say someObject = [[[SomeClass alloc] init... you are basically pointing the someObject pointer to a new object. So to answer your question- your current code is fine.
As for whether arrays maintain copies of the objects added to them - NO, the array retains the object you add to it. However, that doesn't matter to your code above.
Three20 provides the answer!

big memory problem in objective c

i've a function like this:
#property(nonatomic,retain) NSMutableArray *array;
#synthesize array = _array;
(NSMutableArray *) name
{
self.array = [[NSMutableArray alloc]init];
[_array addObject:object];
[object release];
return [_array autorelase];
}
In the other function i've a property like the property above, named result, and i make:
self.result = [... name];
Then in dealloc i make
[_result release];
and it crashes in this point, how can i solve this?
I've tried many roads, but or it crashes, or i see memory leak in Instruments, where am i wronging?
Thanks.
While there's a lot wrong with this code, the likely cause of your crash is that you're releasing object within -name without taking ownership of it- unless you're creating object within the method through a call to -alloc, -new, or -copy, that method doesn't own it and isn't responsible for releasing it. This is causing that object to be invalid within the NSMutableArray, so when _result releases, it attempts to release an invalid piece of memory and crashes.
Also, properties aren't simply local variables for individual functions, they're member variables for instances of the class for which you're writing these classes. If your end goal is only to return an autoreleased array and set it to result you could do the following:
- (NSMutableArray *) name {
//call a convenience method- it comes back autoreleased
NSMutableArray* theArray = [NSMutableArray array];
[theArray addObject:object];
//don't release object unless you took ownership of it in this function
return theArray;
}
then outside the function, either call self.result = [... name] or [self setResult:[... name]];
You have a very strange method definition (the header should have a - before the return type), and inside that definition you are accessing a variable called object that doesn't seem to exist. I'm not sure what you want, but you've got at least one memory problem. The array that you create in name gets leaked every time the method is called. If you add some details, like the crash message, someone may be able to help more.

Releasing objects returned by method

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