Advertise here




Advertise here

Howdy, Stranger!

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

Getters, setters and properties for the newbie

rames44rames44 Posts: 365Tutorial Authors, Registered Users @ @
edited February 2012 in iPhone SDK Tutorials
iPhone developers who have experience with Macintosh development have probably long since become comfortable with the whole "property" notion in Objective C. For iPhone developers who are recent converts from other mobile environments (such as Java or Symbian), this can be a bit twistier. In the hopes that some people won't repeat my pain, here's a brief discussion of variables and properties, along with the implications to proper memory management.

Let's start with a simple class:
//MyClass.h file
@interface MyClass: NSObject
{
    NSString *text;
}

-(void) init;
-(void) logText;
@end

//MyClass.m file
@implementation MyClass

- (void)init
{
    text = @"some text";
}

- (void)logText
{
    NSLog(@"%@", text);
}
@end

This is pretty straight-forward. We have a simple class, which has a single member variable named "text" which is a pointer to an NSString. The variable is initialized in the "init" function, and used in the "logText" function.

Now, at present, the value that's stored in "text" is a constant, so we don't have to worry about memory management. In any real application, however, we'd probably want the ability to change the value of "text" at runtime. One way we can do this is to introduce getter and setter methods:
//MyClass.h file
@interface MyClass: NSObject
{
    NSString *text;
}

-(void) init;
-(void) logText;
-(NSString*) text;
-(void) setText:(NSString *)textValue;
@end

//MyClass.m file
@implementation MyClass

- (void)init
{
    text = @"some text";
}

- (void)logText
{
    NSLog(@"%@", text);
}

-(NSString*) text
{
    return text;
}

-(void) setText:(NSString *)textValue
{
    if (textValue != text)
    {
        [textValue retain];
        [text release];
        text = textValue;
    }
}

-(void)dealloc
{
    [text release];
    [super dealloc];
}

@end

So, we've done the following:

First, we've added a member function that allows us to retrieve the current text value like so:
    NSString *theTextValue = [obj text];
(assuming that "obj" was an instance of MyClass that we previously obtained). This function is pretty obvious - it just returns the current text value.

Second, we've added a member function that allows us to change the current text value like so:
    [obj setText: newStringValue];
(assuming that "obj" is an instance of MyClass and "newStringValue" is an instance of NSString).

Now, we can't be certain that the string value that will be passed in will be a constant - it may be any NSString value, including ones that have been allocated off the heap. Because of this, we need to "retain" the object that we're being passed to ensure that it survives for at least as long as the instance of MyClass does. Similarly, since we're not going to be holding onto the old value that's being replaced, we need to release that value. The "if" in "setText" just cuts out unnecessary work in the case where the object being set is the same object as the one we're holding onto. Such things can happen on occasion depending on how the code is used, so it's always best to consider these cases. Of course, we could have just written:
-(void) setText:(NSString *)textValue
{
    [textValue retain];
    [text release];
    text = textValue;
}
This would have worked just as well in the "same object case," because we would first increment the retain count and then decrement it. Note that the following code is wrong:
-(void) setText:(NSString *)textValue
{
    [text release];
    [textValue retain];
    text = textValue;
}
In this case, in the situation in which the "input" value and the "held" value happened to point to the same instance of NSString, the string would end up being deallocated - the first line would probably decrement the retain count to zero, resulting in the object being returned to the heap. The second line would then try to retain this now-dead object. Bad mojo. Not necessarily likely, but it's a bug waiting to happen someday.

Finally, note that we've implemented a "dealloc" function in which we release the text object. Basically, when we die, we need to return all our books back to the library, so if we're holding onto something, we need to release it.

It's worth pointing out that all of this works perfectly well if the value set is "nil." Objective C has a wonderful feature that allows you to send messages (such as release and retain) to "nil" without things blowing up. This is similar to the fact that you can "delete" a null pointer in C++ without harm.

Now, just for fun, we're going to add a second member variable, along with its getters and setters. We'll see why in a minute.
//MyClass.h file
@interface MyClass: NSObject
{
    NSString *text;
    int value;
}

-(void) init;
-(void) logText;
-(NSString*) text;
-(void) setText:(NSString *)textValue;
-(int) value;
-(void) setValue:(int*)intValue;
@end

//MyClass.m file
@implementation MyClass

- (void)init
{
    text = @"some text";
    value = 2;
}

- (void)logText
{
    NSLog(@"%@", text);
}

-(NSString *) text
{
    return text;
}

-(void) setText:(NSString *)textValue
{
    if (textValue != text)
    {
        [textValue retain];
        [text release];
        text = textValue;
    }
}

-(int) value
{
    return value;
}

-(void) setValue:(int)intValue
{
    value = intValue;
}

-(void)dealloc
{
    [text release];
    [super dealloc];
}

@end

Here, we've simply added in integer member variable. We have the same kind of getter and setter methods as before. Here, however, we don't have to do the "retain" and "release" stuff, because the integer isn't allocated off the heap - it's just stored within MyClass. So, now we can do:
    NSString *s = [obj text];
    [obj setText:@"new string"];
    int i = [obj value];
    [obj setValue:3];
[code]

and so forth.

Now, getters and setters like this are so incredibly common that the folks that build Objective C probably got tired of writing the same methods over and over.  They're also incredibly cookie-cutter - the same lines over and over again.  As a result, they provided some shortcut methods that will write these methods for us.  Let's take advantage of this by rewriting the code as:
[code]
//MyClass.h file
@interface MyClass: NSObject
{
    NSString *text;
    int value;
}

@property(nonatomic, retain) NSString *text;
@property(nonatomic, assign) int value;

-(void) init;
-(void) logText;
@end

//MyClass.m file
@implementation MyClass

@synthesize text;
@synthesize value;

- (void)init
{
    text = @"some text";
    value = 2;
}

- (void)logText
{
    NSLog(@"%@", text);
}

-(void)dealloc
{
    [text release];
    [super dealloc];
}

@end

Here's what we've done:

First, we've removed the definitions of the two getters and two setters from the MyClass.h file. Then, we've added two lines starting with "@property".
In MyClass.m, we've removed the implementation of the two getters and two setters, and added two lines starting with "@synthesize".

This block of code is functionally identical to the previous block of code. If you think about it, writing a getter is a no-brainer. All you need to know is what variable you're returning and what type it is. Given that information, it's easy to crank out the code. Similarly, for a setter, all you need to know is what variable you're setting, what type it is, and whether you're going to be simply assigning the value (as in the "int" case) or whether you're going to be going through the retain/release procedure (as in the "NSString" case).

Thus, the four "@" lines translate as follows:

@property(nonatomic, retain) NSString *text; translates as
"I have a member variable of type NSString* named 'text'. I will want a getter/setter pair that uses the retain/release procedure."

@property(nonatomic, assign) int value; translates as
"I have a member variable of type int named 'value'. I will want a getter/setter pair that doesn't use the retain/release procedure - just assign it."

@synthesize text; translates as
"Please automatically create the code for the getter and setter for 'text'"

@synthesize value; translates as
"Please automatically create the code for the getter and setter for 'value'"


Note that we are still responsible for doing the "release" in the "dealloc" method. Unfortunately, the Objective C gurus apparently didn't come up with a way to write that code for us.


Part II of this moves on to "property notation"
Post edited by rames44 on
For a little fun, check out my <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=298866985&mt=8" target="_blank">Biorhythms</a> app
«1

Replies

  • rames44rames44 Posts: 365Tutorial Authors, Registered Users @ @
    edited December 2008
    So now we've simplified the process of writing our source code - we don't have to write those tedious getter and setter methods manually - the compiler can automagically generate them given the information that we've provided it.

    That wasn't enough for the Objective C gurus, however. After a while, I guess they got tired of writing code like
        NSString *s = [obj text];
        [obj setText:@"new string"];
        int i = [obj value];
        [obj setValue:3];
    
    As a result, along with the autogenerated code feature we just discussed, the property stuff introduced a new notation:
        NSString *s = obj.text
        obj.text = @"new string";
        int i = obj.value;
        obj.value = 3;
    
    Basically, since the @property and @synthesize lines gave the compiler enough information to generate the getters and setters, it also gave it enough information to know how to call them. Thus, the four lines just above are absolutely functionally identical to the four lines of code just before that. Under the hood, the exact same thing happens - calls to getters and setters - you the programmers just get to write code that's a little more concise.

    Now, this is where the slightly twisty bit comes in particularly for Java programmers. In Java, many people will write a function like this:
    void doSomething(String text)
    {
        this.text = text;
    }
    
    In this Java function, "text" is a local variable, while "this.text" is a member of the class that happens to have the same name. If you were to try to directly translate this into Objective C, you might come up with
    -(void)doSomething:(NSString *)text
    {
        self.text = text;
    }
    
    In this case, however, "self.text" is not a direct reference to the member variable "text" the way it is in Java. Instead, it is shorthand for a function call to the method
    -(void)setText:(NSString *)text;
    
    As a result, the two lines in this function have significantly different effects:
    -(void)doSomething:(NSString *)input
    {
        text = input;        //direct variable assignment
        self.text = input;  //call to the setter
    }
    
    It may not be obvious at first that these two lines have different results. Indeed, if we were dealing with an integer property that would, in fact, be the same. When we're dealing with a retained item like a string, however, the first line will overwrite the pointer to the string without releasing any previous value or retaining the new one while the second line will release the old value and retain the new one. Obviously a very different thing.

    As a result of this, it is almost always better to use the "self.text =" form instead of the "text =" form - the latter is quite likely to generate memory leaks. To use the "text=" form properly, one would have to do:
    -(void)doSomething:(NSString *)input
    {
        [input retain];
        [text release];
        text = input;
    }
    
    in order to make sure that the memory was handled properly. But guess what? This is essentially identical to the setter that was automatically generated for us, so why not just do it as:
    -(void)doSomething:(NSString *)input
    {
        self.text = input;
    }
    
    and save the aggravation and potential for bugs.

    Carrying this a bit further, this is why you see code that looks like this so often:
    -(void)doSomething
    {
        SomeObject *obj = [[SomeObject alloc] init];
        self.obj = obj;
        [obj release];
    }
    

    The first line creates an object. Since we "alloc'd and init'd" the object, it now has a retain count of one. Then we call the setter in the second line. The setter was presumably defined to retain the object, so that makes the retain count two. Then the third line releases the object once. This balances out the implicit retain in the first line, leaving the retain count at one, meaning that the only reference to the object is by the owner.

    It is almost always best to code this way, because it helps you make sure that you're balancing out retains and releases, thus preventing memory leaks. The two fundamental rules are:
    1) If you allocate it, create it or copy it, you need to release it.
    2) If you retain it, you need to release it.

    As far as rule #1 goes, it's easy to see that the "alloc" and "release" are balanced out when you write code like this.

    As far as rule #2 goes, the "retain" that's automatically generated inside the setter is balanced out in one of two places:
    a) By the generated "release" if the setter is called again, or
    b) By the "release" that you wrote into your "dealloc" function.

    Now, having said all that, there is one place in which writing "direct assignment" code, as opposed to "create/setter/release" code is perfectly fine, and that's inside an "init" function. Your "init" function is supposed to get called only once - immediately after an object has been allocated. As such, you know that all the member variables are "nil" (or zero). As such, you don't have to be paranoid about making sure that a previous value is released. Thus, in your init function, it's perfectly fine to write:
        obj = [[SomeObject alloc] init];
    
    instead of
        SomeObject *obj = [[SomeObject alloc] init];
        self.obj = obj;
        [obj release];
    
    Both sets of lines have the same effect - a member variable named "obj" is left holding an object with a release count of one. Thinking back to rule #2 above, the implicit "retain" caused by the "alloc/init" line is balanced out by the "release" we wrote into the "dealloc" function.

    So what would happen if you wrote:
        self.obj = [[SomeObject alloc] init];
    
    In this case, you're holding onto an object with a retain count of two - the first count comes from the "alloc" and the second one is added by the setter.

    To release this variable, you'd have to do something like this:
        [obj release];
        self.obj = newValue;
    
    so that "release" gets called twice on the object. If you omit the extra "release", then when the pointer gets overwritten the object will still be floating around with a retain count of one, and thus doesn't get deallocated. Instant memory leak.
    For a little fun, check out my <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=298866985&mt=8" target="_blank">Biorhythms</a> app
  • rames44rames44 Posts: 365Tutorial Authors, Registered Users @ @
    edited December 2008
    So, now that we've written and used getters and setters and looked at property notation, let's look at a few related items.

    Why do we have to specify @synthesize propertyName;? Why doesn't the compiler just generate it for us automatically?

    There are two good answers to this.

    First, the compiler may not know where to generate the code. It's possible that the implementation of your class is split across several source files. If you write @synthesize propertyName; into one of them, the compiler knows that now's the time. (Of course, if you wrote the same line into more than one file, you'd generate multiple functions with the same name, and Xcode would probably start generating little red marks.) It's possible, of course, that the Objective C gurus could have figured out a way to handle this if this was the only issue.

    Second, and perhaps more importantly, maybe you want to use the property "dotted" notation, but the code that the compiler would automatically generate won't cut it for you. Suppose that we wanted to link "text" and "value" so that any time you set "value" it would change "text" to match, but you also wanted to use the "dot" notation to access either. Thus, you want the setter for "value" to look something like this:
    -(void) setValue:(int)intValue
    {
        value = intValue;
        self.text = [NSString stringWithFormat:@"%d", intValue];
    }
    
    Now, there's no way that Objective C can read your mind (at least in ObjC 2.0 - you may have to wait for 3.0 for this feature). Thus, the gurus give you another option: you can declare the "value" property exactly the same way, but provide your own getters and setters. You do this by giving Objective C
    @dynamic propertyName; instead of
    @synthesize propertyName;

    "@dynamic" basically tells Objective C that you want it to translate the "object.property" notation into calls to "[obj property]" and "[obj setProperty:x]" the same way it did, but that you will provide the implementation for "property" and "setProperty:" instead of having them automatically generated.

    Every now and then being able to do this is convenient.

    What's this autorelease stuff?

    Remember rule #1 from the previous part - if you allocated it, you're responsible for releasing it. There's a problem, however. Sometimes you need to create something and return it so that someone else can use it. You can't do:
    - (MyObject *)getObject
    {
        MyObject *returnValue = [[MyObject alloc] init];
        [returnValue release];
        return returnValue;
    }
    
    After the first line, the object has a retain count of one. When you release it in the second line, the retain count drops to zero, so it will get deallocated before you ever get a chance to return it.
    But you also can't do
    - (MyObject *)getObject
    {
        return [[MyObject alloc] init];
    }
    
    because you've broken rule # 1 - you allocated it, but you didn't release it. What you really need to be able to do is tell Objective C "I'm responsible for this object, so I want to release it, but I want the release to be delayed until whoever asked for this object has had a chance to use it."

    This is the function of the autorelease pool.

    The correct way (or at least one correct way) to write the function above is:
    - (MyObject *)getObject
    {
        return [[[MyObject alloc] init] autorelease];
    }
    

    What happens is that you allocate and init the item, and then call "autorelease" on it. The system reacts to this by making an entry in a "deferred release" pool or, more correctly, in the "autorelease pool." This entry guarantees that the pool will call "release" on the object at a later date, thus satisfying your responsibility. In the meantime, however, the code that called this function can:
    1) Use the object (because it hasn't actually been released for the final time yet) and then just forget about it. The calling code didn't allocate the object, so it's not responsible for releasing it (rule #1).
    2) Retain the object. In this case, the retain count gets incremented, so when the automatic "release" happens later, the count will only get decremented to one, and the object will hang around. This makes the caller responsible for the ultimate dealloc-ing "release," based on Rule #2.

    So when does the object actually get released? In the iPhone implementation, an autorelease pool is created for each message that is processed by the system and the objects in the pool are all automatically released after processing on that message has completed. Thus, when your code returns back to the operating system, any autorelease objects that haven't been otherwise retained will go poof at that point. If you want them to hang around any longer than that, you need to retain them yourself.

    If you remember our earlier example
    -(void) setValue:(int)intValue
    {
        value = intValue;
        self.text = [NSString stringWithFormat:@"%d", intValue];
    }
    
    in this case, the string object that stringWithFormat: returns has been autoreleased before being returned to us. By assigning it to "self.text", however, the "text" setter retains it. As a result, the value will stick around. If we had done something like this, however
    -(void) logValue:(int)intValue
    {
        NSLog(@"%@", [NSString stringWithFormat:@"%d", intValue]);
    }
    
    then the we would be using the autoreleased string and then letting the autorelease pool clean up after stringWithFormat: - the string object that it returns would be deallocated some time after our function returns.

    Now, we talked earlier about writing code like this:
    -(void)doSomething
    {
        SomeObject *obj = [[SomeObject alloc] init];
        self.obj = obj;
        [obj release];
    }
    
    The presence of the autorelease pool means that it is equally valid to write this code like this:
    -(void)doSomething
    {
        SomeObject *obj = [[[SomeObject alloc] init] autorelease];
        self.obj = obj;
    }
    
    In the first case, the "alloc" on line 1 was balanced out by the "release" on line 3. In the second case, the "alloc" was balanced out by the (deferred) release from the "autorelease".

    So which is better? While either is technically correct, Apple indicates that the second form is slightly less efficient than the first - there's overhead associated in getting hold of the current autorelease pool, making an entry in it and then later having the pool do the release. In the case of
    - (MyObject *)getObject
    {
        return [[[MyObject alloc] init] autorelease];
    }
    
    we really don't have any choice but to suffer that overhead, but in the case where we have both ends of the deal, it doesn't hurt to use the slightly more efficient way of doing things. (And it's just about the same number of keystrokes - maybe just a few more.)
    For a little fun, check out my <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=298866985&mt=8" target="_blank">Biorhythms</a> app
  • rames44rames44 Posts: 365Tutorial Authors, Registered Users @ @
    edited December 2008
    I like the "dotted" notation, but I want to have a read-only property - in other words, a getter but no setter.

    Quite simple. When you declare the property, do it as:
    @property (nonatomic, readonly) PropertyType propertyName;
    

    Given this,
        something = obj.propertyName;
    
    will work but
        obj.propertyName = something;
    
    won't.

    If you want to explicitly denote that something is read/write you can use the readwrite keyword, but this is the default, so it's rarely used.

    What's the nonatomic bit you keep using?

    By default, when Objective C synthesizes getters and setters, it makes the worst-case assumption - that these methods are going to get used in a multi-threaded system. As such, it generates extra internal code to manage the case when two threads call one of these methods at the same time, or that one thread is calling a setter as another is calling a getter.

    Unless you're explicitly using multiple threads in your application, all calls to your object will be made from the same thread, and so you don't have this particular issue. By including nonatomic in the property declaration, you cause Objective C to generate simpler, more efficient getters and setters.

    Of course, if you are coding a multithreaded app, you have to watch out for this kind of thing. If an object's properties can be accessed from multiple threads at the same time, then you can get the more robust code by leaving the nonatomic keyword off.

    I like to code my member variables with a prefix so that I don't confuse them with local variables. What does this do to my property names?

    Let's suppose that you like to use an underscore prefix on member variables, as shown below:
    @interface MyClass: NSObject
    {
        NSString *_text;
    }
    
    -(void) init;
    -(void) modifyText:(NSString*)text;
    @end
    
    //MyClass.m file
    @implementation MyClass
    
    - (void)init
    {
        _text = @"some text";
    }
    
    -(void) modifyText:(NSString *)text
    {
        // do something with "text" and "_text"
    }
    @end
    
    Now you'd like to expose the "_text" value as a property, but without the underscore.
    You write the property declaration in the same way as normal, but with the "public" name, not the variable name
    @property(nonatomic, retain) NSString *text;
    
    since this part of the declaration is telling the world what property names can be used in the rest of the software.

    In the implementation, however, you write:
    @synthesize text = _text;
    
    instead of just
    @synthesize text;
    
    This tells Objective C to synthesize a property named "text", but to wire it to the variable named "_text" instead of one named "text".

    I like my boolean properties to be isValue instead of just value, but I still want the setter to be value.

    No problem. The property declaration allows you to manually specify the name of the getter and setter if you want. In this case you can write
    @property (nonatomic, assign, getter=isValue) boolean value;
    

    Now you can access the property as:
        if (obj.isValue) ...
        obj.value = YES;
    

    There is a corresponding "setter=name" directive as well if you have need to rename the setter.

    Are retain and assign my only options?

    No, there is a third option - copy. This specifies that, instead of retaining the object passed in, the setter will make a copy of the object (implicitly retaining it at the same time). This is only valid for object types (not for int's, boolean's, etc.), and the object that is passed in must implement the NSCopying protocol.

    "Copy" has some special implications if you're using it on objects that you want to be mutable - the "copy" operation usually returns an immutable object. This is covered in more detail in The Objective-C 2.0 Programming Language: Declared Properties
    For a little fun, check out my <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=298866985&mt=8" target="_blank">Biorhythms</a> app
  • stevej2kstevej2k Posts: 2New Users
    edited May 2009
    I'd just like to say a quick thank you for writing this article, it was extremely well described, clear and very informative.

    I'm sure its going to help clarify a few things for other beginners like me.
    Many thanks once again.
  • ob1ob1 Posts: 97Registered Users
    edited May 2009
    Very good explanation. Thank you! I wish I had seen it when I needed it :-D

    I think it should be a sticky by the way.
    Isometric Traffic Management Game with stunning artwork:<br />
    <br />
    <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=329821720&mt=8" target="_blank">AutoTrafego</a>
  • arjit292arjit292 Posts: 60Registered Users
    edited May 2009
    i have a function
    // this is the header 
    @interface MyClass
    {
        theclass * obj;
    }
    
    
    // this is the .m file 
    -(void) doSomething{
        obj=[[theclass alloc] init];
    }
    
    -(void)something{
        theclass=nil;
        obj=[[theclass alloc] init];
    }
    

    so in something function it is getting a bad memory error how do this ?
    i need to init the same object again and again ....
    how do i do that again ?
    any help
  • kevinCkevinC Posts: 2New Users
    edited May 2009
    rames44 wrote: »
    ... we really don't have any choice but to suffer that overhead, but in the case where we have both ends of the deal, it doesn't hurt to use the slightly more efficient way of doing things. (And it's just about the same number of keystrokes - maybe just a few more.)

    I wrote the developer tool Accessorizer because I was tired of writing the same code over and over again. Even writing @property and @synthesize statements grew tedious!

    iPhone developers will want to check out the videos at this site demonstrating how Accessorizer saves you a ton of time and helps eliminate mistakes.

    Accessorizer

    Some testimonials: Testimonials
  • MaxMax Posts: 4New Users
    edited November 2009
    this tutorial is great, but I'm still a bit confused about autorelease stuff:

    In this example:
    -(void)doSomething
    {
        SomeObject *obj = [[[SomeObject alloc] init] autorelease];
        self.obj = obj;
    }
    


    When does autorelease get called on *obj value? After doSomething method ends or in the current class dealloc?

    Then again, in this code:
    -(void) logValue:(int)intValue
    {
        NSLog(@"%@", [NSString stringWithFormat:@"%d", intValue]);
    }
    

    When does stringWithFormat value get destroyed? After logValue ends or somewhere after current class dealloc?
  • rames44rames44 Posts: 365Tutorial Authors, Registered Users @ @
    edited November 2009
    Max wrote: »
    this tutorial is great, but I'm still a bit confused about autorelease stuff:

    In this example:
    -(void)doSomething
    {
        SomeObject *obj = [[[SomeObject alloc] init] autorelease];
        self.obj = obj;
    }
    


    When does autorelease get called on *obj value? After doSomething method ends or in the current class dealloc?

    Then again, in this code:
    -(void) logValue:(int)intValue
    {
        NSLog(@"%@", [NSString stringWithFormat:@"%d", intValue]);
    }
    

    When does stringWithFormat value get destroyed? After logValue ends or somewhere after current class dealloc?

    Basically, "autorelease" causes the object to be added to a system-managed list of objects that need to be released. The actual release typically happens after the current message is processed. Conceptually, the message handler does:
    1) Set up the autorelease pool
    2) Call the message handler
    3) Delete any objects that got put into the autorelease pool during message processing.

    Thus, an object on which autorelease has been called will be "alive" for the rest of the current message handler, but won't survive past that unless it's retained somewhere.

    In your first example, the "self.obj = obj" call (assuming that the property is set up to "retain") will cause a "retain" call on the object. Thus, the "use count" on that object will be 2 - one from your original alloc/init, and one from the retain. At the end of the message processing, since you called autorelease on "obj," the autorelease pool will call "release" on "obj", which will decrement the use count to 1. The object will not be dealloc'd, because it's use count is non-zero - your class is still holding onto it. Later on, when that object is replaced or the holding object is destroyed, a second release call will be made on "obj", which will decrement the use count to zero and cause it to be dealloc'd.

    "stringWithFormat" is a great example of where autorelease is useful. The rule "if you alloc it, you need to release it" works great for objects that you create, but what about factory methods? In this case, "stringWithFormat" creates a string and calls autorelease on it before returning it to you. Thus, unless you retain the string, it will get released at the end of the current message handler. Using this paradigm means that if you get an object from anything other than an alloc/init method, you don't have to worry about whether or not to release it. If the factory method means the object to be ephemeral, it will have called autorelease on it for you. If not, then the factory has probably retained it, and will be responsible for releasing it at the appropriate time. Essentially, the normal "lifecycle" of that object is determined by the factory. (Although, of course, you can extend the life of such an ephemeral object by retaining it yourself and releasing it later.)

    Hope that helps....
    For a little fun, check out my <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=298866985&mt=8" target="_blank">Biorhythms</a> app
  • MaxMax Posts: 4New Users
    edited November 2009
    wow, thanks a lot, it's more clear

    So I guess it will be like this:
    -(void)doSomething
    {
     SomeObject *obj = [[[SomeObject alloc] init] autorelease]; // count 1
     self.obj = obj; // count becomes 2
    
    //( can put some additional code here)
    
    } // count back to 1
    

    and in second case:
    -(void) logValue:(int)intValue
    {
        NSLog(@"%@", [NSString stringWithFormat:@"%d", intValue]); // count 1
    
    // (some additional code)
    // value is still there somewhere with count 1
    
    } // count becomes 0, value destroyed
    
    

    is it correct?
  • rames44rames44 Posts: 365Tutorial Authors, Registered Users @ @
    edited November 2009
    Max wrote: »
    wow, thanks a lot, it's more clear

    So I guess it will be like this:
    -(void)doSomething
    {
     SomeObject *obj = [[[SomeObject alloc] init] autorelease]; // count 1
     self.obj = obj; // count becomes 2
    
    //( can put some additional code here)
    
    } // count back to 1
    

    and in second case:
    -(void) logValue:(int)intValue
    {
        NSLog(@"%@", [NSString stringWithFormat:@"%d", intValue]); // count 1
    
    // (some additional code)
    // value is still there somewhere with count 1
    
    } // count becomes 0, value destroyed
    
    

    is it correct?

    That's a reasonable way of thinking about it, I suppose, but it's not QUITE accurate. Strictly, the decrementing of the count doesn't happen at the end of the method, but at the end of the event processing - the highest level call from the event loop into your code. For example, consider the following:
    - (void) handleButtonPress {
      NSString *myString = [self getMyString:2];
      ...do something with myString...
    }
    
    - (NSString *) getMyString:(int)intVal {
     return [NSString stringWithFormat:@"%d", intValue];
    }
    

    Here "handleButtonPress" is wired up to be called when a button is pressed. So the sequence is (very loosely):

    1. Event processor pulls the "button press" event off the event queue
    2. Event processor sets up an autorelease pool
    3. Event processor calls "handleButtonPress"
    4. "handleButtonPress" calls "getMyString"
    5. "getMyString" calls [NSString stringWithFormat]
    6. [NSString stringWithFormat] creates a string (use count is now 1)
    7. [NSString stringWithFormat] calls "autorelease", placing the string in the autorelease pool, then returns it to "getMyString"
    8. "getMyString" now has the string with a use count of 1
    9. "getMyString" returns the string to "handleButtonPress" (still has a use count of 1)
    10. "handleButtonPress" fiddles with the string, but doesn't retain it
    11. "handleButtonPress" returns back to the event processor code (use count on the string is still 1)
    12. The Event Processor now releases the autorelease pool. The pool reacts to this by executing a "release" on every object that's been placed in the pool. As a result, the string's use count is decremented to 0, and, as a result, the string is dealloc'd.

    You see the distinction, I'm sure - it's not leaving the method that created the string that causes the "autorelease" to be triggered, it's completing the entire handling of the individual event call, because at the end of that process, the event loop explicitly dumps the pool. The object remains valid from the time it's created until you return back out of your code back to the OS - only then is it autoreleased. Thus, this isn't like a destructor in C++ that gets triggered when an object goes out of scope - it's a mechanism supported by the language, but which has to be supported, in turn, by the code in the environment.

    As one example of what I mean by that last sentence, if you create your own thread to do background work, you have to explicitly create an autorelease pool as one of your first steps, and then release the autorelease pool (which, in turn, causes all the contained objects to be released) before your thread ends. If you don't do this your code goes BOOM as soon as you create anything involving an autorelease, since there's no pool into which the object can be placed. (Autorelease pools are thread-specific.) In "normal code", the event processing loop is what takes care of setting up and tearing down the pools for you, so you're not really aware that this is going on.

    Hopefully that didn't muddy the water too much, but I always think that understanding the "magic" is good in the long run.
    For a little fun, check out my <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=298866985&mt=8" target="_blank">Biorhythms</a> app
  • MaxMax Posts: 4New Users
    edited November 2009
    wow, that's awesome explanation, now it's all really clear.

    thanks a lot!
  • rocotilosrocotilos Posts: 3,264iPhone Dev SDK Supporter, Registered Users @ @ @ @ @
    edited November 2009
    Thanks for this post. Helps a lot especially to a noobie like me. Thanks again!! I cant thank enough. Only can offer my prayers for u to get huge sales of ur apps. :)
  • RemyRemy Posts: 15Registered Users
    edited December 2009
    Thank you! I wish the Objective C gods would descend from their lofty kingdom and impart us mere mortals with with such powerful (mortal written) knowledge. Thank you for taking the time.
  • SoundOfEmotionSoundOfEmotion Posts: 1New Users
    edited December 2009
    I'm joining in with the others on this thread and giving you a round of applause, and maybe a hi-5, for taking the time to put this documentation together. It took me a good half hour or so to read through, and I'm sure it took you at least thrice that long!

    Last night I was playing with timers and I had an issue when trying to set a member value:
    // .h eader
    ...
    NSTimeInterval *startTime;
    ...
    
    // .m plementation
    ...
    startTime = [NSDate timeIntervalSinceReferenceDate];
    ...
    

    For the life of me, I couldn't figure out why the app would crash whenever I attempted to set a value for startTime. I found a doc that briefly explained @property and @synthesize which gave me the insight I needed to get my app running. I knew that it worked, but I didn't know WHY it worked.

    Now I do! Your explanation was extremely thorough and helped me (and others) tremendously! I'll be checking your other posts to see if I find any other juicy iPhoneDev morsels. Cheers!
  • ethanwaethanwa Posts: 645Registered Users @ @ @
    edited March 2010
    kevinC wrote: »
    I wrote the developer tool Accessorizer because I was tired of writing the same code over and over again. Even writing @property and @synthesize statements grew tedious!

    iPhone developers will want to check out the videos at this site demonstrating how Accessorizer saves you a ton of time and helps eliminate mistakes.

    Accessorizer

    Some testimonials: Testimonials

    Freaking AWESOME. Buying it now.
  • FatFattoFatFatto Posts: 17Registered Users
    edited April 2010
    rames44, that must be one of the best-written articles ever, on any topic, in all of Western civilisation.

    You took on the hardest possible topic and produced the greatest possible explanation!

    This is now the "reference' article...
  • MozyMacMozyMac Posts: 609Registered Users
    edited April 2010
    Great tutorial, Thank you.
    <div align="center"><a href="http://mozymac.com/forums" target="_blank">Free Mac Video Tutorials and Online Help</a></div><div align="center"><a href="http://bit.ly/qSqPrz" target="_blank">Steve Job's Most Famous Quotes</a> | Steve Jobs 1955-2011, RIP<br
  • bittenapplebittenapple Posts: 1New Users
    edited May 2010
    Thank you for the tutorial.

    Can anyone please, oh sweet pain, please take me out of my misery by writing a simple example on how the heck you pass a number (int value) which gets created in 1 .m file to another .m file.

    In the apple demo application called QuartzDemo, there is a file called QuartzImages.m

    This file has the following line of code:
    CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1);
    
    Notice the (pdf, 1) in that line. This number should be replaced with an integer variable.

    Now, there is also a file called MainViewController.m.
    In that file, there is a method? called -(void)viewDidLoad

    In that method, I want to assign a number to the integer variable which would replace the damn "1" with whatever number I want.

    I have been pulling my hair trying to get this done, reading beginning iPhone 3 Development book, dev documentation and God knows what not, without results.

    Any help would be greatly appreciated...

    Sigh.
  • RagnarogRagnarog Posts: 3New Users
    edited May 2010
    Total nub here but maybe you're looking at using a global variable for that int?

    Second thing that might work is something like say you have class1.m and class2.m you'd call a method -doSomething:(int)anInt in class1 from class2 with: class1 doSomething:1 ... etc.

    Well.. at least I think so..

    Rag
  • tootensetootense Posts: 3New Users
    edited August 2010
    rames44,

    I just read the setter and getters. Great job! Thank you.
  • kiranthoratkiranthorat Posts: 1New Users
    edited September 2010
    Feels like I didn't no a thing... nice keep up the good work.
  • aikhanaikhan Posts: 9Registered Users
    edited October 2010
    Just wanted to say Thank you for writing this, it really cleared some of the ambiguities I had in my mind.

    Keep on writing keep on educating.

    :)

    Education will set us free.
  • FatFattoFatFatto Posts: 17Registered Users
    edited October 2010
    As many people have said, this is basically one of the best articles ever written, on any topic - ever - anywhere!

    Rames - you know you're good - you are good!!! I can only add my thanks.

    One suggestion, "Note that we are still responsible for doing the "release" in the "dealloc" method..."

    Perhaps you should enlarge on this slightly so that people have the definitive explanation in one place; i.e. one would not (cannot) release unless it is of such and such type, etc. So, I suggest a small new subsection "releasing properly in dealloc".

    Again I and everyone else can only say Thank You.
«1
Sign In or Register to comment.