ARC autoreleases too early (?) - objective-c

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.

Related

Difference between Strong and Weak references (Using ARC) Please No Theory..I know the difference theoretically

I have been trying to understand the difference between Strong and Weak references in iOS. What I did to understand is:
//.h File
#property(nonatomic,strong) NSString* myStrongString;
#property(nonatomic,weak) NSString* myWeakString;
//.m File
- (void)viewDidLoad
{
[super viewDidLoad];
[self assignTempString];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)assignTempString{
self.myStrongString = [[NSString alloc] initWithString:#"Varun Mehta"];
}
- (IBAction)printAssignedString:(id)sender {
NSLog(#"Object will have strong reference so it will print my name==%#",self.myStrongString);
}
According to my understanding when I repeat the above step by using myWeakString it should print null. But its still printing my name. Anybody having any idea why its happening.
But when I replace [[NSString alloc] initWithString:#"Varun Mehta"] with [NSString stringWithFormat:#"Varun Mehta"] or [[NSString alloc] initWithFormat:#"Varun Mehta"] result is coming as I have expected.
There are several things to consider here.
A statically declared string is built into your app so it isn't really retained or released, thus a weak reference to #"my string" will always be valid. The compiler is just recognizing [[NSString alloc] initWithString:#"Varun Mehta"] as a static string and removing your alloc/init. However anything that deals with formatting is, by definition, creating a new string and thus the new string obeys the weak referencing rules and is immediately deallocated, nil-ing out the reference.
If you access a weakly retained object that ends up in the autorelease pool it won't actually get deallocated until all your methods return and the run loop goes back into another cycle (and thus drains the autorelease pool), so you can continue to work with the object even though it is "walking dead". This is typically only when interacting with non-ARC code.
If you need practise try this code:
- (void)viewDidLoad
{
[super viewDidLoad];
[self assignTempString];
}
-(void)assignTempString{
#autoreleasepool
{
self.myStrongString = [NSString stringWithFormat:#"%#", #"Strong string"];
self.myWeakString = [NSString stringWithFormat:#"%#", #"Weak string"];
}
}
- (IBAction)printAssignedString:(id)sender {
NSLog(#"Strong ptr content: %#",self.myStrongString);
NSLog(#"Weak ptr content: %#",self.myWeakString);
}
[NSString alloc] will allocate an ARC-managed object and will set its retain count to 1. As long as your view controller is alive, this retain count will be 1, so it will not be deallocated. [NSString stringWithFormat:] returns an autoreleased string which is deallocated after the execution of [self assignTempString].
Two methods initWithString and stringWithFormat suggest exactly what is to expect.
So initWithString expects you to create allocate memory and then initialise it.
While stringWithFormat expects you to just point to the string.
When you do a init with your strong/weak variable it will exist till end of your program.
While when you point;
strong literal will keep a reference and hence will not allow ARC to cleanup the string literal,
weak literal will not keep a reference and hence ARC is free to clean it up immediately after the function call.
Hope it clarifies working for you.
What you are experiencing happens because of how NSString is implemented.
Since NSString objects are immutable the compiler takes a shortcut when you use stringWithString: with a string literal as argument. If the argument of this and other related methods is a string literal the returned value will just point to the string literal. The whole object instantiation is optimized away.
And string literals won't be deallocated. But the weak variable is only nil'd out during dealloc, so if dealloc is never called the weak variables are never set to nil.
This won't happen if you use stringWithFormat:. Even using only string literals as argument will create new string instances.
Why? Most likely because Apple decided that it's not worth the effort to check if stringWithFormat: was used with a string literal that does not have any format specifiers.
That's an implementation detail, don't think too long about this decision. It should not influence the code you write. I would suggest you treat every string that is not a bare literal (i.e. #"Foo" without any NSString methods) as dynamically created NSString (i.e. use isEqualToString: for all your string comparisons)
This logging code will show this reuse behaviour. It'll show the same addresses for all NSString instances, because the compiler has optimized all those calls to a simple #"Foo".
NSLog(#"%p", #"Foo");
NSLog(#"%p", [[NSString alloc] initWithString:#"Foo"]);
NSLog(#"%p", [NSString stringWithString:#"Foo"]);
NSLog(#"%p", [[NSString stringWithString:#"Foo"] copy]);
NSLog(#"%p", [#"Foo" copy]);
In newer versions of Xcode you will even get nice warnings for this code:
using initWithString: with a literal is redundant
using stringWithString: with a literal is redundant

NSString Release

I have this code of string and I have problems trying to free up memory, I have understood that only those who release it initializes and is not autorelease but I had problems with the string "end", and as nSum release.
NSString *urlBase = [[NSString alloc] initWithFormat:#"http://service.svc/"];
NSString *op = [[NSString alloc] initWithFormat:#"op1"];
NSString * final = [urlBase stringByAppendingFormat:op];
NSString * nSum = sumTextfield.text;
final = [final stringByAppendingFormat:nSum];
//release
[ urlBase release ];
[ op release ];
//[final release]; error
//[final autorelease]; error
thank for you help.
UPDATE:
- (IBAction)mostrarOpciones {
// code (UP)
}
If you create an object using a method that begins with init, new, copy, or mutableCopy, then you own that object and are responsible for releasing it (or autoreleasing it) when you're done with it. If you create an object using any other method, that object is autoreleased, and you don't need to release it. In that case, you actually need to retain the object if you want to keep it around. Apple has a Memory Management Programming Guide that includes all these rules.
The code you've posted is actually correct. You need to release urlBase and op because you created them using a method beginning with init (initWithFormat: in this case). final and nSum are already autoreleased for you. final was created by a method that doesn't begin with init, new, copy or mutableCopy (in this case, the factory method stringByAppendingFormat:). nSum was returned by a method called text, and you can assume that sumTextField "owns" it or has autoreleased it before returning it to you, and so you're not responsible for releasing it.
you cannot release NSString which you did not allocate. Since your two variables are not allocated, they need not to be released.

Retain/Release through intermediary method

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

objective-C: simple question about copy/retain NSString

If I set a NSString as property
#property (copy) NSString* name;
I always want to use (copy), so that if its value change, all object with such string still have the old value (as specifiedhere).
However, what happens if my NSString is not a class property, but it is just declared in the code ? Is in that case retained or copied everytime I assign a new value ?
thanks
It depends on how you declare it. You should read the documentation on memory management. Basically the rules are:
NSString *aString = [NSString stringWithString:#"Hello"];
NSString *bString = [NSString stringWithFormat:#"%#", #"Hello"];
In these cases, the string is not copied or retained. It is autoreleased, which means it will be automatically deallocated the next time the autorelease pool drains. You do not have to call the release method on them. (So assigning a new value will not cause it to leak.)
NSString *cString = [[NSString alloc] initWithFormat:#"%#", #"Hello"];
[cString release];
According to Objective C convention, methods that use alloc and have a retain count of one are not autoreleased, so you need to release them explicitly. Assigning a new value without releasing the old one will cause a leak.
You can also explicitly call a "copy" method or a "retain" method on a string. In either case, the new string will have a retain count of 1 and will not be autoreleased, so you will need to call the release method on it before you assign a new value.
NSString *dString = [cString retain];
NSString *eString = [cString copy];
...
[dString release];
[eString release];
If it is a property, and you use self.variableName, this will be taken care of for you (through the getters and setters which are generated with #synthesize). If you do it explicitly, you must make sure to call release on variables that you have called retain or copy on.
Edit: As some commentators below have noted, thinking about management in terms of "ownership" is usually the preferred of describing these ideas, rather than retain count.
If it's not a property and just declared in code, you need to explicitly retain or copy it, ie
NSString myString = [otherString copy];
or
NSString myString = [otherString retain];
Either way you also need to ensure it's released at somepoint.
If you're not using the property's setter, like self.name = #"foo" or [self setName:#"foo"], but rather assign the variable directly, like name = #"foo", it doesn't matter at all how the property is declared.
You have to understand that the property syntax is just a shortcut for writing accessor methods (-name and -setName: in this case). If you're not calling these methods (implicitly by setting the property), it doesn't matter how they work internally (which is what you specify by retain or copy).

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!