Playing around with ARC: Force release irritation? - objective-c

I am currently playing around with ARC a bit to get some things figured out, before starting to do the actual work. I did setup this code:
NSNumber* n = [[NSNumber alloc] initWithInt:3];
__weak NSNumber* weakN = n;
n = nil;
NSLog(#">>>: %# %#", n, weakN);
I expected n and weakN to be nil, as n = nil; should trigger a release in my eyes? Unfortunately it doesn't. The output is ">>>: (null) 3". What am I missing here?
Another thing is, that I am pretty sure, the below code was giving me a hard time when starting with arc:
__weak NSNumber* weakN2 = [[NSNumber alloc] initWithInt:3];
NSLog(#">>>: %#", weakN2);
I am pretty sure, I've had some problems with similar code, as arc would release the object straight after initialization, as there is no strong reference to the object. Unfortunately, the output of the above is ">>>: 3".
It would be great to get some clarification on this stuff. I am clearly missing something here!
Best regards,
Michael

In addition to what kevboh said, it's also rather pointless to create weak references to simple immutable Foundation objects like NSNumber. For performance reasons, Foundation might well vend you a cached object instead of creating an entirely new one. And if it doesn't now, then it might in some future release.
The upshot is that you're probably not the sole owner of the object returned by [[NSNumber alloc] initWithInt:3], no matter what you think.

Well, you just picked a bad object to test this with. If you do it with NSString's (or most other objects), you get the expected result:
NSString* n = [[NSString alloc] initWithFormat:#"3"];
__weak NSString* weakN = n;
n = nil;
NSLog(#">>>: %# %#", n, weakN);
// Output is (null) (null)
__weak NSString* weakN2 = [[NSString alloc] initWithFormat:#"3"];
NSLog(#">>>: %#", weakN2);
// Output is (null)
The behavior of NSNumber is caused because the class is caching the number that was created so is actually still valid. The same behavior will be exhibited if you use string constants that are compiled in as part of the code. (Like NSString* n = #"3";)

I expected n and weakN to be nil, as n = nil; should trigger a release in my eyes? Unfortunately it doesn't. The output is ">>>: (null) 3". What am I missing here?
ARC doesn't work like that. Ownership of the object is nondeterministic; ARC likely held on to it until the end of your function. You should not expect deallocs to happen and instead use strong/weak references when you intend ownership to happen.

Related

__weak reference is still a mystery for me

NSString *myString = [NSString stringWithFormat:#"string1"];
__weak NSString *myString1 = myString;
myString= nil;
NSLog(#"%#, %#",myString,myString1);
I was expecting null , null. But the output is string1, (null). Why is myString1 still holding the value as myString is set to nil?
Weak references only get zeroed when the object is deallocated. That object is not immediately deallocated (it's probably in an autorelease pool here, though there are many other reasons something might be held onto in different situations), so the reference stays alive.
Try something like this:
NSString *myString;
NSString* __weak myString1;
#autoreleasepool{
myString= [NSString stringWithFormat:#"string1"];
myString1= myString;
myString= nil;
}
NSLog(#"%#, %#",myString,myString1);
Explanation
You probably noticed that there are many methods to allocate a string or generally an object:
1) [NSString stringWithFormat: ...] / [[NSString alloc]initWithFormat: ...] ;
2) [NSArray arrayWithArray: ...] / [[NSArray alloc]initWithArray: ...];
...
(Also for many other classes)
The first category of methods return an autoreleased object. The second one a non autoreleased object. Indeed if in the above code you use alloc + initWithFormat: instead of stringWithFormat: you don't need an autorelease pool to see that both objects will be nil.
I think your question may be answered by this quote from the Memory Management Guide
In particular, you should not design classes so that dealloc will be
invoked when you think it will be invoked. Invocation of dealloc might
be delayed or sidestepped, either because of a bug or because of
application tear-down.
The output should be (null), string1, not string1, (null). I guess you typed it the wrong way around.
You're explicitly setting one reference to nil, but the other reference is still being used within the scope of the definition (because you're using it in the NSLog). So, it won't be released by ARC until that usage is complete.
The weak reference isn't holding onto it. The fact that you're using it means that ARC will hold onto it (by not adding the release code). Once that usage is complete, ARC will release the object and then the weak reference will be nilled.

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.

Using alloc, init in ARC enabled projects

Actually I am working on a project with ARC enabled. I know using alloc and init is taking ownership of the object. I know, If I create a string like this
NSString *myString = [[NSString alloc]initWithFormat:#"Something"];
then I need to release the myString on myself. What If I am using ARC enabled? I cannot release on myself. So will it create a leak? Or should I don't create object like this?
I can create a string like below code too.
NSString *myString = [NSString stringWithFormat:#"Something"];
But which type I need to use exactly for ARC enabled project ? What will happen if I use first type?
If you use ARC, all the necessary release calls will be added for you when you compile. It will not leak.
The difference between
NSString *myString = [[NSString alloc]initWithFormat:#"Something"];
and
NSString *myString = [NSString stringWithFormat:#"Something"];
is that the first one will automatically released after the last reference to myString in that block, while the second one is an autoreleased instance that will only be released at the end of the run loop. It's not a big difference, but if you're using a lot of objects, try to avoid autoreleased ones to keep memory usage low.
ARC takes care of the memory management, so no you don't need to worry about calling release on your myString variable, ARC will do that for you. Also as a suggestion I would recommend using convenience methods to create your object such as
[NSString stringWithFormat:#"Something"];
It's enough to set the string pointer to nil to release it.
You can also do the same things that you would be able to do without ARC, but with the advantage that if you don't explicitly do anything, the ARC will manage (almost) everything for you.
So to release it you set it to nil, let's see what else you could do:
NSString* str= [[NSString alloc]initWithUTF8String: "Hello"];
// here the retain count of str is 1
__unsafe_unretained NSString* string= str;
// again 1 because string is __unsafe_unretained
void* data= (__bridge_retained void*) string;
// data retains the string, so the retain count is to 2
// This is useful in the case that you have to pass an objective-c object
// through a void pointer.You could also say NSString* data= string;
str=nil;
// Here the retain count of str is 1
NSLog(#"%#",(__bridge NSString*)data);
UPDATE
Here's why sometimes you don't notice that an object is released:
NSString* str= [[NSString alloc]initWithString: #"hey"];
__unsafe_unretained NSString* str2=str;
str=nil;
NSLog(#"%#",str2);
In this case str=[[NSString alloc]initWithString: #"hey"] is equal to str=#"hey", with the difference that str is autoreleased and not released.But the compiler optimizes the code in str=#"hello", so if you are inside an autorelease block you won't have any problem, str2 will be printed correctly.
That's why I used initWithUTF8String, to avoid that compiler optimization.

How to assign a string value in Objective C 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.

Does [NSMutableDictionary setValue: value forKey: key] retain NSString key?

When adding items to NSMutableDictionary using the setValue:forKey: method (I suppose this generalizes to any NSObject) does the dictionary retain the second parameter, the NSString?
For example:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
NSString *theString = #"hello";
int i;
for (i=0; i<[theString length]; i++){
NSNumber *myInt = [NSNumber numberWithInt:i];
NSString *character = [NSString stringWithFormat:#"%C",[theString characterAtIndex:i]];
[dict setValue: myInt forKey:character];
}
[dict release];
[pool release];
Clearly, there is no reason to release myInt in the loop, it is retained by dict so it can't be released until the end of the code. But is the same true of character? My thinking is that if NSMutableDictionary stores the string in some other way, then one could create a temporary pool around the loop and release those strings instead of waiting until the release of the dictionary.
I am also curious as to why retainCount of character is 7fffffff as if it is an NSConstantString, I would expect stringWithFormat to return an NSString object which would need retaining, but that doesn't seem to be the case.
It's very common in Cocoa for NSString parameters to be copied instead of retained. That's because you could have just as easily given the dictionary an instance of NSMutableString. Because the string's value could change, NSDictionary makes a copy.
But, regardless of how NSMutableDictionary really operates, you don't have to worry whether character needs to be retained. Once you've passed it to NSMutableDictionary as a parameter, it's really that class's problem to decide how to store the data, unless the documentation specifically tells you that retaining the objects are your responsibility.
I also wouldn't worry too much about the retainCount of any object. Following the retain count of an object too closely can lead you down rabbit holes that just make you spin your wheels.
Finally, I really don't think you need to create your own autorelease pool here. Unless you know with absolute certainty that theString is going to be very long, or you've already observed high memory utilization in Instruments, adding the autorelease pool is an unnecessary optimization.
You don't need to retain character there, the dictionary retains it when you set it as a key and your own code has no need to retain it.
You also don't need to worry about why the retain count isn't what you expect. Maybe the Foundation framework has Flyweight-like instances of a load of single-character NSString instances. In any case if you've got the memory management correct following the guidelines, you'll be OK regardless of what the framework's doing behind the scenes. http://iamleeg.blogspot.com/2008/12/cocoa-memory-management.html