Assigning values : difference between properties and class variables? - iphone

I noticed that I rarely use properties, due to the fact that I rarely need to access my object's variables outside my class ;)
So I usually do :
NSMutableArray *myArray; // not a property !
My question is : even if i don't declare myArray as a property, does iphone make a retain anyway if I do
myArray = arrayPassedToMe;
I think so but I just wanted to confirm ;)
Any thoughts welcome !
Gotye

If you do not declare a property with 'retain' then no retain call will be made. It is generally preferable to use the property accessors (for all cases, it makes memory management much simpler), however you can perform a manual retain as such:
myArray = [otherArray retain];

Just to add to Kevin's answer, in this case you also need to make sure that any existing object at which myArray is currently pointing is freed before you assign new value to it, which means:
[myArray release];
myArray = [otherArray retain];
When you access your class variables via declared properties, all this stuff with retaining/releasing memory is done for you automatically, making your life much easier.

Related

When to use properties and allocation initializing

I was just working on my application where I needed to set an instance variable of NSMutableData a value. Now I also created a property for my instance variable which means that my program automatically allocates it etc, right? But then I assigned it a value but it was not taking it but staying null. I then manually allocated it and then it suddenly accepted the value. So now my question is what is the need for properties and why do I have to manually allocate my instance variable although I have a property set up for it?
Thanks in advance!
edit: my code:
in my .h file I have
#interface FirstScreen : UIViewController{
NSMutableData* fetchedData;
}
#property(nonatomic, retain)NSMutableData*fetchedData;
in my .m file I have:
-(void) connectionDidFinishLoading:(NSURLConnection *)connection{
NSString* fetchedDataString= [[NSString alloc]initWithData:fetchedData encoding:NSUTF8StringEncoding];
}
Now if I do not implement:
-(void)viewDidLoad{
self.fetchedData=[[NSMUtableData alloc]init];
}
fetchedDataString does not have any value. However if it is allocated it has a value. I am confuces when to allocate instance variables and when not to.
It doesn't allocate. All properties do for you is define the instance variable & implement accessor methods.
I'm assuming by "assigning a value" you mean trying to set the contents of the NSMutableData object you thought had been allocated for you.
Now I also created a property for my instance variable which means
that my program automatically allocates it etc, right?
Wrong. If you synthesize accessors for the property, an ivar will also be created for it if you haven't created one. But your property is just a pointer... it doesn't point to anything until you create an object for it to point to, and set it:
self.fetchedData = [[NSMutableData alloc] init];
That's C. What you're saying is true for every single variable, whether it's local or an instance variable. It's one thing to declare storage for a variable (that's all you're doing by declaring a property). It's another to give it a value. This declaration in code:
NSMutableArray* arr;
...does not cause arr to take on any particular value (under ARC it's nil; prior to ARC it could be anything at all). It is certainly not an empty mutable array! But that's exactly analogous to what you're doing when you declare a property.
If this is the first value the variable is to adopt, that's called initializing. You might say in code:
NSMutableArray* arr = [NSMutableArray array];
But you can't do that in a property declaration, so you have to initialize at some later time while the code is running. A typical approach is to do this in your designated initializer, so that no matter what happens later there will be an actual array at this address, from very early on.
I've written a book on this topic (iOS programming), and the chapter dealing with the issue you're having is free to read online:
http://www.apeth.com/iOSBook/ch03.html

Need to retain, and synthesize NSStrings?

I'm a bit confused as to whether NSStrings should ever be retained and synthesized. I have a NSString value as an instance variable, and am retaining and synthesizing it. But I am assigning it different values such as:
self.value = #"VALUE";
....
self.value = #"DIFFERENT_VALUE";
I'm not actually calling alloc anytime. Do I need to retain and synthesize this variable then?
You can think of on-the-fly strings as autoreleased in terms of how you use them, although in reality they will probably stay around as fixed values... because you are using the accessors they will automatically get copied or retained (however you marked the accessor) and so you do need to release them in dealloc.
As for the need to #synthesize, remember all that is doing for you is actually creating the get/set methods that take the variable and place it in your iVar. So not matter what you either need to #synethsize a property OR create the get/methods yourself - usually far better just to use #sythesize.
You should as you'll never know how you are going to change the use of the code in the future. Change the code to use dynamically created strings, and it will break if don't follow the rules.
Also note that the best practice for NSString is to set it to copy instead of retain. The reason is simple, this prevents the string from being changed under your feet.
See NSString property: copy or retain? for more details.
If you have never alloc'ed them, you usually don't need to retain, but if they are instance variables in your objects, they are probably marked as retain or copy so at your object's dealloc method you should release these objects if there is a value on it.

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

retain with objective-c

I have a question about a retain and a NSString, if I have a method who a return a NSString, and I put the return NSString in a nsstring variable, I must do a retain or not?
NSString *myString = #"";
myString = [self methodWhoReturnString]; // I must do this?
myString = [[self methodWhoReturnString]retain]; // Or I must do this?
The Apple Developer Documentation on Memory Management explains the scenarios where you retain/release objects.
Simply put, if you want the string to stick around, you need to retain it until you're finished with it. If that is just the scope of the current function, you can get away without retaining it as if the string is already autorelease'd (likely) it won't get released until your function finishes and the current AutoReleasePool is purged.
Bear in mind that an NSString * could actually be pointing to an NSMutableString *. If it matters to you if the string is changed by some other function without you realizing, be sure to copy it: NSString * myCopyOfString = [mystring copy];
If the string is set to autorelease, which it most likely is, then yes you will need to retain it somehow. I would suggest doing this though:
myString = [[self methodWhoReturnString] copy];
this ensures you have retained the data in the string not just a reference to a string that might still be controlled elsewhere. Be sure you release your copy later!
Usually, methodWhoReturnString would return an autoreleased string, which means you should retain it if you want to keep it around.
So, if methodWhoReturnString is your method, I believe that to keep with convention you should return [stringToReturn autorelease]; from that method, and then retain it if you want to keep it.
You use retain if you're going to be using myString at a later point in time (i.e. after the current method has returned) to prevent it being autoreleased.
You don't need to use retain if it's just a temporary variable used within the current method, since in that case you do want it to be autoreleased.
One special case is properties. If you call self.blah = foo, you don't need to retain foo, since the setBlah: method should do it for you.
(there's a whole load of other complexities and edge cases, but this is a good rule of thumb to get you started on understanding what to do)
Given the code you provided, you shouldn't call -retain. In your example, the return value of a method that returns an instance of NSString is assigned to myString, an automatic local variable. If the assignment had been made to an instance variable or a static variable, you would want to call either retain or copy to guarantee that the reference remains valid beyond the end of the local scope.
In this case though, the reference to the NSString instance is stored in a variable that will be destroyed automatically at the end of the local scope, so your code needn't concern itself with the object's lifetime.
Any method that has alloc, new or copy in it automatically retains and infers that you have ownership of the object. All others shouldn't. It would be helpful if you had more context though. If we are in a contained method where this string is used briefly, then you probably don't need to retain. If it is going to be used for a while, you might want to use the #synthesize syntax to make it a property of the class you are in. When you use #property and #synthesize and call something like self.myProperty = something it will automatically retain.

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.