 |
 |
|
 |
12-02-2008, 03:08 PM
|
#1 (permalink)
|
|
Mobile Geek
Join Date: Aug 2008
Location: Florida, USA
Posts: 355
|
Getters, setters and properties for the newbie
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:
Code:
//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:
Code:
//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:
Code:
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:
Code:
[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:
Code:
-(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:
Code:
-(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.
Code:
//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:
[code]
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"
|
|
|
12-02-2008, 03:09 PM
|
#2 (permalink)
|
|
Mobile Geek
Join Date: Aug 2008
Location: Florida, USA
Posts: 355
|
Getters, setters and properties for the newbie, Part II
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
Code:
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:
Code:
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:
Code:
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
Code:
-(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
Code:
-(void)setText:(NSString *)text;
As a result, the two lines in this function have significantly different effects:
Code:
-(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:
Code:
-(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:
Code:
-(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:
Code:
-(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:
Code:
obj = [[SomeObject alloc] init];
instead of
Code:
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:
Code:
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:
Code:
[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.
Last edited by rames44; 12-02-2008 at 03:15 PM.
|
|
|
12-02-2008, 03:48 PM
|
#3 (permalink)
|
|
Mobile Geek
Join Date: Aug 2008
Location: Florida, USA
Posts: 355
|
Getters, setters and properties for the newbie, Part III
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:
Code:
-(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:
Code:
- (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
Code:
- (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:
Code:
- (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
Code:
-(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
Code:
-(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:
Code:
-(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:
Code:
-(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
Code:
- (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.)
|
|
|
12-02-2008, 04:21 PM
|
#4 (permalink)
|
|
Mobile Geek
Join Date: Aug 2008
Location: Florida, USA
Posts: 355
|
Getters, setters and properties for the newbie, Part IV
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:
Code:
@property (nonatomic, readonly) PropertyType propertyName;
Given this,
Code:
something = obj.propertyName;
will work but
Code:
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:
Code:
@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
Code:
@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:
Code:
@synthesize text = _text;
instead of just
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
Code:
@property (nonatomic, assign, getter=isValue) boolean value;
Now you can access the property as:
Code:
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
|
|
|
05-02-2009, 05:35 AM
|
#5 (permalink)
|
|
New Member
Join Date: May 2009
Posts: 2
|
Many thanks!
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.
|
|
|
05-02-2009, 01:02 PM
|
#6 (permalink)
|
|
Registered Member
Join Date: Mar 2009
Location: Switzerland
Age: 34
Posts: 97
|
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.
Last edited by ob1; 05-02-2009 at 01:08 PM.
|
|
|
05-09-2009, 05:02 PM
|
#7 (permalink)
|
|
New Member
Join Date: May 2009
Posts: 2
|
nice!
very good!
|
|
|
05-19-2009, 06:15 AM
|
#8 (permalink)
|
|
New Member
Join Date: Jan 2009
Posts: 60
|
i have a function
Code:
// 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
Last edited by arjit292; 05-19-2009 at 06:32 AM.
|
|
|
05-27-2009, 01:24 AM
|
#9 (permalink)
|
|
New Member
Join Date: May 2009
Posts: 2
|
Quote:
Originally Posted by rames44
... 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
|
|
|
11-05-2009, 07:49 AM
|
#10 (permalink)
|
|
Registered Member
Join Date: Oct 2009
Posts: 4
|
this tutorial is great, but I'm still a bit confused about autorelease stuff:
In this example:
Code:
-(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:
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?
Last edited by Max; 11-05-2009 at 07:54 AM.
|
|
|
11-05-2009, 09:22 AM
|
#11 (permalink)
|
|
Mobile Geek
Join Date: Aug 2008
Location: Florida, USA
Posts: 355
|
Quote:
Originally Posted by Max
this tutorial is great, but I'm still a bit confused about autorelease stuff:
In this example:
Code:
-(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:
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 Biorhythms app
|
|
|
11-05-2009, 10:01 AM
|
#12 (permalink)
|
|
Registered Member
Join Date: Oct 2009
Posts: 4
|
wow, thanks a lot, it's more clear
So I guess it will be like this:
Code:
-(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:
Code:
-(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?
Last edited by Max; 11-05-2009 at 10:29 AM.
|
|
|
11-05-2009, 03:44 PM
|
#13 (permalink)
|
|
Mobile Geek
Join Date: Aug 2008
Location: Florida, USA
Posts: 355
|
Quote:
Originally Posted by Max
wow, thanks a lot, it's more clear
So I guess it will be like this:
Code:
-(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:
Code:
-(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:
Code:
- (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 Biorhythms app
|
|
|
11-05-2009, 05:49 PM
|
#14 (permalink)
|
|
Registered Member
Join Date: Oct 2009
Posts: 4
|
wow, that's awesome explanation, now it's all really clear.
thanks a lot!
|
|
|
11-11-2009, 10:17 AM
|
#15 (permalink)
|
|
iPod Touch 8GB
Join Date: Oct 2009
Location: MY
Age: 32
Posts: 1,604
|
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.
__________________

New & Noteworthy Apr '10
(click icon.. it's a FREE App!)
" ...I decided that Apple can't afford to change its core values and simply let it slide. We have the same core values as when we started, and we come into work wanting to do the same thing today that we wanted to do five years ago."
|
|
|
12-13-2009, 10:10 AM
|
#16 (permalink)
|
|
Registered Member
Join Date: Oct 2009
Posts: 15
|
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.
|
|
|
12-30-2009, 03:49 PM
|
#17 (permalink)
|
|
Registered Member
Join Date: Dec 2009
Posts: 1
|
Why can't all docs be this precise?
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:
Code:
// .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!
|
|
|
03-29-2010, 11:48 PM
|
#18 (permalink)
|
|
Registered Member
Join Date: Aug 2008
Location: Seattle, WA USA
Posts: 397
|
Quote:
Originally Posted by kevinC
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.
|
|
|
04-10-2010, 04:07 PM
|
#19 (permalink)
|
|
Registered Member
Join Date: Mar 2010
Posts: 12
|
incredible article, rames44...
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...
|
|
|
04-16-2010, 02:11 AM
|
#20 (permalink)
|
|
iPhone Developer
Join Date: Nov 2009
Posts: 383
|
Great tutorial, Thank you.
|
|
|
05-21-2010, 05:49 PM
|
#21 (permalink)
|
|
Registered Member
Join Date: May 2010
Posts: 1
|
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:
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.
|
|
|
05-27-2010, 01:16 PM
|
#22 (permalink)
|
|
Registered Member
Join Date: May 2010
Posts: 3
|
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
|
|
|
 |
| Thread Tools |
|
|
| Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is On
|
|
|
|
» Advertisements |
» Online Users: 385 |
| 29 members and 356 guests |
| AdamSubach, airsoft808, anonymous@, benoitr007, bensj, Danneman, Duncan C, gtyt38, gustavo7sexton, HemiMG, Jeremy1026, lifeCoder45, maxus182, mox, Ovidius, Paul10, pofak, qilin, Racker, raheel, Sega dude, squidboy, timle8n1, ufbobbo, ultrayard077, ZunePod |
| Most users ever online was 965, 06-30-2010 at 04:26 AM. |
» Stats |
Members: 41,860
Threads: 49,768
Posts: 213,052
Top Poster: BrianSlick (3,138)
|
| Welcome to our newest member, gustavo7sexton |
|