Short Question with a code example:
NSLog(#"%i", [[[NSArray alloc] init] retainCount]);
NSLog(#"%i", [[[NSMutableArray alloc] init] retainCount]);
Output:
2
1
Why is the retainCount from the NSArray and NSMutableArray different?
Nobody outside of apple knows for sure (but I'm sure soon there will be somebody that claims he knows exactly why that happened).
Maybe this happens because iOS is smart and it reuses empty NSArrays. And obviously [[NSArray alloc] init] creates an empty array that is of no real use. And since it`s not mutable (ie you can't add objects later, and it will be empty forever) all empty NSArrays pointers can reference the same object.
The mutable one can't be reused because you can add objects to it.
Do not use retainCount!
From the apple documentation:
Important: This method is typically of no value in debugging memory management issues. Because any number of framework objects may have retained an object in order to hold references to it, while at the same time autorelease pools may be holding any number of deferred releases on an object, it is very unlikely that you can get useful information from this method.
To understand the fundamental rules of memory management that you must abide by, read “Memory Management Rules”. To diagnose memory management problems, use a suitable tool:
The LLVM/Clang Static analyzer can typically find memory management problems even before you run your program.
The Object Alloc instrument in the Instruments application (see Instruments User Guide) can track object allocation and destruction.
Shark (see Shark User Guide) also profiles memory allocations (amongst numerous other aspects of your program).
The reason is that [[NSArray alloc] init] returns the same object no matter how many times you call it. Look at this code:
NSArray *array1 = [[NSArray alloc] init];
NSArray *array2 = [[NSArray alloc] init];
NSArray *array3 = [[NSArray alloc] init];
NSLog(#"\narray1: %p\narray2: %p\narray3: %p",
array1, array2, array3);
The output is:
array1: 0x10010cae0
array2: 0x10010cae0
array3: 0x10010cae0
This makes sense, because NSArray is immutable, and all empty arrays are identical. It looks like NSArray keeps an empty array handy for this purpose since the retain count for the array pointed to by array1, array2, and array3 is 4.
I don't disagree with #fluchtpunkt's answer, but I think it's safe to say that we know exactly why this happens. I suppose you could say that nobody knows exactly why Apple chose to implement it this way, but it does seem like a good idea.
Related
I have started converting my projects to work under ARC and I was wondering how the following would behave.
As I understand the following line would cause a memory leak under the manual memory management rules.
self.array = [[NSArray alloc] init];
and it is recommended to use an autorelease object such as ,
self.array = [NSArray array] or
array = [[NSArray alloc] init];
Therefore, does the ARC mode cause a memory leak from the following line as well?
self.array = [[NSArray alloc] init];
When we are directly assigning to the array(?) as follows without using the generated setter could this cause premature release of the array ?
array = [[NSArray alloc] init];
Please consider array as an instance variable.
1) NO, not leaking.
2) NO, should work as well
Both ways are safe with ARC. Anyhow you should use properties where possible. The only case you need to be aware of is the following:
If your property is weak and you assign a newly created object like self.array = [[NSArray alloc] init], it will be gone right in the next line. That's a little bit strange in ARC.
But if your properties are strong, you don't need to care about memory stuff at all.
When you use ARC, the compiler actually inserts the proper Retains and Releases for you, so you won't have to worry about memory leaks in these situations. It helps to read a bit on ARC from the "Transitioning" guide:
http://developer.apple.com/library/mac/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
This is a basic question, and I'm not really sure what to search for to see if its been asked before.
In a lot of examples, I've seen property assignments handled like this:
NSArray *tempArray = [/*some code to get array*/];
self.propertyArray = tempArray;
[tempArray release];
Why not just do:
self.propertyArray = [/*some code to get array*/];
What's the difference between the two?
This answer is assuming your not using ARC.
The first code snippet, is the way Apple recommends initializing a property as long as you are doing this in any method besides init. You'll notice Apple code snippets do this a lot. It makes it much easier to manage your memory, and in my opinion it makes it easier to read.
NSArray *tempArray = [[NSArray alloc] initWith....]
self.propertyArray = tempArray;
[tempArray release];
The second code snippet you have could potential lead to a memory leak depending how you set up the NSArray.
This would be a leak. The propertyArray would have an retain count of 2. After you release the instance variable in dealloc, you still have a retain count of 1. That is a leak.
self.propertyArray = [[NSArray alloc] initWith...];
This would be okay, because they both are being autoreleased, which would give you a retain count of 1. As, long as you release the instance variable in dealloc.
// Class Method so autoreleased
self.propertyArray = [NSArray arrayWith....];
// Explicitly declaring autorelease
self.propertyArray = [[[NSArray alloc] initWith.....] autorelease];
It's simply just a matter of preference. I prefer the first way, I think it is easier to to read and follow.
Edit (with ARC):
All these methods would be acceptable with the exception of the release statement and autorelease statement.
[tempArray release];
ARC automatically takes care of the memory management for you, so you do not have to explicitly release them. This is the benefit of ARC, you can create as many objects as you want without the headache of trying to figure out where to release them.
Also, your property would be different. You must either use strong or weak, and not retain. In this case strong would be the solution.
#property (nonatomic, strong) NSArray *tempArray;
In the second example, you don't release the object, which is retained by the property, so you have a memory leak.
self.propertyArray = [[SomeClass alloc] init];
// ^ no release
With ARC, the two statements are equivalent in practice (although you would have to omit the release call for it to actually compile in ARC).
In a manual managed memory scenario, you would leak tempArray in the second ("direct assignment", which it isn't because you're calling a property setter not setting an ivar) example, as you do not have a release call on tempArray to balance it's alloc/init.
The the useful distinction is reduced to expressiveness, the ability to debug, and ultimately the programmers personal preference.
Your first example is the way it was done before the advent of automatic reference counting (ARC). The second example works fine under ARC. Unless you have to do it the old-fashioned way, select ARC for all your projects.
Code like that most likely means that somebody wanted an ability to debug it easier. Basically if you have a separate variable, you can print it out in the debugger without triggering (possibly custom) property setters and getters.
I am in the process of learnig objective-c and programming an iPad app. One thing I keep tripping myself up on and having to re-read is memory management. I am getting there...slowly. Basic rules such as for every alloc / retain you must have a release is useful. However, one relatively basic thing eludes me and I wonder if someone could explain...
Take the following code...
NSArray *myArray = [[NSArray alloc] init];
myArray = [someNSSet allObjects];
This is relatively straight forward coding and would require a [myArray release] statement.
However, I keep seeing examples of (and indeed, I have used extensively the following 'short cut'...
NSArray *myArray = (NSArray *)[someNSSet allObjects];
How, as far as I understand when you use the (NSString *) you dont need to use a [myArray release] statement, but I dont understand why.
Could someone possible explain?
NSArray *myArray = [[NSArray alloc] init];
myArray = [someNSSet allObjects];
this code is leaking myArray because you lose the reference to NSArray that you've allocated on the first line; you don't need to alloc here, because on the second line you're assigning a new value to myArray.
NSArray *myArray = (NSArray *)[someNSSet allObjects];
and this code example is perfectly fine, you're assigning the result of [someNSSet allObjects] to myArray pointer and you don't own the returned value, so you don't need to care about releasing it.
Consider using ARC (Automatic Retain Counting) for you project. With ARC the compiler takes care of retain counts so you don't have to, in fact aren't allowed to. There is a refactoring that will convert a current project.
As you said, there is a leak in the first code you posted. so you must add a release:
NSArray *myArray = [[NSArray alloc] init];
[myArray release];
myArray = [someNSSet allObjects];
In fact, when you obtain an object through a method that starts with alloc, new or copy, you own it, and you should release it. That's why, here you should release the array you obtained using the method alloc. This convention makes it easy to know when you own objects and when you don't. So remember: alloc, new or copy.
As for the second example, you obtained the array though a method that doesn't start with one of the three words (alloc, new or copy), so you don't own the object, and you are not responsible of releasing it. In fact, the array you obtained is an autoreleased object, which means that even though its retain count is currently 1, it will be automatically released when something called the autorelease pool is drained.
Here is a reference about Memory Management Rules.
In the first line:
NSArray *myArray = [[NSArray alloc] init]
some amount of memory is allocated for an array (actually in this case it is senseless since the size of the array is 0. Keep in mind that NSArray is immutable!).
The variable myArray holds the address of the first byte of the reserved memory area.
Now in the second line you change the value of myArray which now will point to the first byte of the memory area where [someNSSet allObjects] is stored. At this moment you do not know any more where the array is stored what you've created in the first line. And so you have a leak.
The line:
NSArray *myArray = (NSArray *)[someNSSet allObjects];
is correct, since you do not reserve any memory at this point. If you are not using ARC you might call retain in order to keep GC away from the referenced block of memory. In other way you might receive a BAD_EXEC when the owner of the object releases it and you try to access it through e.g.: [myArray objectAtIndex:0]
I've seen in (Apple) sample code two types of ways of allocation memory, and am not sure I understand the difference and resulting behavior.
// FAILS
NSMutableArray *anArray = [NSMutableArray array];
[anArray release];
// WORKS
NSMutableArray *anArray1 = [[NSMutableArray alloc] init];
[anArray release];
By "FAILS" I mean I get crashes/runtime warnings etc., and not always as soon as I call the release...
Any explanation appreciated.
Thanks
Please keep in mind that
NSMutableArray *anArray = [NSMutableArray array];
acts like:
NSMutableArray *anArray1 = [[[NSMutableArray alloc] init] autorelease];
So doing a release again will cause the crash as you are trying to release an autoreleased object.
Hope this helps you.
Thanks,
Madhup
In the first instance you are getting an autoreleased object, which you don't need to release
The second instance is where you are manually allocating the memory yourself, so you a responsible for releasing it.
Read this documentation for help:-
http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
To clarify on djhworlds response:
alloc, copy, mutableCopy and new make you the owner of the new object, retain makes you an owner of an existing object, and you become responsible for -[(auto)release]ing it. Other methods return an object that has been -[autoreleased], and thus you don't have any responsibility for it, but beware: It will disappear on the next iteration of the run loop (usually), as that is generally when the autorelease pool drains.
The practical upshot of this is that the //FAILS version works perfectly in the context of that particular piece of code, but once the run loop rolls around and the pool is drained, your object, being already released and gone, causes things to go boom.
In a branch of my code, I previously used this
NSMutableArray *array1 = [[NSMutableArray alloc] init];
The above array is used populate a UITableVew.
Just cause, I switched to the following:
NSMutableArray *array1 = [NSMutableArray arrayWithCapacity:0]
I made no other changes to my code) and my app crashes whenever I try to scroll down the list in the UITableView.
It looks like my array is not initialized correctly. Can someone explain why this would happen? Are the two methods not identical wrt how the underlying memory space is allocated?
Your second line of code is not retaining the NSArray, which is causing a crash. You'll need to call [array1 retain] after you call arrayWithCapacity:.
There's quite a bit of useful information in this post: Understanding reference counting with Cocoa / Objective C
In general, if you're calling a class method that doesn't start with "new" or "init" (e.g. arrayWithCapacity), you can usually assume that the returned object will be autoreleased.