This is probably a dumb question, but in the introduction to TableViews, the author has a property of NSArray *listData to fill the table with dummy data. In the viewDidLoad, he basically does this:
- (void)viewDidLoad {
NSArray *array = [[NSArray alloc] initWithObjects#"1", #"2", #"3", more stuff, nil];
self.listData = array;
[array release];
...
}
Why does he create another array and set it to the property as opposed to doing something like
- (void)viewDidLoad {
listData = [[[NSArray alloc] initWithObjects#"1", #"2", #"3", more stuff, nil]autorelease];
Is it to manage memory better by using alloc/init vs the autorelease pool? Or is the second way just not going to work? Thanks.
Your code is wrong and is likely to crash. In his code, he calls alloc, meaning the retain count is 1. He then assigns it to a property. I'm assuming this property is declared to be retain, in which case the retain count would go up to 2. He then called release, which drops the retain count back to 1.
In your code, you call alloc, meaning the retain count is 1, then you call autorelease, which means that the retain count will drop to 0 and the object's memory will be deallocated soon. You assign the object to an instance variable - not a retained property like he does - so you won't increase the retain count any more. This means that you'll be left with a dangling pointer to memory that could be overwritten with anything else at any time. When you try to access listData, you'll crash because it is likely to have been overwritten.
Please read Memory Management Programming Guide if you do not understand what is happening here.
Having said that, the core of your question is valid. There's nothing stopping him from doing the same as you, except assigning to a retained property instead of an instance variable.
Related
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 noticed that my program was crashing because it was running out of memory. I figured out that this was happening because of this code segment:
DataSet *tempSet = [[DataSet alloc] init];
tempSet.rightFoot = [NSNumber numberWithDouble:temp1];
tempSet.leftFoot = [NSNumber numberWithDouble:temp2];
[footData addObject:tempSet]; //add dataSet object to the array
[tempSet release];
I read some tutorials about memory management online and was able to figure out that I needed to do this: (notice the added "autoreleases")
DataSet *tempSet = [[DataSet alloc] init];
tempSet.rightFoot = [[NSNumber numberWithDouble:temp1] autorelease];
tempSet.leftFoot = [[NSNumber numberWithDouble:temp2] autorelease];
[footData addObject:tempSet]; //add dataSet object to the array
[tempSet release];
I am still confused about why I had to do this. I did not use alloc, new or copy when creating the numberWithDouble.
Does this mean that I would need to add autorelease in this situation as well?:
[subset addObject:[NSNumber numberWithDouble:temp]];
What about this situation?:
tempSet.rightFoot = [NSString stringWithString:#"temp"];
I appreciate any help.
+numberWithDouble
is called a convenience method. Meaning, it replaces the little section of code that would look like this:
[[[NSNumber alloc]initWithDouble:double]autorelease];
Most (if not all) convenience methods are auto release by default, so the OP code with the autoreleases is incorrect, as it drops the retain count to -1.
The equals sign however is equivalent to
[self setRightFoot:[[[NSString alloc]initWithString]autorelease]];
which increments rightFoot's retain count and requires it to be released elsewhere.
as for the -addObject code, it returns void, so it does not in fact increment the receiver's retain count, and requires no release of the receiver. The object in the array should already be released by the convenience method for later, which doesn't matter because the array is now holding "copy" of it.
This is not an answer (I just do not know how to comment -- I only see "share, edit, flag"), but just a few info about Memory Management in iOS:
1. Don't release objects that you do not own.. ---> owned objects are usually the ones you "alloc", "new", "copy." //And probably the one in your #property wherein you "retain" an object.
2. when you "autorelease" an object, don't "release" it afterwards, because that would mean you're releasing the same object twice.
But there's ARC already, so you better upgrade your Xcode to avoid overreleasing objects / memory leaks (for not releasing objects)..
If there's something wrong or inappropriate with the one I put here, please edit. :)
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]
What's the right way to do this?
I have an array that I will use on several methods. I will add objects to it, get values, replace values, etc.
Today I do this:
I declare it on .h, using something like
NSMutableArray *myArray;
as soon as the application starts, I declare it on .m doing something like
myArray = [[[NSArray alloc] init] retain];
If I don't add the retain the array will be released at some point and the application will crash. But allocating the array at the beginning of the application and left it "open" without releasing it will make instruments cry, pointing the finger at me, calling me a "leaker"...
How to solve that? Is this the correct way to do that? how do you guys do stuff like this?
thanks
alloc implicitly sets the retain count to 1. By sending the retain message you're incrementing the retain count to 2. In order for the object to be deallocated you would then need to release it twice. Failure to do so would result in a memory leak.
Ideally you should create the object in your init method using [[NSArray alloc] init] and then release it in your dealloc method like so:
- (void)dealloc {
[myArray release];
[super dealloc];
}
You might also find this article useful: http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
One more thing: You declared myArray as an NSMutableArray but instantiated it as an NSArray. Perhaps that's causing the crash.
You should not retain the object you've just created. You already own it. If, as you say, "the array will be released at some point and the application will crash," that is the code you should change. Your code shouldn't be releasing an object that you still want to keep around.
I'm new to memory managed code but I get the idea pretty well.
On taking my app through the leaks tool in XCode, I noticed I only had to clean up my custom objects, but not dynamically created arrays for example, so I figured those data types are autoreleased - makes sense since I only had to release the arrays I used as properties that had a (retain) on them.
Then I noticed something peculiar : I was getting a leak on a certain array initialized like this :
NSMutableArray *removals = [NSMutableArray new];
but not a similar one
NSMutableArray *removals = [NSMutableArray arrayWithCapacity:9];
Now, the reason one was set up with "new" is that it could have 0-99 items in it, whereas the other one I knew was going to always be 9. Since both arrays are passed to the same method later based on user interaction, I was either getting a leak if I did not release at the end of the method, or an exception if I did!
I changed the first array to
NSMutableArray *removals = [NSMutableArray arrayWithCapacity:99];
and I get no leaks and don't have to release anything. Can anyone explain?
As noted in the memory management rules, whenever you have an object that you have created with +alloc, +new, -copy, or -mutableCopy, you own it and are responsible for releasing it at some point. (In fact, +new is just shorthand for [[MyClass alloc] init].) As you noted, creating an array via [NSArray new] without releasing it is a memory leak. However, if you handle this object properly, it is usually possible to release it at some point. For example:
If the method that uses the array is called from within the method that creates the array, then you should be able to release the array after it has been used. If the inner method needs to keep a more permanent reference to the array around, then that method is responsible for sending -retain and, eventually, -release to the object. For example:
- (void)myMethod {
NSArray *removals = [NSArray new];
// ...
[someObject someOtherMethod:removals];
[removals release];
}
If you created the array in an -init method for an object, then the -dealloc method can release it when the object is destroyed.
If you need to create the array and then return it from the method, you've discovered the reason that autoreleasing was invented. The caller of your method isn't responsible for releasing the object, since it isn't an +alloc, +new, -copy, or -mutableCopy method, but you need to ensure it is released eventually. In this case, you manually call -autorelease on the object before you return it. For example:
- (NSArray *)myMethod {
NSArray *removals = [NSArray new];
// ...
return [removals autorelease];
}
When you create the array via +arrayWithCapacity:, you aren't calling one of the "special" methods, so you do not have to release the result. This is probably implemented with -autorelease, much like the last example above, but not necessarily. (Incidentally, you can also create an empty autoreleased NSMutableArray with [NSMutableArray array]; the method is found in NSArray, so it won't show up in the documentation under NSMutableArray, but it will create a mutable array when sent to the NSMutableArray class.) If you're going to be returning the array from your method, you can use this as shorthand for [[[NSMutableArray alloc] init] autorelease]—but it is just a shortcut. In many situations, though, you can create an object with -init or +new and manually release it at the appropriate time.
This is how things implemented behind the scene:
+(NSMutableArray*) new
{
return [[NSMutableArray alloc] init];
}
and
+(NSMutableArray*) arrayWithCapacity:(NSNumber)capacity
{
return [[NSMutableArray alloc] initWithCapacity:capacity] **autorelease**];
}
In first case the array is allocated only and you're responsible for de-allocating it. In contrary the arrayWithCapacity has autoreleased for you and won't cause leak even you forget to deallocate.
Cocoa uses certain naming conventions. Anything that starts with alloc, new, or copy returns something with a retainCount of 1 and you are required to release. Anything else that a function returns has a balanced retainCount (it might be held by something else, or it might be retained and out released).
So:
NSMutableArray *removals = [NSMutableArray new];
Has a retainCount of 1, and:
NSMutableArray *removals = [NSMutableArray arrayWithCapacity:99];
or
NSMutableArray *removals = [NSMutableArray array];
Don't since the methods are not prefixed with alloc, new or copy. This is all spelled out in the memory management documentation. In particular:
You take ownership of an object if you
create it using a method whose name
begins with “alloc” or “new” or
contains “copy” (for example, alloc,
newObject, or mutableCopy), or if you
send it a retain message. You are
responsible for relinquishing
ownership of objects you own using
release or autorelease. Any other time
you receive an object, you must not
release it.