Why create a variable rather than direct assignment? - iphone

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.

Related

objective-c: when to autorelease an nsnumber object when not using alloc

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. :)

TablewView example in Beginning iPhone book

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.

releasing variables to save memory

I have a beginner's question about releasing variables and not wasting memory...
I don't quite understand when to release variables. I understand that I should always do this if I have assigned them in my header file (in my #interface section and my #property commands). I release them in my -(void)dealloc function.
However, what am I supposed to do with variables that I happen to use in some of my methods, e.g.:
for (int temp = 0; temp < 3; temp++) {
// do something...
}
[temp release];
This is obviously wrong (at least xCode tells me so), as 'temp' is undeclared. But why? I've declared it as an int and temp thus takes up space in my memory, I'm sure. How do I tell the program to free up the space temp has taken after I don't need it anymore? I'm sure this is obvious, but I simply don't get it.
I'd be very happy for any suggestions for a beginner of how not to be a memory pig and to have 'memory leaking' everywhere in my apps...
You declared it as an int in the scope of the loop. Once the loop is done, it goes out of scope.
Also, you can not release an int, which is a primitive type. You can only release a subclass of NSObject. A good rule of thumb is that you eventually have to release anything that you called alloc or retain on.
Edit: For your edification, memory management only applies to objects allocated from the heap. That would be NSObjects obtained via "alloc" or must C-level memory allocated with something like "malloc()". Declaring a variable like "int x" is called an "auto" variable in that is is created on the stack and will AUTOmatically disappear then that block ends (the end of a "block" being the end of the function or perhaps the end of a {} pair or even the end of a for/if/while block.
Since Objective-C is basically just a special version of C (with messages), it does not create permanent objects unless you explicitly tell it to. This is different form languages like Python or Javascript.
You only need to release objects, and temp is an int, not an object.
As far as when to release objects, Apple's docs explain that better than I can: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
You do only release objects, and not primitive types. For example you create an array with
NSArray *myArray = [[NSArray alloc] init];
This initialization allocated memory which you have to free after your done using your array, it's your responsibility, else there will be memory leaks. You can do that in the dealloc section of a controller, or at the end of a method, or even after you've enumerated through the array and no longer need it.
If you create instances of objects with other methods than alloc, copy or new(rarely used) you have to release them. If you call retain yourself on an object you have to release it as well.
Please refer to apples memory management rules, which have been posted earlier.
There are two ways to store information in RAM in C and c like things, primitives which are allocated automatically by the compiler, and memory chunks allocated by you in your program.
Variables allocated for you by the compiler are called "automatics" and can be marked by the essentially unused (because it is the default) "auto" keyword. Automatics are bound to the scope in which they are declared and go away when it ends. Regular variables, like "int x" are automatic.
Notably, pointers are typically automatic. However, the things they point to are not. Those would be the thing you asked to be allocated.
In objective-c, things are allocated with the alloc message. However, sometimes a class will call this for you, so you might not see it. To help make it clear what you should do, there is a tradition: If you get an object where you alloc'ed it, got it from a class method with the word "copy" in the method name, or sent it a "retain" message, then you own a share of it, and it won't go away until you send it a release message.
If you didn't get the object through one of those means, you must not release it, because you don't have a share in it.
So, in summary: regular variables (int, short, char, double, float, long) are automatic, no need to allocate it. Pointers are also automatic, however, the things they are pointing to are not. In obj-c, you own a share if you alloc'ed it, copy'ed it, or sent it a retain message.
You can't release an integer...
Release works only with instance of Objective-C classes.
Variables such as integers are placed on the stack, and they does not persist after a function/method call, unless allocated explicitely.
You only release objects. If you use the alloc, init procedure to create an object you must release it. If you retain an object you must release it. If you use a method that has the word "create" in it, you must release it. Anything else, the system will handle for you.
Primitives do not need to be released, only objects.
Biggest thing to keep in mind is that whenever you "alloc" or "retain" an object, put a corresponding "release" or "autorelease".
you see an error on the line [temp release]; because temp is not in scope. temp will not be seen outside of the for loop you created.
you do not need to clean up the memory of temp, since you have not claimed ownership of it(see Memory Management Rules): http://developer.apple.com/library/ios/#documentation/general/conceptual/DevPedia-CocoaCore/MemoryManagement.html
examples of objects where you do not need to release/manage memory:
NSArray *myArray = [NSArray array];
NSNumber *myNumber = [NSNumber numberWithInt:5];
NSString *myString = #"Hello World";
NSInteger i = 5;
int x = 2;
examples of objects where you do need to release/manage memory:
NSArray *myArray = [[NSArray alloc] initWithObjects:#"Hello", #"World", nil];
NSNumber *myNumber = [[NSNumber alloc] initWithInt:5];
NSString *myString = [[NSString alloc] initWithString:#"Hello World"];
-
typically, when your completely done using an object you own, you clean up its memory
see apple's docs for explanations on properties: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW1
setting a property with assign or readonly: you should not worry about releasing its memory, as you don't own it
property with retain or copy: you claim ownership of the object and need to release it at some point
this answer won't answer/solve all memory management questions/concerns, but it may shove you in the right direction

iPhone - initialising variables using self

So, let's say you have a local variable NSArray *myArray declared in your class header file.
You then write #property (nonatomic, retain) NSArray *myArray also in your header file.
In your .m file, you write #synthesize myArray.
All very standard so far. You now have a variable myArray, which can be accessed through setters and getters synthesized by Apple.
A little bit later, you initialise your variable.
NSArray *anArray = [[NSArray alloc] initWithObjects etc etc...];
self.myArray = anArray;
[anArray release];
So now myArray is pointing to an array in memory, which has a release count of one (if I'm not mistaken).
My question is, why can't we write
#property (nonatomic, assign) NSArray *myArray;
#synthesize myArray;
..and then by initialisation write
self.myArray = [[NSArray alloc] initWithObjects etc etc...];
This has TOTALLY confused me ever since the first time I saw it. Is there a technical reason for this? Or moral? ;-) Or theoretical?
Any help would be MUCH appreciated...
Cheers
Karl...
One of the points of properties is to ease us from having to think about memory management ourselves. Making the property assign and then assigning a retained object into it kind of defeats the purpose of using the property.
It's really simple to do:
#property (nonatomic, retain) NSArray * myArray;
#synthesize myArray;
self.myArray = [NSArray arrayWithObjects:etc, etc1, etc2, nil];
And then all the memory management is taken care of for you.
You can.
I mean, it's what I'm doing in my program because I don't like using retain property ^^
It doesn't work ? what is the error ?
By the way you can just write
myArray = [[NSArray alloc] initWithObjects etc etc...];
You can write:
self.myArray = [[[NSArray alloc] initWithObjects etc etc...] autorelease];
(note the addition of the autorelease)
Though it would be simpler to write:
self.myArray = [NSArray arrayWithObjects etc etc...];
Purists might argue that you shouldn't put things into an autorelease pool unless you really need to, however if it makes your code simpler I say go for it, the performance overhead is negligible in many/most cases.
If you use an assign property instead, you need to make sure you release the old contents of myArray yourself, which negates much of the advantage and simplicity.
Memory management in Cocoa (and Cocoa Touch) is very strongly based on conventions. One of those conventions is that objects take ownership of other objects they need to keep around, which means that they must properly retain (to claim ownership) and release (to relinquish ownership) those objects. If you make it an assign property and require every caller to handle the memory for you, this violates the memory management conventions.
It's also poor program design, because rather than have one place (the setter) that is concerned with managing that property, instead you spread the responsibility to every place that accesses the property. Clear separation of concerns is one of the most important aspects of design.
In short: You can do it the way you're asking about. It's just worse in every respect. It violates the assumptions Cocoa makes, it makes bugs more likely, it complicates your design and it bloats your code.
However, in cases where you're setting properties of self, you can do something like what you want. Instead of writing self.someProperty = [[NSString alloc] initWithString:#"Foo"], you can just write someProperty = [[NSString alloc] initWithString:#"Foo"] (assuming someProperty is the underlying instance variable). This is, in fact, the normal way to do it in an initializer method or a dealloc method. This allows you to simply assign the variable in the internal implementation of your class without requiring everybody who uses the class to do the class's memory management for it.
The short answer is that using assign will probably result in memory leaks. Unless you're very careful.
By declaring the array property as retain, you are indicating that the object should take ownership of the array by sending it a retain message and, more importantly, that it should send it a release message when it is no longer interested in keeping the array around. When you use assign, the object won't send the array any retain or release messages. So, in the example you give, there isn't a problem YET. You've created an array with a retain count of one (conceptually) and given it to your object. In this case, the array hangs around in memory with a retain count of 1 just as it would have if you'd used the retain attribute when declaring the property.
The problem comes when you want to change the value of myArray. If your property is declared with retain, an assignment will do something like this:
- (void)setMyArray:(NSArray *)newArray {
if (myArray != newArray) {
[myArray release]; // Old value gets released
myArray = [newValue retain];
}
}
The old myArray gets sent a release message indicating that the object is done with it. If the retain count of myArray drops to zero, it will get deallocated and its memory reclaimed. If the property is declared with assign, this basically happens:
- (void)setMyArray:(NSArray *)newArray {
myArray = newArray;
}
The object forgets about the array at myArray without sending it a release message. Therefore, the array previously referred to by myArray probably won't get deallocated.
So, it's not the assignment that's a problem. It is the failure to release the array during reassignment that will cause the memory leak. This might not be a problem if another object owns the array.
If another object owns the array, and the array is just being referenced by myArray, that other object is in charge of making sure the array stays around as long as myArray needs it and of releasing the array when it's no longer needed. This is the pattern typically used for delegates. You then have to be careful that you don't access myArray after that other object has released the array it references.
Essentially, this comes down to the question of who owns the array referenced by myArray. If another object owns it and will handle retaining and releasing it as needed, it's perfectly okay for your object to simply reference it. However, if your object is the owner of myArray (and will be releasing it in dealloc), it makes more sense to use the retain attribute. Otherwise, in order to avoid leaks, you'll require other objects to release the contents of myArray prior to calling your object's setter, since your assign setter won't do it for you.
You definitely can.
Using "assign" properties instead of "retain" properties is actually a common practice (see some core object header files from Apple for examples). The issue here is your code being aware of this memory relationship (if the property has something in it at any given time).
Some programmers prefer this pattern, in fact. Complete personal control of memory.
I would add, however, that it is a very difficult pattern to protect when there are multiple developers on a project unless they are all the types that like manually managing memory. It's much easier to leak memory in this pattern from a simple oversight and compilers have a tougher time interrogating such problems.
There is no reason why you can't do that. You just have to pay some extra attention to your memory.
Because what happens when you later assign to the property again?
Using your example:
#property (nonatomic, assign) NSArray *myArray;
#synthesize myArray;
...
self.myArray = [[NSArray alloc] initWithObjects: #"foo", nil];
self.myArray = [[NSArray alloc] initWithObjects: #"bar", nil]; // MEMORY LEAK!
In this case you would have to manually release your ivar by calling release on it. If you do not, you will have leaked the memory.
Another smart thing about having it retained (or copied, less bug prone) it that you can say:
self.myArray = nil;
This will release the variable AND set the reference to nil, so you avoid getting yourself into trouble.
I absolutely see your point though. It is alot more verbose to have to write 3 lines instead of one. You can as #willcodejavaforfood suggests use autorelease when you are assigning to retained properties, as he seems to have missed). But Apple suggests that on the iPhone you do as little autoreleasing as you can, and we always listen to apple like good little children.
Update:
When you specify a property as (nonatomic, assign) an synthesize it the setter code that is generated looks something like this:
- (void)setMyArray:(NSArray *)newValue {
myArray = newValue;
}
If you on the other hand define it as (nonatomic, retain) you get:
- (void)setMyArray:(NSArray *)newValue {
if (myArray != newValue) {
[myArray release];
myArray = [newValue retain];
}
}
Hope it clears things up.

iPhone memory management, a newbie question

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.