I am a little curious about the last lines in the two examples presented below (i.e. planetName = [value ??????]) My understanding is that the 1st example with the copy is best as that takes a copy of the string object to protect against the original string object being changed elsewhere.
I am also a little confused by the last line in the 2nd example, again my understanding was that the value object was being passed into the method, I guess I am confused as value is being retained with no associated release? Can someone set me straight?
- (void)setPlanetName:(NSString *)value {
if (planetName != value) {
[planetName release];
planetName = [value copy];
}
}
.
- (void)setPlanetName:(NSString *)value {
if (planetName != value) {
[planetName release];
planetName = [value retain];
}
}
Given:
- (void)setPlanetName:(NSString *)value {
if (planetName != value) {
[planetName release];
planetName = [value copy];
}
}
The -copy ensures that if someone passes in an instance of NSMutableString as value, then planetName won't change in your instances out from under you.
A good defensive programming pattern. Note that -copy is free on immutable strings; -copy just bumps the retain value and returns self (the instance of the string that was copied).
Now, consider your second example:
- (void)setPlanetName:(NSString *)value {
if (planetName != value) {
[planetName release];
planetName = [value retain];
}
}
Perfectly valid and works fine, just not as defensive.
In both cases, planetName must be released in your -dealloc method.
- (void) dealloc
{
[planetName release];
planetName = nil; // defensive
[super dealloc];
}
It doesn't matter if the string is copied, retained, or was originally a constant string that was passed into your setter. If you retain it (or implied retain it through copy), you must release it.
Note that you can think of this as "escape patterns". Whenever the existing value of planetName escapes your object, you must release it. That can happen when the object is deallocated or when a new value of planetName is set, hence the -release in the setters.
Or, if on Mac OS X, you could turn on garbage collection and be done with it. In any case, you should be using #property & #synthesize to automatically generate the getter/setter pair.
in the class's dealloc method, there should be another [planetName release] there, which will handle releasing of any instance variables. If that is present then there is no memory leak to worry about.
As for your other question, yes copy is used if you potentially have a mutable object that you don't want to allow other code to be able to modify what your class expects. You are expected to release any object that you call copy on, as it returns something with a retain count of 1. Also, in the case of immutable objects (like NSStrings vs. NSMutableStrings) copy just calls retain since there is no need to make a full copy of something that is immutable.
Related
Here is implement of a setter method:
- (void)setCount:(NSNumber *)newCount {
[newCount retain];
[_count release];
// Make the new assignment.
_count = newCount;
}
If retainCount of _count is <=0, how it can release ?
The only valid object that can ever have a retain count of 0 is nil. And sending any message to nil just returns 0 without doing anything else, so that case is covered.
If you mean "how can this work with a deallocated object" — well, it can't. And a deallocated object's retain count isn't really 0, because the object doesn't exist anymore — it's been destroyed and is now just a chunk of memory — so it doesn't have any attributes. Doing anything with a deallocated object is invalid and what will happen is undefined.
Imagine a class with a retainCount instance variable.
#implementation MyClass
{
NSUInteger retainCount;
}
- (id) retain {
retainCount++;
return self;
}
- (void) release {
if (retainCount > 1)
retainCount--;
else
[self dealloc];
}
...
#end
Once an object is deallocated, it is dead, gone, done for, etc... Thus, there is no point in ever decrementing the retainCount to 0 because, by definition, the object is going to be deallocated and working with a deallocated object is undefined behavior.
The above is the exact logic of NSObject, but a completely different implementation (you really wouldn't want to see NSObject's actual implementation -- it is quite painful).
The other source of confusion appears to be what a reference means.
NSObject *foo;
char *bar;
NSUInteger baz;
For all intents and purposes, the above three variable declarations behave identically [in manual retain/release].
When you say bar = "Hello, World!";, you are telling the compiler 'copy the address of the memory that holds the string "Hello, World!" into the memory named bar". Same for foo, only you are copying the address of memory that holds an instance of the class NSObject.
Now, baz may seem different. But it really isn't except that it holds numbers, not addresses. But, really, an address is a number!
So, in a setter::
- (void)setCount:(NSNumber *)newCount {
// increment newCount's retain count
[newCount retain];
// decrement the _count's retain count (which may cause it to be deallocated or not)
[_count release];
// copy the address of the memory that holds the NSNumber instance referenced
// by `newCount` into the instance variable `_count`.
_count = newCount;
}
There is nothing magical about that assignment [under manual retain release]. It is just copying a number from one variable to the other. The objects are not impacted at all by this.
I bet that I could find the answer of this question from reading similar threads here or by googling, but I would like "hear" it first hand so to speak because it's an anomaly in my understanding.
So here's the thing, I have some code which a previous employee wrote and I see a lot of a certain type of construct which looks rather odd to me and I just want to clarify what is "right and wrong".
For example
- (void) setWwanServiceId: (NSString *) newValue {
[wwanServiceId autorelease];
wwanServiceId = [newValue copy];
}
Here wwanServiceIdis a NSString member of the class, and to me this seem like a strange way to do it. As far as I understand it would begin by putting an autorelease on the object, basically saying: "whenever this object seem to not be used, release it for me I don't really care" then the copy will up the retain count +1 on.... wwanServiceId? or newValue? I guess the first.
Then to make me all more confused let's just quickly run through the life-cycle of the wwanServiceId-string..
basically the value will be set if we receive a notification which then from the notification-handler method will call the above -setWwanServiceId: method. other than that it will only ever be accessed for reading, so we are safe to say that it will at any given point:
put autorelease on object
retain a new string copy
Then there is one more quirk to this and this is where I am getting rather suspicious, namely in the -dealloc method which looks like this:
- (void) dealloc {
[[self wwanServiceId] release];
[super dealloc];
}
So what happens there? it will afaik release the wwanServiceId and as I said the only time it's memory management is being touched (if I haven't missed anything but I feel pretty sure) is to put an autorelease and retain it.
So to summarize:
Is the idea here that he thought that since he always retain a new copy after putting autorelease he need to release it in the end.. or well it's the only thing I can think about. Or just felt it would be safe to do an extra release in the end just in case..?
Because as far as I understand it, if this setter is called one time it will put an autorelease (-1 in future), do a retain (+1) and when destructor is called it will do the "final release" (-1).
Any ideas or suggestions helping me understand (if in fact I am wrong and the memory handling is correct as is) would be appreciated.
You wrote:
Here wwanServiceIdis a NSString member of the class, and to me this seem like a strange way to do it. As far as I understand it would begin by putting an autorelease on the object, basically saying: "whenever this object seem to not be used, release it for me I don't really care" then the copy will up the retain count +1 on.... wwanServiceId? or newValue? I guess the first.
This seems to point to the source of your confusion. wwanServiceId is a variable which can contain a reference to an object of type NSString. Variables do not have reference counts only objects do.
The previous employee wrote:
- (void) setWwanServiceId: (NSString *) newValue {
[wwanServiceId autorelease];
wwanServiceId = [newValue copy];
}
The expression [wwanServiceId autorelease] means: read the reference stored in wwanServiceId and autorelease the object that reference refers to - let's call that object A. Important: This does not delete object A; it will be released as some later stage and if at that time there are no remaining references object A will be deleted.
The expression [newValue copy] means: read the reference stored in newValue, use that to locate the object (call it object B), make a copy of that object to produce a new object (call it object C), and return a reference to the new object. This new object is owned by the caller of copy, so there is no need to retain it.
Finally the assignment stores the reference to object C into wwanServiceId.
So there is a maximum of three distinct objects involved:
A: the original object referenced by wwanServiceId, this is autoreleased to remove the ownership of wwanServiceId.
B: the object referenced by newValue, this is left untouched
C: a newly created copy of B owned through wwanServiceId
Why "autorelease" in the code and "maximum of three distinct" above?
The method could be called with newValue referencing object A, e.g. as in:
[self setWwanServiceId:[self wwanServiceId]]
If this was to occur and (a) release was used instead of autorelease and (b) there was no other reference to object A then the release would delete object A and then when [newValue copy] was evaluated newValue would be referencing a deleted object... The use of autorelease in this case delays the deletion until after the copy.
So what the previous employee wrote is not in any way "wrong", but as some of the other answer suggest it may be an unusual style. Another way you see this written is:
- (void) setWwanServiceId: (NSString *) newValue
{
NSString *oldValue = wwanServiceId;
wwanServiceId = [newValue copy];
[oldValue release];
}
which also ensures any deletion occurs after the copy.
HTH.
To be short and helpful:
This is wrong:
- (void) setWwanServiceId: (NSString *) newValue {
[wwanServiceId autorelease];
wwanServiceId = [newValue copy];
}
This is right:
- (void) setWwanServiceId: (NSString *) newValue {
if (newValue != wwanServiceId) {
[wwanServiceId release];
wwanServiceId = [newValue copy];
}
}
To explain in short:
[wwanServiceId autorelease]; is an unnecessary sent message, because autoreleasing an object will reduce the retain count at some unknown point in the future. And in the next line you are wwanServiceId = [newValue copy]; instantly setting the instance variable. So, in your memory you now have a to be autoreleased object and a new object. One of them is too much The new object is, where the pointer of your IVar is pointing to. The old one is swimming in your Memory Pool with probably no reference to it :-)
Autorelease as few as possible or use ARC.
Oh: And in the dealloc method, please do not send the message like this:
[[self wwanServiceId] release];
Better like this:
[wwanServiceId release];
as Apple recommends to work directly with instance methods in init and dealloc methods, instead of using getters and setters there.
Debug it and have a look.
[wwanServiceId autorelease];
wwanServiceId has an address. This statement does not change it. It does decrement the retain count of this object though.
Thats it.
wwanServiceId = [newValue copy];
This statement creates a new object. The new object is a copy of newValue. Compare the addresses of the objects and you will see, that the address inn wwanServiceId will vary from the address of newValue and it will vary from the address that wwanServiceId did have just before the statement was executed.
The retain, that is implicit in copy will affect wwanServiceId, but it affects the new object, that was just creaed with copy. It does not affect the wwanServiceId object which was autoreleased during the statement before.
At some point after the execution of setWwanServiceId had finished, the old and autoreleased object will dissappear. (Assuming that the retain count is 0 now. If it is >0 because it is still retained for other reasons or just for error, then it will remain and potentially cause a leak.)
Once you understood that you will not question any more what is happening in the dealloc method. wwanServiceId is released. Meaning its retain count is reduced by 1. In the event that it is 0 then it will be deallocated automatically.
You could even autorelease it there too. The diffrerence to autorelease is basically that an autoreleased object is still around and available while the current method is being executed. Its release comes into effect at some later point in time.
But there is no reason for autoreleasing the object in dealloc.
In the example given there is not even a good reason to autorelease the object in the setter setWwanServiceId. You may well release the object directly in both methods.
Assuming wwanServiceId is the private ivar mapped by the getter wwanServiceId and setter setWwanServiceId, I think it is not correct to autorelease the ivar in the setter.
I would have coded the following:
- (void) setWwanServiceId: (NSString *) newValue {
if (newValue != wwanServiceId) {
[wwanServiceId release];
wwanServiceId = [newValue copy];
}
}
What I mean is: it is not necessary to give ownership of your var to the autorelease pool (which it may be drained at the end of the application). Simply release the ivar. If someone is using it, no problem, it will have a strong reference to it. Otherwise it will be deallocated.
Does ARC ever inject retain and release calls that you generally wouldn't see in a non-ARC environment?
For example, explicitly releasing an object from a getter:
- (NSArray *)dummyArray {
return [[NSArray alloc]init];
}
- (void)useDummyArray {
NSArray * arr = [self dummyArray];
//do something with arr
[arr release]; //unconventional injection of release.
}
Would ARC ever generate a release statement like the code above or would it autorelease the array returned by [self dummyArray];
The beauty of ARC is that you don't know, or need to know. However, you can give hints to the ARC static analyzer:
-(NSArray *) dummyArray NS_RETURNS_RETAINED { // this tells ARC that this function returns a retained value that should be released by the callee
return [[NSArray alloc] init];
}
-(NSArray *) otherDummyArray NS_RETURNS_NOT_RETAINED { // this tells ARC that the function returns a non-retained (autoreleased) value, which should NOT be released by the callee.
return [[NSArray alloc] init];
}
However, NS_RETURNS_NOT_RETAINED is the default, as long as your function name doesn't begin with init, in which NS_RETURNS_RETAINED becomes default.
So, in your specific scenario, it will almost always return an autorelease'd value. One major reason for this is support for interpolation with non-ARC code, which could result in leaks.
I am learning memory management, but I don't really understand why I should implement my attribute getter like this:
-(Weapon*)myWeapon
{
[myWeapon retain];
[myWeapon autorelease];
return myWeapon;
}
I understand what I wrote, but I can't imagine why it is important? Can you please explain why I should do this?
This is from the Memory Management Programming guide:
Technique 1
In technique 1, values returned by the getter are autoreleased within the calling scope:
- (NSString*) title {
return [[title retain] autorelease];
}
- (void) setTitle: (NSString*) newTitle {
if (title != newTitle) {
[title release];
title = [newTitle retain]; // Or copy, depending on your needs.
}
}
Because the object returned from the get accessor is autoreleased in the current scope, it remains valid if the property value is changed. This makes the accessor more robust, but at the cost of additional overhead. If you expect your getter method to be called frequently, the added cost of retaining and autoreleasing the object may not be worth the performance cost.
Technique 2
Like technique 1, technique 2 also uses an autorelease technique, but this time does so in the setter method:
- (NSString*) title {
return title;
}
- (void) setTitle: (NSString*) newTitle {
[title autorelease];
title = [newTitle retain]; // Or copy, depending on your needs.
}
The performance of technique 2 is significantly better than technique 1 in situations where the getter is called much more often than the setter.
Technique 3
Technique 3 avoids the use of autorelease altogether:
- (NSString*) title {
return title;
}
- (void) setTitle: (NSString*) newTitle {
if (newTitle != title) {
[title release];
title = [newTitle retain]; // Or copy, depending on your needs.
}
}
The approach used by technique 3 is good for frequently called getter and setter methods. It is also good for objects that do not want to extend the lifetime of their values, such as collection classes. Its disadvantage is that the old value may be deallocated immediately (if there are no other owners), which will cause a problem if another object is maintaining a non-owning reference to it. For example:
NSString *oldTitle = [anObject title];
[anObject setTitle:#"New Title"];
NSLog(#"Old title was: %#", oldTitle);
If anObject was the only object that owned the original title string, then the string will be deallocated after the new title is set. The log statement would then cause a crash as oldTitle is a freed object.
EDIT: Basically the point of retaining and then auto-releasing is to ensure that the object won't get deallocated if the property value is changed before the calling scope has a chance to retain it. This isn't usually an issue unless you've got asynchronous code. In most situations - (Weapon *)myWeapon { return myWeapon; } is just fine (plus it's faster).
As ACBurk says, but there is a good description here:
http://vgable.com/blog/2009/03/31/how-to-write-cocoa-object-getters/
I'm declaring an NSString property in a class and objective-c is complaining that:
NSString no 'assign', 'retain', or 'copy' attribute is specified
It then casually lets me know that "assign is used instead".
Can someone explain to me the difference between assign, retain and copy in terms of normal C memory management functions?
I think it is drawing your attention to the fact that a assign is being used, as opposed to retain or copy. Since an NSString is an object, in a reference-counted environment (ie without Garbage Collection) this can be potentially "dangerous" (unless it is intentional by design).
However, the difference between assign, retain and copy are as follows:
assign: In your setter method for the property, there is a simple assignment of your instance variable to the new value, eg:
- (void)setString:(NSString*)newString
{
string = newString;
}
This can cause problems since Objective-C objects use reference counting, and therefore by not retaining the object, there is a chance that the string could be deallocated whilst you are still using it.
retain: this retains the new value in your setter method. For example:
- (void)setString:(NSString*)newString
{
[newString retain];
[string release];
string = newString;
}
This is safer, since you explicitly state that you want to maintain a reference of the object, and you must release it before it will be deallocated.
copy: this makes a copy of the string in your setter method:
- (void)setString:(NSString*)newString
{
if(string!=newString)
{
[string release];
string = [newString copy];
}
}
This is often used with strings, since making a copy of the original object ensures that it is not changed whilst you are using it.
Cocoa uses reference counting to manage memory. Objects with a reference count of 0 are deleted.
assign - does nothing to reference count simply points your variable to the data
retain - points your variable to data and adds 1 to reference count, data is guaranteed to be there while your variable is still alive
copy - makes a copy of data, points your variable at it and makes the retain count 1
More detail here, at Apple's own documentation.
assign - the ivar is set by doing a simple assignment. Implementation:
- (void) setFoo:(NSString *)newFoo {
foo = newFoo;
}
retain - the ivar is sent the retain message before doing the assignment. Implementation:
- (void) setFoo:(NSString *)newFoo {
if (foo != newFoo) {
[foo release];
foo = [newFoo retain];
}
}
copy - the ivar is sent the copy message before doing the assignment. Implementation:
- (void) setFoo:(NSString *)newFoo {
if (foo != newFoo) {
[foo release];
foo = [newFoo copy];
}
}