views:

67

answers:

1

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?

+2  A: 

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 it 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.

Jeremy W. Sherman
**nonatomic** retain properties just return the pointer. They **do not** do the retain, autorelease thing. See the **atomicity** section of the docs http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html
JeremyP
@JeremyP: Good call. The decision not to `[[foo retain] autorelease]` in non-atomic accessors does makes sense: If you're going to hold onto the value for longer than the current runloop cycle, you should be retaining it yourself. If you're using `nonatomic`, you're basically stating that thread-safety is not a concern. If you don't have to worry about thread-safety, then no code aside from yours will be executing while you are using the return value from the accessor, so it's unnecessary for the accessor to perform `[[foo retain] autorelease]`.
Jeremy W. Sherman
@Jeremy: I would go so far as to say that using nonatomic means you are stating that robustness is not a concern - or is less of a concern than performance. Setting properties to nonatomic without first profiling your code counts as a premature optimisation in my book.
JeremyP
@Jeremy W. Sherman: Many good tips here. Questions: 1) If you override a property declaration how does the compiler know which accessor to use? Through the class extension? 2) Assign also for NSInteger, BOOL, double, float, CLLocationCoordinate2D, ... ?
testing
@JeremyP: 1) `atomic` is a concern if I'm writing a multithreading application with e.g. `performSelectorInBackground`? Because I'm writing a single threaded app I always should use `nonatomic`? 2) A `retain` property is never retained (only the pointer returned)?
testing
@testing: (1) The class extension override is not allowed to change anything except `readonly` to `readwrite`. The only change is that a setter is also synthesized. This way, the class declares to the world, "You can get this, but not set it." Then, it tells the compiler, "But I do actually want there to be a setter - the property should not be `readonly` for *me*." (2) Yes, you would use `assign` for those types. They require no memory management.
Jeremy W. Sherman
@testing: I would always use atomic properties unless profiling showed them to be a performance bottleneck. As I said, nonatomic properties don't even do the retain autorelease thing mentioned in Jeremy's answer. That means you can't get a property of an object, release the object and then use the property safely even in a single threaded application.
JeremyP
@Jeremy W. Sherman: Why does `int` does not require memory management?
testing
Because `int`'s aren't reference counted. A plain old `int` property can safely be copied here, there, and everywhere, with no concern for strong or weak references. Heck, read and writes to it most likely don't even need to be locked, because it's small enough that it's always read/written atomically by the underlying instruction set architecture. (That's not to say that you won't get a stale cached value from another thread if you don't use a memory barrier, but you won't get an inconsistent, half-set value regardless.)
Jeremy W. Sherman