I want to understand how to set the parameters of properties (accessors).
I took the following code from an example of Kal calendar.
// Holiday.h
@interface Holiday : NSObject
{
NSDate *date;
NSString *name;
NSString *country;
}
@property (nonatomic, retain, readonly) NSDate *date;
@property (nonatomic, retain, readonly) NSString *name;
@property (nonatomic, retain, readonly) NSString *country;
- (id)initWithName:(NSString *)name country:(NSString *)country date:(NSDate *)date;
@end
// Holiday.m
#import "Holiday.h"
@implementation Holiday
@synthesize date, name, country;
- (id)initWithName:(NSString *)aName country:(NSString *)aCountry date:(NSDate *)aDate
{
if ((self = [super init])) {
name = [aName copy];
country = [aCountry copy];
date = [aDate retain];
}
return self;
}
- (void)dealloc
{
[date release];
[name release];
[country release];
[super dealloc];
}
@end
1) The properties are set to retain
, but since the setter can't be used the retain
makes no sense here.
2) In addition, in the initWithName
method the values are set with copy
. Why not directly defining the properties with copy
and using the accessor methods?
@property (nonatomic, copy) NSString *name;
// ...
self.name = aName;
3) Do I need the readonly
here? I don't know why they are used here. If I would use copy
together with the setter the readonly
prohibits me to set the value, because there is no setter.
4) In the initWithName
method sometimes copy
and sometimes retain
is used. I would suggest to use always copy
here, because the value should not get modified later.
5) What I can remember is that it is OK to copy
/retain
in the initWithName
and release
in the dealloc
method.
So how would you suggest to use retain
, copy
and readonly
in this example here?
ETA: @DougW correctly points out that a property's ownership type (assign
/retain
/copy
) does not affect the getter. It does still affect the setter. For readonly
types, this matters if you are going to override the readonly
portion of the declaration in a class extension, so you can use the setter within your implementation. A class extension property override is only allowed to change the readonly
status of the property, so the rest of it - which means the atomicity and ownership types - must be declared appropriately in the header. Even if you're not overriding the property now, you might in future, so you might as well document how you want the memory managed for yourself by using the correct option to begin with.
Automatic reference counting (ARC) changes the runtime implementation details by overlaying its own memory management rules on top of the classic refcount rules, but the rules and advice for configuring your properties stay the same.
Why use retain
with readonly
? If you mark a property as retain
, the synthesized accessor does something like this:
/* getter for retain property */
- (NSString *)name {
return [[name retain] autorelease];
}
Now, if the object you sent -name
to changes the name while you're still using it, the calling code will still have a valid reference to a string. If you declared it as assign
, though, it would be like this:
/* getter for assign property */
- (NSString *)name {
return name;
}
Now, as soon as name is changed by the object, it will have to be released to avoid a leak, which will invalidate the calling code's reference. The retain
/copy
/assign
is really stating a memory-management policy: retain
/copy
says, "I promise that I hold a reference to the original/a copy of the value I provide here," while assign
says, "I just have the value and claim no owning reference to this."
When the value doesn't need memory management, such as a plain int
, then assign
makes sense. When you're intentionally not retaining an object, such as a delegate, then assign
makes sense. But, in most other cases, you'll want retain
or copy
.
Further, the implementation file can only override the readwrite
/readonly
portion of a property declaration, not the memory management portion. As declared, the .m
file can have:
@interface Holiday (/*class extension*/)
@property(nonatomic, retain, readwrite) NSDate *date;
/* override other properties to make them readwrite... */
@end
Non-public setters for the overridden property declarations will then be synthesized along with the public accessors.
Why not use setters/accessors during -init
? Because setters/accessors frequently perform KVO notification, which you want to avoid while your object is not fully initialized, i.e., during -init
(when it's half-initialized on its way to full initialization) and -dealloc
(when it's half-initialized on its way to being fully un-initialized).
Why use copy
with readonly
? As in response to your first question: because if copy
versus retain
versus assign
affects both the setters and the getters. A copy getter would look like this:
/* getter for copy property */
- (NSString *)name {
return [[name copy] autorelease];
}
Why sometimes copy
and sometimes retain
? copy
is generally used with value objects (passive objects representing a value); retain
is generally used with other objects. Sometimes, efficiency concerns come into play (most likely prematurely...), and you might opt to use retain
where you would normally use copy
.
How would you use copy
/retain
along with readonly
here? Pretty much as they did. I'd override the declarations in a class extension so I can use setters to change the properties' values outside of -init
and -dealloc
, where I would only use direct instance variable access. I would also nil
out the ivars after releasing them in -dealloc
, e.g.,
[name release], name = nil;
This helps avoid sending messages to or otherwise referencing an already-released object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With