Advertise here




Advertise here

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Slick's Definitive Guide To Properties

BrianSlickBrianSlick Treadmill Desk NinjaNorthern Virginia / DCPosts: 10,389Tutorial Authors, Registered Users @ @ @ @ @ @ @ @
edited May 2012 in iPhone SDK Tutorials
I keep answering similar questions with similar answers, so I'm trying to put this stuff in one place. I'll just start referring people to this thread. Please feel free to offer comments or suggestions, and I'll make corrections where appropriate. I'm open to debate on anything mentioned here, but this is what I do, it works well for me, and is drawn from the advice of significantly more-experienced coders.

Thread Guide

You can just scroll down and start reading, but the core of this thread involves these three posts:

Part 1 - Overview
Part 2 - Common Mistakes
Part 3 - Color Coding

Additionally, as questions are asked, answers are provided. Other useful posts in this thread to check out:
Dot Syntax
More Setter Behavior Explanation
Naming Conventions
Scope Overview, More Setter Behavior

These are just highlights, and not meant to detract from the other discussion posts that are also quite useful. The entire thread is worth reading. The less you understand, the more you should read.
Post edited by BrianSlick on
BriTer Ideas LLC - WWW | Facebook | Twitter | LinkedIn - Professional iOS App Development. Available for hire.
I can provide personalized help at codementor.io

SlickShopper 2 | Leave a PayPal donation

Free Xcode Tools: NSLog Utility | Getter Utility
GitHub Projects: BTIKit | BTICoreDataKit

Are you a newbie? Things you should read:
Definitive Guide To Properties | UITableView Series | A Model (Object) Is A Beautiful Thing
«134

Replies

  • BrianSlickBrianSlick Treadmill Desk Ninja Northern Virginia / DCPosts: 10,389Tutorial Authors, Registered Users @ @ @ @ @ @ @ @
    edited August 2013
    First, let's make sure we have our terminology on the same page. So a sample view controller would look like this:

    Figure 1
    //  TemplateViewController.h
    
    #import <UIKit/UIKit.h>
    @class MyClass;
    
    @interface TemplateViewController : UIViewController 
    {	
       MyClass *ivInstanceVariable;
    }
    
    @property (nonatomic, retain) MyClass *myProperty;
    
    @end
    

    Right off the bat, it is extremely important to keep in mind that properties and instance variables are not the same thing. It's relatively common for books and examples to use the same name - and that's ok - but you have to remember that they are two separate entities. One is a variable, the other is a method or pair of methods - myProperty and setMyProperty.

    The other half of this class should look like this:

    Figure 2
    //  TemplateViewController.m
    
    #import "TemplateViewController.h"
    #import "MyClass.h"
    
    @implementation TemplateViewController
    
    @synthesize myProperty = ivInstanceVariable;
    
    // ...
    // Other methods
    // ...
    
    - (void)dealloc 
    {
       // All 'retain' and 'copy' properties should be released here in dealloc.
       // There is no rule for 'assign', as it does not perform any memory management tasks.  Evaluate case-by-case.
    
       [ivInstanceVariable release], ivInstanceVariable = nil;
    	
       [super dealloc];
    }
    

    What @property (and @synthesize) does for you is create the getter/setter methods. You are still able to make your own if you want. In the 'old' days before Obj-C 2.0, you had to make them yourself. There are several ways of doing getters/setters, and that's why there are several options when you declare your @property. A typical getter/setter pair using retain might look like this:

    Figure 3
    - (MyClass *)myProperty
    {
       return ivInstanceVariable;
    }
    
    - (void)setMyProperty:(MyClass *)newValue
    {
       [newValue retain];
       [ivInstanceVariable release];
       ivInstanceVariable = newValue;
    }
    

    You don't need to create these methods (unless you have a reason to customize them). These are what are created behind-the-scenes when you @synthesize a @property. I'm just illustrating them so that you can see what they do.

    The important part here is what's going on in the setter. The new value is retained, the old value is released, and then the new value is assigned to the instance variable. This is what you want to happen, to avoid leaks.

    After that, simply remember that these are methods just like any other method. If you want to use a method within your class, you send a message to 'self'. You can't just do:
    calculateThisBigHairyNumber;
    
    You have to do:
    [self calculateThisBigHairyNumber];
    

    Getters/setters, whether done manually or via @property, are exactly the same. If you want to use them - and you pretty much always do - then you have to use self.

    Figure 4
    MyClass *myLocalVariable = [self myProperty];
    [self setMyProperty: aNewObject];
    

    I don't like dot-syntax, and recommend against its use, but if you must, that same thing looks like this:

    Figure 5
    MyClass *myLocalVariable = self.myProperty;
    self.myProperty = aNewObject;
    

    Now, the value. Creating an instance variable and defining the @property does NOT populate the instance variable with anything. It is empty until you put something in it. It is a little like a typed placeholder; yes, you've indicated that this instance variable will be an NSArray, thus only NSArrays will be allowed to occupy this space, but you haven't actually created an NSArray yet. So somewhere in your program, you will want to do that. Exactly where depends on when you'll need it, but for sake of discussion let's use viewDidLoad.

    First, create your object:

    Figure 6
    - (void)viewDidLoad
    {
       MyClass *newItem = [[MyClass alloc] init];
       // Not done yet...
    }
    

    Now we need to get that new object assigned to the instance variable. HOWEVER, you want to use the accessor methods as much as possible. A very good guideline is to use instance variables directly ONLY in dealloc, and/or in any custom getter/setter methods. Everywhere else, use the accessors.

    Figure 7
    - (void)viewDidLoad
    {
       MyClass *newItem = [[MyClass alloc] init];
       [self setMyProperty: newItem];
       // Not done yet...
    }
    

    Now we need to clean up our memory. According to Apple's Memory Management Rules, you need to release any object where you have done any of the following method: alloc, new, copy, retain. We did an alloc, therefore we are responsible for releasing this object. So at last we reach:

    Figure 8
    - (void)viewDidLoad
    {
       MyClass *newItem = [[MyClass alloc] init];
       [self setMyProperty: newItem];
       [newItem release], newItem = nil;
       // The nil thing is mostly defensive, to prevent a crash in case the 
       // object was inadvertently used again past this point.
    }
    

    Now, why did we do this in 3 lines? Why didn't we just go straight to the instance variable, like this:

    Figure 9
    ivInstanceVariable = [[MyClass alloc] init];
    

    Look closely at the example setter back there and notice what it does. It performs 2 actions: it releases the old value, and retains the new value. By using direct assignment to the instance variable, we did not use the setter method. This means the old value was NOT released. However, our instance variable is now pointing to our new object, so we no longer have a way of talking to the old object, and thus no means to send it the release message. Therefore, the old value has been leaked.

    Now you might be thinking "hey, this is viewDidLoad, this is one of the first things that happens... there probably isn't an old value to leak in the first place." That's probably true, but you want to be consistent with your techniques. And viewDidLoad can be run multiple times. You don't want to have to be constantly thinking "Ok, I'm in viewDidLoad, so I'll use the instance variable, but now I'm viewWillAppear so I'll use the property... uh, what should I do in my table view delegate methods?"

    The old value isn't the only problem here; what about the new object that was just created? Ah HA!, you're thinking... that gets released in dealloc! And if you do nothing else with the instance variable, that is possibly also true. But what happens if you do this in some other method:

    Figure 10
    ivInstanceVariable = someOtherObject;
    

    Now your 'new' object has become the 'old' object, and is subject to the same leak we just just described. Dealloc will only do any good - well, first of all you have to make sure to release your instance variable there, or else it REALLY won't do any good - on the object that exists in your instance variable at the time that dealloc is called. If you've swapped out objects in the instance variable 10 times, then there are 10 leaked objects, and only the last one will be released in dealloc.

    Technically, you can do the same thing that the setter is doing yourself. So that would be along these lines:

    Figure 11
    [ivInstanceVariable release];
    ivInstanceVariable = [someOtherObject retain];
    
    ...but this is just begging for trouble. You have to be incredibly consistent with how you use this instance variable. If you slide in an extra release somewhere, now you've overreleased the object, and that's bad. If you forget to do this release, now you've leaked the object, and that's bad. You may think you are saving yourself some effort by using the instance variable directly, but in reality you are causing yourself a bigger headache.

    Can you avoid this hassle by using autorelease, like this:

    Figure 12
    ivInstanceVariable = [[[MyClass alloc] init] autorelease];
    

    No. Autorelease means exactly that: the object will be sent a release message later. By using direct assignment, the property was not used, so the object does not receive the extra retain. The release message will balance the alloc here, so the object will be deallocated. Whenever that happens, and it may not be instantly, your instance variable will be referencing a dead object. You'll crash sooner or later.

    The life of your objects can be described in these 3 phases:
    1. Create it
    2. Do something with it
    3. Get rid of it, ASAP

    So let's return to the 3-line pattern from above and see how that links up:

    Figure 13
    - (void)viewDidLoad
    {
       MyClass *newItem = [[MyClass alloc] init];    // Create it
       [self setMyProperty: newItem];                // Do something with it
       [newItem release], newItem = nil;             // Get rid of it, ASAP
    }
    

    In this case, Step 2 amounts to "make this object available for my entire class". So what's also happening is a second, more subtle, 3-phase pattern

    1. Initial assignment to property (create)
    2. Usage throughout your class methods (do something)
    3. Release in dealloc (get rid of it)
    3a. Or get released when a new value is assigned

    Keep reading for Part 2...
    Post edited by BrianSlick on
    BriTer Ideas LLC - WWW | Facebook | Twitter | LinkedIn - Professional iOS App Development. Available for hire.
    I can provide personalized help at codementor.io

    SlickShopper 2 | Leave a PayPal donation

    Free Xcode Tools: NSLog Utility | Getter Utility
    GitHub Projects: BTIKit | BTICoreDataKit

    Are you a newbie? Things you should read:
    Definitive Guide To Properties | UITableView Series | A Model (Object) Is A Beautiful Thing
  • BrianSlickBrianSlick Treadmill Desk Ninja Northern Virginia / DCPosts: 10,389Tutorial Authors, Registered Users @ @ @ @ @ @ @ @
    edited August 2013
    One more pattern to discuss is the combination of alloc/init with the setter method. I see this a lot:

    Figure 14
    [self setMyProperty: [[MyClass alloc] init]];
    

    This is great, right? It's one line of code, I'm using the setter method... perfect.

    Wrong. This is a leak. Every time. Let's take it in steps. First, we create an object:
    [[MyClass alloc] init]
    
    This newly-created object has a retain count of 1, though for purposes of this discussion I'm going to use relative retain counts, not absolute. So doing alloc/init results in a +1 retain count.

    Next, we assign this object directly using the setter method. Remember what the setter method does: it releases the old value (we don't care right now), and it retains the new value. That's another +1. We are now at a total of +2.

    And there's no Step 3... We didn't assign the new object to a local variable first, so there is nothing to release.

    Eventually, our parent class will be deallocated. In the dealloc method, we release the instance variable:
    [myInstanceVariable release];
    
    Release changes the retain count by -1.

    This is worth emphasizing: Sending the 'release' message does NOT (necessarily) kill the object. You could create an object, send it the 'retain' message 50 times, and then send it the 'release' message 50 times. It will continue to exist (+1 from alloc) until its retain count is 0. When you send the 51st 'release' message, the one that brings the retain count to 0, THAT will kill the object. But the object is killed because the retain count is now 0, not simply because the release message was sent. That's a subtle but important distinction.

    Our parent class is now gone, so let's check our math:
    +1
    +1
    -1
    ----
    +1

    Uh oh. Our MyClass object never got down to 0, so it still exists. We no longer have any means to talk to it, so that object has been leaked.

    The same would be true if a new value gets assigned to the property. Remember, the setter will release (-1) the old value, and the object we created is now the old value. So again we are left with a +1 net retain count, again we do not have any means to communicate with the object, and again it is a leak. And as a double-bonus, if the new object is another alloc/init creation, we're replacing a leaked object with another leaked object.

    In order to be diligent, you can send a release message (before it's gone, obviously) this way:
    [[self myProperty] release];
    

    ...but if you get here, you are largely defeating the purpose of using @properties in the first place. The whole point is to simplify memory management. Manually releasing properties this way is just confusing the issue. Better to use the 3-line pattern and just worry about this stuff up front and be done with it.

    If you are dead-set against using the 3-line pattern, you can accomplish this with a single line:

    Figure 15
    [self setMyProperty: [[[MyClass alloc] init] autorelease]];
    

    This does not leak. It's mostly a style choice at this point.

    One other aspect that is worth pointing out is the use of other object's properties. Up to this point, we've only been worrying about the properties declared in our own class. But the rules all still apply to any other object's properties, or to any other class (such as collections) that performs some kind of memory management action. Arrays and dictionaries jump to mind. Each of these is a leak (without deliberate additional action on your part):

    Figure 16
    [anArray addObject:[[MyClass alloc] init]];
    [aDictionary setObject:[[MyClass alloc] init] forKey:@"key"];
    

    Collection classes will retain objects as they are added, and release them as they are removed. So the +1 -1 stuff described above still applies. The difference with pure properties is that collections still have the object, so in a worst-case scenario you can still get ahold of a reference and do any necessary releasing. But IMO, that's sloppy coding and is just begging for trouble.

    Bonus Test
    Brian keeps telling me that I'm not using properties, but I am! I've got the @property and @synthesize and everything!

    There is a very simple test you can perform to find out if your properties are being used. Comment out the @property and @synthesize lines, like so:
    //  TemplateViewController.h
    
    #import <UIKit/UIKit.h>
    @class MyClass;
    
    @interface TemplateViewController : UIViewController 
    {	
       MyClass *ivInstanceVariable;
    }
    
    //@property (nonatomic, retain) MyClass *myProperty;
    
    @end
    
    //  TemplateViewController.m
    
    #import "TemplateViewController.h"
    #import "MyClass.h"
    
    @implementation TemplateViewController
    
    //@synthesize myProperty=ivInstanceVariable;
    
    // ...
    // Other methods
    // ...
    
    - (void)dealloc 
    {
       [ivInstanceVariable release], ivInstanceVariable = nil;
    	
       [super dealloc];
    }
    

    Build your app. Heck, run your app. If you have no warnings, if it continues to function exactly as it did before, then you are not using your properties. If you do not understand why this is the case, then start over at the beginning of this thread and keep rereading until you do.

    Keep reading for Part 3...
    Post edited by BrianSlick on
    BriTer Ideas LLC - WWW | Facebook | Twitter | LinkedIn - Professional iOS App Development. Available for hire.
    I can provide personalized help at codementor.io

    SlickShopper 2 | Leave a PayPal donation

    Free Xcode Tools: NSLog Utility | Getter Utility
    GitHub Projects: BTIKit | BTICoreDataKit

    Are you a newbie? Things you should read:
    Definitive Guide To Properties | UITableView Series | A Model (Object) Is A Beautiful Thing
  • BrianSlickBrianSlick Treadmill Desk Ninja Northern Virginia / DCPosts: 10,389Tutorial Authors, Registered Users @ @ @ @ @ @ @ @
    edited August 2013
    I suddenly got the idea for a tip that will help identify when instance variables are being used: custom colors.

    So, head into Xcode's preferences:

    xcode_custom_colors.png

    Find the "Project Instance Variables" line and set the color to be something really obnoxious. I'm going with red here (and I changed the "Strings" line to something else, since it defaulted to red for me), but choose whatever will stand out amongst your other colors.

    Now, inspect your code. Here's a header file:
    [color="Magenta"]@interface[/color] DisplaySection : NSObject <NSCopying>
    {
       [color=magenta]@private[/color]
       [color=purple]NSString[/color] *[color=red]ivName[/color];
       [color="Purple"]NSString[/color] *[color="Red"]ivSectionHeader[/color];
       [color="Purple"]NSString[/color] *[color="red"]ivSectionIndexLetter[/color];
       [color="purple"]NSMutableArray[/color] *[color="red"]ivContents[/color];
    }
    

    This is where instance variables are declared, so that's fine.

    Synthesizing properties in the .m file:
    [color="Magenta"]@interface[/color] DisplaySection () // Private Methods
    [color="Magenta"]@property[/color] (nonatomic, readwrite, copy) [color="Purple"]NSString[/color] *name;
    [color="Magenta"]@property[/color] (nonatomic, readwrite, copy) [color="purple"]NSString[/color] *header;
    [color="Magenta"]@property[/color] (nonatomic, readwrite, copy) [color="purple"]NSString[/color] *indexLetter;
    [color="Magenta"]@property[/color] (nonatomic, readwrite, retain) [color="purple"]NSMutableArray[/color] *contents;
    @end
    
    [color="Magenta"]@implementation[/color] DisplaySection
    
    [color="Magenta"]@synthesize[/color] name=[color="red"]ivName[/color];
    [color="Magenta"]@synthesize[/color] header=[color="red"]ivSectionHeader[/color];
    [color="Magenta"]@synthesize[/color] indexLetter=[color="red"]ivSectionIndexLetter[/color];
    [color="Magenta"]@synthesize[/color] contents=[color="Red"]ivContents[/color];
    

    This should help to reinforce the difference between instance variables and properties.

    You DO want to use instance variables in dealloc:
    - ([color="Magenta"]void[/color])dealloc
    {
       [[color="Red"]ivName[/color] [color="Navy"]release[/color]], [color="red"]ivName[/color] = nil;
       [[color="red"]ivContents[/color] [color="Navy"]release[/color]], [color="red"]ivContents[/color] = nil;
       [[color="red"]ivSectionHeader[/color] [color="Navy"]release[/color]], [color="red"]ivSectionHeader[/color] = nil;
       [[color="red"]ivSectionIndexLetter[/color] [color="Navy"]release[/color]], [color="red"]ivSectionIndexLetter[/color] = nil;
       [[color="Magenta"]super[/color] [color="Navy"]dealloc[/color]];
    }
    

    Even though synthesizing properties will create getters/setters for you, you can customize them if you want. So if you have created custom getters/setters, use the instance variables there as well. Here I have a customized getter that makes sure my instance variable is populated with an array:
    - ([color="Purple"]NSMutableArray[/color] *)contents
    {
       [color="Magenta"]if[/color] ([color="Red"]ivContents[/color] == [color="Magenta"]nil[/color]) 
       {
          [color="red"]ivContents[/color] = [[[color="Purple"]NSMutableArray[/color] [color="Navy"]alloc[/color]] [color="Navy"]init[/color]];
       }
    	
       [color="Magenta"]return[/color] [color="red"]ivContents[/color];
    }
    

    In pretty much all other cases, you do NOT want to use the instance variables directly. So, this is a problem:
    - ([color="Magenta"]void[/color])setNameOfSection:([color="Purple"]NSString[/color] *)newName
    {
       [color="Red"]ivName[/color] = newName;
    }
    

    Use the accessor method instead:
    - ([color="Magenta"]void[/color])setNameOfSection:([color="Purple"]NSString[/color] *)newName
    {
       [[color="Magenta"]self[/color] [color="Navy"]setName[/color]:newName];
    }
    

    Much better.
    Post edited by BrianSlick on
    BriTer Ideas LLC - WWW | Facebook | Twitter | LinkedIn - Professional iOS App Development. Available for hire.
    I can provide personalized help at codementor.io

    SlickShopper 2 | Leave a PayPal donation

    Free Xcode Tools: NSLog Utility | Getter Utility
    GitHub Projects: BTIKit | BTICoreDataKit

    Are you a newbie? Things you should read:
    Definitive Guide To Properties | UITableView Series | A Model (Object) Is A Beautiful Thing
  • ghanalupoghanalupo Posts: 133Registered Users
    edited September 2009
    Fantastic thread and has shown me where I have obviously had leaks but did not understand why!

    I see many examples like this:
    self.title = ....
    
    imageView.image = ....
    
    

    so are these wrong too?

    should they be:
    [self.title setTitel: ...]
    
    [imageView setImage:...]
    
    

    instead?

    if so I must be leaking memory all over the show!!!
  • BrianSlickBrianSlick Treadmill Desk Ninja Northern Virginia / DCPosts: 10,389Tutorial Authors, Registered Users @ @ @ @ @ @ @ @
    edited August 2013
    Close. Using dot-syntax does not inherently cause leaks like that, so that isn't the issue. Mis-using dot-syntax, which IMO is very easy to do, leads to issues.

    So these are equivalent:
    self.title = ...
    [self setTitle: ...]
    

    As are these:
    imageView.image = ....
    [imageView setImage:...]
    

    In the second case, the only possible concern there would be using the instance variable directly, which you want to avoid. If that's a local variable, it's fine as-is. If it is an instance variable, then you want to use the accessor methods, so it should become:
    self.imageView.image = ....
    [[self imageView] setImage:....];
    

    The important part is 'self'.
    Post edited by BrianSlick on
    BriTer Ideas LLC - WWW | Facebook | Twitter | LinkedIn - Professional iOS App Development. Available for hire.
    I can provide personalized help at codementor.io

    SlickShopper 2 | Leave a PayPal donation

    Free Xcode Tools: NSLog Utility | Getter Utility
    GitHub Projects: BTIKit | BTICoreDataKit

    Are you a newbie? Things you should read:
    Definitive Guide To Properties | UITableView Series | A Model (Object) Is A Beautiful Thing
  • ghanalupoghanalupo Posts: 133Registered Users
    edited September 2009
    You are a star! Thanks for clear concise help ;)
  • sarahconnorsarahconnor Posts: 197Registered Users
    edited September 2009
    That's some really great info! Very clear and not greek for once! :rolleyes:
    I really appreciate that! NICE! B-)
    One Question...
    I've been having a problem with a view...when I leave the view to go to the next view, then come back to it all the info is there BUT I want it to be cleared! Is there some type of release or reload that I should be using on the view I want cleared out? :confused:

    Thanks again!

    Mandy :D
  • BrianSlickBrianSlick Treadmill Desk Ninja Northern Virginia / DCPosts: 10,389Tutorial Authors, Registered Users @ @ @ @ @ @ @ @
    edited September 2009
    That's not really a release issue. You need to define how you want the view to look when it is displayed, and choose an appropriate place to do so. viewDidLoad will fire one time, so is not useful for constantly changing stuff. viewWillAppear/DidAppear, and/or viewWillDisappear/DidDisappear may be more appropriate, as they fire each time a view is displayed or goes away.
    BriTer Ideas LLC - WWW | Facebook | Twitter | LinkedIn - Professional iOS App Development. Available for hire.
    I can provide personalized help at codementor.io

    SlickShopper 2 | Leave a PayPal donation

    Free Xcode Tools: NSLog Utility | Getter Utility
    GitHub Projects: BTIKit | BTICoreDataKit

    Are you a newbie? Things you should read:
    Definitive Guide To Properties | UITableView Series | A Model (Object) Is A Beautiful Thing
  • david_daviddavid_david Posts: 372Registered Users @ @
    edited September 2009
    good tutorial to all new iphone developers
    <a href="http://davidbits.blogspot.com" target="_blank">lazy blogs</a><br />
    The Lord's holy name be praised.
  • PeeksPeeks Posts: 22Registered Users
    edited November 2009
    Cheers for this thread Slick.

    One question. I see alot of examples with self.iboutletvariable = nil; in the ViewDidUnload method. I get that this gets called to release any subviews in a viewController that is currently not in view when mem is low.
    I see it also being done for other non-iboutlet variables as well, e.g. self.array = nil;.

    My confusion here is, with properties. Using dot syntax as a setter method like:
    self.variable = nil. Would this in effect release an old variable whilst retaining a new variable with value = nil? If, so - is it really freeing up memory seeing that it still has a retain count of 1?
  • BrianSlickBrianSlick Treadmill Desk Ninja Northern Virginia / DCPosts: 10,389Tutorial Authors, Registered Users @ @ @ @ @ @ @ @
    edited August 2013
    Peeks wrote: »
    My confusion here is, with properties. Using dot syntax as a setter method like:
    self.variable = nil. Would this in effect release an old variable whilst retaining a new variable with value = nil? If, so - is it really freeing up memory seeing that it still has a retain count of 1?

    Well, let's take a basic case with an integer, and a local variable.

    int myInt = 5;

    We've declared a variable, defined its type, and given it a value. If the value is changed...

    myInt = 10;

    ...there isn't anything keeping track of the old value (5). It is one variable, just with a new value.

    Same idea with objects.

    id myObject = [anArray objectAtIndex:2];

    If we then change that:

    myObject = [anArray objectAtIndex:5];

    ...myObject no longer knows about the old value. If we change it again:

    myObject = nil;

    ...myObject again no longer knows about the old value, but this time does not refer to any particular object. That's the point of nil. It's sort of a universal "nothing". The current value of this variable is nothing.

    A characteristic of Objective-C is that you can send a message to nil... and nothing happens. It's like a black hole. You can send all the messages that you want, but they aren't going anywhere. This would be a crash in some languages, Obj-C just decides 'eh, whatever' and doesn't do anything.

    So bringing this back around to @properties. Although you don't see this code when you @synthesize the @properties, the setter method that a retain property creates looks something like this:
    - (void)setTheArrayProperty:(NSArray *)anArray
    {
       [myArrayInstanceVariable release];
       [anArray retain];
       myArrayInstanceVariable = anArray;
    }
    

    This is how accessors were created before @properties were introduced with Obj-C 2.0. What this does is:
    1. release the old value
    2. retain the new value
    3. assign the new value to the instance variable.

    So your question is what happens when you send in nil. Same thing:
    1. the old value is released.
    2. the new value is retained.
    ---> Wait, what? Ok, keep in mind what I just said. The new value is nil. Nil is like a black hole. Retain is simply another message. So, sending the retain message to nil doesn't do anything.
    3. the new value (nil) is assigned to the instance variable.
    ---> So the instance variable is now nil.

    If we were looking at values instead of variables, it would look more like this:
    [[I][COLOR="Red"]someArray[/COLOR][/I] release];
    [[I][COLOR="red"]nil[/COLOR][/I] retain];   <-- doesn't do anything
    instanceVariable = [I][COLOR="red"]nil[/COLOR][/I];
    

    Carry that through to the next time you send in an actual object. So right now, the value is nil, but we're sending in a new array.
    1. the old value is released.
    ---> Nil. Black hole. Message.
    2. the new value (array) is retained
    3. the new value is assigned to the instance variable.

    Again looking at values:
    [[I][COLOR="red"]nil[/COLOR][/I] release];   <-- doesn't do anything
    [[I][COLOR="red"]anotherArray[/COLOR][/I] retain];
    instanceVariable = [I][COLOR="red"]anotherArray[/COLOR][/I]
    
    Post edited by BrianSlick on
    BriTer Ideas LLC - WWW | Facebook | Twitter | LinkedIn - Professional iOS App Development. Available for hire.
    I can provide personalized help at codementor.io

    SlickShopper 2 | Leave a PayPal donation

    Free Xcode Tools: NSLog Utility | Getter Utility
    GitHub Projects: BTIKit | BTICoreDataKit

    Are you a newbie? Things you should read:
    Definitive Guide To Properties | UITableView Series | A Model (Object) Is A Beautiful Thing
  • PeeksPeeks Posts: 22Registered Users
    edited November 2009
  • EAGLEEAGLE Posts: 55Registered Users
    edited November 2009
    Hey Brian, very nice memory management tutorial. Could you explain how you handle UIImage and UIImageView, especially how you free the memory when using imageNamed:

    Edit:

    Damn, this release crap is everything but easy:
    UIImageView *mybild1;
    @property (nonatomic, retain) UIImageView *bild1;
    

    @synthesize bild1 = mybild1;
    - (void)viewDidLoad {
    	// Festlegen der Hintergrundfarbe.
    	
    	UIImageView *myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Default.png"]];
    	NSLog(@"myImageView: %i", [myImageView retainCount]);
    	NSLog(@"mybild1: %i", [mybild1 retainCount]);
    	
    	[self setBild1:myImageView];
    	NSLog(@"myImageView: %i", [myImageView retainCount]);
    	NSLog(@"mybild1: %i", [mybild1 retainCount]);
    	
    	[myImageView release]; //, myImageView = nil;	
    	NSLog(@"myImageView: %i", [myImageView retainCount]);
    	NSLog(@"mybild1: %i", [mybild1 retainCount]);
    	
    	mybild1.frame = CGRectMake(0.0f, 0.0f, 320.0f, 460.0f);
    	NSLog(@"myImageView: %i", [myImageView retainCount]);
    	NSLog(@"mybild1: %i", [mybild1 retainCount]);
    	
    	[self.view addSubview:(UIImageView *) mybild1];
    	NSLog(@"myImageView: %i", [myImageView retainCount]);
    	NSLog(@"mybild1: %i", [mybild1 retainCount]);
    	
    	[mybild1 release]; //, mybild1 = nil;
    	NSLog(@"myImageView: %i", [myImageView retainCount]);
    	NSLog(@"mybild1: %i", [mybild1 retainCount]);
    	
        [super viewDidLoad];
    }
    

    Without setting mybild1 and myImageView to nil, the retainCount response looks like this:

    2009-11-30 14:13:01.226 KlimadatenApp[1239:20b] myImageView: 1
    2009-11-30 14:13:01.227 KlimadatenApp[1239:20b] mybild1: 0
    2009-11-30 14:13:01.228 KlimadatenApp[1239:20b] myImageView: 2
    2009-11-30 14:13:01.229 KlimadatenApp[1239:20b] mybild1: 2
    2009-11-30 14:13:01.230 KlimadatenApp[1239:20b] myImageView: 1
    2009-11-30 14:13:01.231 KlimadatenApp[1239:20b] mybild1: 1
    2009-11-30 14:13:01.232 KlimadatenApp[1239:20b] myImageView: 1
    2009-11-30 14:13:01.232 KlimadatenApp[1239:20b] mybild1: 1
    2009-11-30 14:13:01.232 KlimadatenApp[1239:20b] myImageView: 2
    2009-11-30 14:13:01.233 KlimadatenApp[1239:20b] mybild1: 2
    2009-11-30 14:13:01.233 KlimadatenApp[1239:20b] myImageView: 1
    2009-11-30 14:13:01.233 KlimadatenApp[1239:20b] mybild1: 1

    With setting them to nil, it looks like this:

    2009-11-30 14:11:43.698 KlimadatenApp[1201:20b] myImageView: 1
    2009-11-30 14:11:43.699 KlimadatenApp[1201:20b] mybild1: 0
    2009-11-30 14:11:43.700 KlimadatenApp[1201:20b] myImageView: 2
    2009-11-30 14:11:43.701 KlimadatenApp[1201:20b] mybild1: 2
    2009-11-30 14:11:43.702 KlimadatenApp[1201:20b] myImageView: 1
    2009-11-30 14:11:43.702 KlimadatenApp[1201:20b] mybild1: 1
    2009-11-30 14:11:43.703 KlimadatenApp[1201:20b] myImageView: 1
    2009-11-30 14:11:43.703 KlimadatenApp[1201:20b] mybild1: 1
    2009-11-30 14:11:43.704 KlimadatenApp[1201:20b] myImageView: 2
    2009-11-30 14:11:43.705 KlimadatenApp[1201:20b] mybild1: 2
    2009-11-30 14:11:43.707 KlimadatenApp[1201:20b] myImageView: 1
    2009-11-30 14:11:43.707 KlimadatenApp[1201:20b] mybild1: 0


    I still don't get it what I did wrong. As I understand it, setting the values to nil shouldn't change the retainCount. And without setting them to nil, their retainCount should be 0 because i released them.
  • BrianSlickBrianSlick Treadmill Desk Ninja Northern Virginia / DCPosts: 10,389Tutorial Authors, Registered Users @ @ @ @ @ @ @ @
    edited November 2009
    You cannot rely on the value from retainCount for troubleshooting. It only confuses the issue. However, in this case, it is reporting the correct information.

    1. alloc the image view: +1
    2. assign image view to property: +1
    3. release the image view: -1
    4. change the frame: 0
    5. Add the image view to parent view: +1
    6. (Incorrectly) release the image view: -1

    That's +1 overall. At some point, the view will release the image view (-1), and you will release your property in dealloc (-1), at which point you should crash.

    You also should not be doing a manual release of properties like that - that is only done in dealloc. If you wish to clear out a property, do this:
    [self setMyProperty: nil];
    

    Aside from that, an image view and an image aren't really any different than any other object. If you did an alloc, copy, new, or retain, you should release. imageNamed does not use any of those, so don't release. If you used a different UIImage initializer, like alloc/initWithContentsOfFile, then you would release.
    BriTer Ideas LLC - WWW | Facebook | Twitter | LinkedIn - Professional iOS App Development. Available for hire.
    I can provide personalized help at codementor.io

    SlickShopper 2 | Leave a PayPal donation

    Free Xcode Tools: NSLog Utility | Getter Utility
    GitHub Projects: BTIKit | BTICoreDataKit

    Are you a newbie? Things you should read:
    Definitive Guide To Properties | UITableView Series | A Model (Object) Is A Beautiful Thing
  • EAGLEEAGLE Posts: 55Registered Users
    edited December 2009
    Hi Brian, could you please write down a little code snippet on how to "make" a Label or Button that doesn't leak? I tried to write an example app to check whether my code will free a viewcontroller completely when i leave it. But I must have missed something in your guide, cf. my other thread:

    http://www.iphonedevsdk.com/forum/iphone-sdk-development/34870-memory-management-problem.html#post150245

    The Code, as it is at the moment, is freeing most of the used memory when returning to the mainView, but not completely.

    Thx, EAGLE
  • BrianSlickBrianSlick Treadmill Desk Ninja Northern Virginia / DCPosts: 10,389Tutorial Authors, Registered Users @ @ @ @ @ @ @ @
    edited December 2009
    You are using instance variables when you should be using properties. After your initial assignment, you stop using the properties for some reason.
    BriTer Ideas LLC - WWW | Facebook | Twitter | LinkedIn - Professional iOS App Development. Available for hire.
    I can provide personalized help at codementor.io

    SlickShopper 2 | Leave a PayPal donation

    Free Xcode Tools: NSLog Utility | Getter Utility
    GitHub Projects: BTIKit | BTICoreDataKit

    Are you a newbie? Things you should read:
    Definitive Guide To Properties | UITableView Series | A Model (Object) Is A Beautiful Thing
  • EAGLEEAGLE Posts: 55Registered Users
    edited December 2009
    Just for everyone reading this thread, here is the solution to my memory management problems:
    UILabel *myLabel = [[UILabel alloc] init];
    [self setBackLabel:myLabel];
    [myLabel release], myLabel = nil;
    [[self backLabel] setFrame:CGRectMake(50.0f, 100.0f, 220.0f, 30.0f)];
    [[self backLabel] setText:@"Label 1"];
    [[self backLabel] setFont:[UIFont boldSystemFontOfSize:12]];
    [[self backLabel] setTextColor:[UIColor blackColor]];
    [self.view addSubview:[self backLabel]];
    	
    UIButton *backButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
    [self setBackButton:backButton];
    [[self backButton] setFrame:CGRectMake(300.0f, 10.0f, 10.0f, 10.0f)];
    [[self backButton] addTarget:self action:@selector(done:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:[self backButton]];
    	
    NSString *defaultImage = [[NSBundle mainBundle] pathForResource:@"efault" ofType:@"png"];
    UIImageView *myImageView = [[UIImageView alloc] init];
    [self setBild1:myImageView];
    [myImageView release], myImageView = nil;	
    [[self bild1] setImage:[UIImage imageWithContentsOfFile:defaultImage]];
    [[self bild1] setFrame:CGRectMake(0.0f, 30.0f, 320.0f, 430.0f)];
    [self.view addSubview:[self bild1]];
    

    Compared to this thread, every other memory management guide out there, including Apple's guide is a piece of crap. You really helped me unterstand this very important stuff. Thx again
  • wdnwdn Posts: 28Registered Users
    edited December 2009
    Nice writeup Brian, but there's one more important check needed to avoid a common bug if you want to write your own setters (or modify an instance variable in general). Before releasing the old value, you'll need to make sure the new value isn't the same. Otherwise you may be trying to retain a freed object and crash.
    - (void)setMyProperty:(MyClass *)newValue
    {
      if (newValue != myInstanceVariable)
      {
        [myInstanceVariable release];
        myInstanceVariable = [newValue retain];
      }
    }
    

    or if you prefer brevity over control (not recommended):
    - (void)setMyProperty:(MyClass *)newValue
    {
      [myInstanceVariable autorelease];
      myInstanceVariable = [newValue retain];
    }
    

    There's some additional locking code @synthesize adds if you don't declare your properties to be nonatomic, so for most purposes it's easiest just to stick with the synthesized accessors to keep your code simpler.
  • BrianSlickBrianSlick Treadmill Desk Ninja Northern Virginia / DCPosts: 10,389Tutorial Authors, Registered Users @ @ @ @ @ @ @ @
    edited December 2009
    Well, it wasn't really supposed to be an emphasis on customizing setters, simply showing what setters - and thus, properties - do.

    I'm having a tough time envisioning how putting the same object into a setter again would be even a semi-common occurrence. Sounds like sloppy coding to me.

    In any event, I would think you could get around any issues related to that possibility by having the custom setter do the retain first, and the release second. Ex:
    [newValue retain];
    [instanceVariable release];
    instanceVariable = newValue;
    

    Elsewhere in code, instance variables shouldn't be messed with directly, so that check would be rendered moot by simply using the accessor methods.
    BriTer Ideas LLC - WWW | Facebook | Twitter | LinkedIn - Professional iOS App Development. Available for hire.
    I can provide personalized help at codementor.io

    SlickShopper 2 | Leave a PayPal donation

    Free Xcode Tools: NSLog Utility | Getter Utility
    GitHub Projects: BTIKit | BTICoreDataKit

    Are you a newbie? Things you should read:
    Definitive Guide To Properties | UITableView Series | A Model (Object) Is A Beautiful Thing
  • wdnwdn Posts: 28Registered Users
    edited December 2009
    Yes, that is a perfectly valid example of a safe setter too. It's always good practice to retain the new value before releasing the old in an assignment, whether that occurs in a setter or elsewhere in your code.

    Sorry, I didn't mean to sidetrack the thread—I just wanted to point out a common memory management mistake that may not be immediately obvious. That's what I meant by common, not necessarily how often the failure occurs. I've been bitten by it before; particularly if you're collaborating with others you want your code to be bulletproof. Without a guard in place, your code may not crash immediately, leading to some elusive debugging if it happens to crash in a distant part of your program. (Hint: use NSZombieEnabled to catch prematurely deallocated objects.)
  • smashersmasher Posts: 3,859Registered Users @ @ @ @ @
    edited December 2009
    wdn wrote: »
    Sorry, I didn't mean to sidetrack the thread—I just wanted to point out a common memory management mistake that may not be immediately obvious. That's what I meant by common, not necessarily how often the failure occurs.

    I can see this happening - for example if you do self.property = [myArray objectAtIndex:chosenIndex] or something similar, you might wind up replacing an object with itself in the property. It's another good reason to @synthesize the setters; writing a good setter is harder than it looks.
    TinyCo is Hiring Mobile Game Programmers (C++)
    http://jobvite.com/m?3Ho5wgwr
  • ChrisLChrisL Posts: 580Registered Users @ @ @
    edited December 2009
    BrianSlick wrote: »
    I'm having a tough time envisioning how putting the same object into a setter again would be even a semi-common occurrence. Sounds like sloppy coding to me.
    Well, the bugs that are really uncommon are often the most dangerous; they're harder to test for, and developers are less likely to know to look for them and know how to deal with them. I've actually seen this exact bug bite a coworker (he didn't implement the setter, but was using it for his current project), and it took him a couple of hours to track it down and fix it. That's a huge amount of company time wasted over something as trivial as passing a value into a setter method.

    This thread offers excellent advice, and so I apologize for sidetracking it a little further, but I think this is worth emphasizing for anyone who is thinking about implementing properties or other built-in functionality manually: the way that property methods behave is an established convention that developers learn and rely on. One aspect of this convention is that setters should have no effect when the current value of the property is passed in (and it certainly shouldn't destroy the object :)). For situations where this is a possibility, like the one Smasher mentioned, I'd argue that relying on the convention actually makes the client code less sloppy -- all of the bookkeeping related to memory management, including making sure an object is not accidentally destroyed, has been completely encapsulated by the property. This frees the calling code from being cluttered with error checking, which would only distract from what the code is actually supposed to do (as well as provide one more place for bugs to creep in).

    To summarize and get this back on topic: Brian's done an excellent job describing how properties work and how they should be used. If you ever decide to implement property methods manually, make sure they adhere to the established conventions, so that developers who do follow Brian's good advice don't get burned by your code.
  • mojomojo Posts: 3New Users
    edited March 2010
    I just want to say "Thanks" for this thread. It really helps.
  • BitzalBitzal Posts: 42Registered Users
    edited March 2010
    Thanks for this! Bookmarked it for a later read! Suspect its gonna be very useful!..
  • bharath2020bharath2020 Posts: 151Registered Users
    edited March 2010
«134
Sign In or Register to comment.