< Previous PageNext Page > Hide TOC

Declared Properties

The Objective-C “declared properties” feature provides a simple way to declare and implement an object’s accessor methods.

In this section:

Overview
Property Declaration and Implementation
Using Properties
Subclassing with Properties
Performance and Threading
Runtime Difference


Overview

There are two aspects to this language feature: the syntactic elements you use to specify and optionally synthesize declared properties, and a related syntactic element that is described in “Dot Syntax.”

You typically access an object’s properties (in the sense of its attributes and relationships) through a pair of accessor (getter/setter) methods. By using accessor methods, you adhere to the principle of encapsulation (see “Mechanisms Of Abstraction” in Object-Oriented Programming with Objective-C > The Object Model). You can exercise tight control of the behavior of the getter/setter pair and the underlying state management while clients of the API remain insulated from the implementation changes.

Although using accessor methods has significant advantages, writing accessor methods is nevertheless a tedious process—particularly if you have to write code to support both garbage collected and reference counted environments. Moreover, aspects of the property that may be important to consumers of the API are left obscured—such as whether the accessor methods are thread-safe or whether new values are copied when set.

Declared properties address the problems with standard accessor methods by providing the following features:

Property Declaration and Implementation

There are two parts to a declared property, its declaration and its implementation.

Property Declaration

A property declaration begins with the keyword @property. @property can appear anywhere in the method declaration list found in the @interface of a class. @property can also appear in the declaration of a protocol or category (protocols and categories are described in “Protocols” and “Categories and Extensions” respectively).

@property(attributes) type name;

@property declares a property. An optional parenthesized set of attributes provides additional details about the storage semantics and other behaviors of the property—see “Property Declaration Attributes” for possible values. Like any other Objective-C type, each property has a type specification and a name.

Listing 4-1 illustrates the declaration of a simple property.

Listing 4-1  Declaring a simple property

@interface MyClass : NSObject
{
    float value;
}
@property float value;
@end

You can think of a property declaration as being equivalent to declaring two accessor methods. Thus

@property float value;

is equivalent to:

- (float)value;
- (void)setValue:(float)newValue;

A property declaration, however, provides additional information about how the accessor methods are implemented (as described in “Property Declaration Attributes”).

Property Declaration Attributes

You can decorate a property with attributes by using the form @property(attribute [, attribute2, ...]). Like methods, properties are scoped to their enclosing interface declaration. For property declarations that use a comma delimited list of variable names, the property attributes apply to all of the named properties.

If you use the @synthesize directive to tell the compiler to create the accessor method(s), the code it generates matches the specification given by the keywords. If you implement the accessor method(s) yourself, you should ensure that it matches the specification (for example, if you specify copy you must make sure that you do copy the input value in the setter method).

Accessor Method Names

The default names for the getter and setter methods associated with a property are propertyName and setPropertyName: respectively—for example, given a property “foo”, the accessors would be foo and setFoo:. The following attributes allow you to specify custom names instead. They are both optional and may appear with any other attribute (except for readonly in the case of setter=).

getter=getterName

Specifies the name of the get accessor for the property. The getter must return a type matching the property’s type and take no arguments.

setter=setterName

Specifies the name of the set accessor for the property. The setter method must take a single argument of a type matching the property’s type and must return void.

If you specify that a property is readonly then also specify a setter with setter=, you will get a compiler warning.

Typically you should specify accessor method names that are key-value coding compliant (see Key-Value Coding Programming Guide)—a common reason for using the getter decorator is to adhere to the isPropertyName convention for Boolean values.

Writability

These attributes specify whether or not a property has an associated set accessor. They are mutually exclusive.

readwrite

Indicates that the property should be treated as read/write. This is the default.

Both a getter and setter method will be required in the @implementation. If you use @synthesize in the implementation block, the getter and setter methods are synthesized.

readonly

Indicates that the property is read-only.

If you specify readonly, only a getter method is required in the @implementation. If you use @synthesize in the implementation block, only the getter method is synthesized. Moreover, if you attempt to assign a value using the dot syntax, you get a compiler error.

Setter Semantics

These attributes specify the semantics of a set accessor. They are mutually exclusive.

assign

Specifies that the setter uses simple assignment. This is the default.

retain

Specifies that retain should be invoked on the object upon assignment. (The default is assign.)

The previous value is sent a release message.

This attribute is valid only for Objective-C object types. (You cannot specify retain for Core Foundation objects—see “Core Foundation.”)

copy

Specifies that a copy of the object should be used for assignment. (The default is assign.)

The previous value is sent a release message.

The copy is made by invoking the copy method. This attribute is valid only for object types, which must implement the NSCopying protocol. For further discussion, see “Copy.”

Different constraints apply depending on whether or not you use garbage collection:

Atomicity

This attribute specifies that accessor methods are not atomic. (There is no keyword to denote atomic.)

nonatomic

Specifies that accessors are non-atomic. By default, accessors are atomic.

Properties are atomic by default so that synthesized accessors provide robust access to properties in a multi-threaded environment—that is, the value returned from the getter or set via the setter is always fully retrieved or set regardless of what other threads are executing concurrently. For more details, see “Performance and Threading.”

If you do not specify nonatomic, then in a reference counted environment a synthesized get accessor for an object property uses a lock and retains and autoreleases the returned value—the implementation will be similar to the following:

[_internal lock]; // lock using an object-level lock
id result = [[value retain] autorelease];
[_internal unlock];
return result;

If you specify nonatomic, then a synthesized accessor for an object property simply returns the value directly.

Markup and Deprecation

Properties support the full range of C style decorators. Properties can be deprecated and support __attribute__ style markup, as illustrated in the following example:

@property CGFloat x
AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4;
@property CGFloat y __attribute__((...));

If you want to specify that a property is an Interface Builder outlet, you can use the IBOutlet identifier:

@property (nonatomic, retain) IBOutlet NSButton *myButton;

IBOutlet is not, though, a formal part of the list of attributes.

If you use garbage collection, you can use the storage modifiers __weak and __strong in a property’s declaration:

@property (nonatomic, retain) __weak Link *parent;

but again they are not a formal part of the list of attributes.

Property Implementation Directives

You can use the @synthesize and @dynamic directives in @implementation blocks to trigger specific compiler actions. Note that neither is required for any given @property declaration.

Important: If you do not specify either @synthesize or @dynamic for a particular property, you must provide a getter and setter (or just a getter in the case of a readonly property) method implementation for that property.

@synthesize

You use the @synthesize keyword to tell the compiler that it should synthesize the setter and/or getter methods for the property if you do not supply them within the @implementation block.

Listing 4-2  Using @synthesize

@interface MyClass : NSObject
{
    NSString *value;
}
@property(copy, readwrite) NSString *value;
@end
 
@implementation MyClass
@synthesize value;
@end

You can use the form property=ivar to indicate that a particular instance variable should be used for the property, for example:

@synthesize firstName, lastName, age = yearsOld;

This specifies that the accessor methods for firstName, lastName, and age should be synthesized and that the property age is represented by the instance variable yearsOld. Other aspects of the synthesized methods are determined by the optional attributes (see “Property Declaration Attributes”).

There are differences in the behavior that depend on the runtime (see also “Runtime Difference”):

  • For the legacy runtimes, instance variables must already be declared in the @interface block. If an instance variable of the same name and compatible type as the property exists, it is used—otherwise, you get a compiler error.

  • For the modern runtimes (see Runtime Versions and Platforms in Objective-C 2.0 Runtime Programming Guide), instance variables are synthesized as needed. If an instance variable of the same name already exists, it is used.

@dynamic

You use the @dynamic keyword to tell the compiler that you will fulfill the API contract implied by a property either by providing method implementations directly or at runtime using other mechanisms such as dynamic loading of code or dynamic method resolution. The example shown in Listing 4-3 illustrates using direct method implementations—it is equivalent to the example given in Listing 4-2.

Listing 4-3  Using @dynamic with direct method implementations

@interface MyClass : NSObject
{
    NSString *value;
}
@property(copy, readwrite) NSString *value;
@end
 
// assume using garbage collection
@implementation MyClass
@dynamic value;
 
- (NSString *)value {
    return value;
}
 
- (void)setValue:(NSString *)newValue {
    if (newValue != value) {
        value = [newValue copy];
    }
}
@end

Using Properties

Supported Types

You can declare a property for any Objective-C class, Core Foundation data type, or “plain old data” (POD) type (see C++ Language Note: POD Types). For constraints on using Core Foundation types, however, see “Core Foundation.”

Property Re-declaration

You can re-declare a property in a subclass, but (with the exception of readonly vs. readwrite) you must repeat its attributes in whole in the subclasses. The same holds true for a property declared in a category or protocol—while the property may be redeclared in a category or protocol, the property’s attributes must be repeated in whole.

If you declare a property in one class as readonly, you can redeclare it as readwrite in a class extension (see “Extensions”), a protocol, or a subclass—see “Subclassing with Properties.” In the case of a class extension redeclaration, the fact that the property was redeclared prior to any @synthesize statement will cause the setter to be synthesized. The ability to redeclare a read-only property as read/write enables two common implementation patterns: a mutable subclass of an immutable class (NSString, NSArray, and NSDictionary are all examples) and a property that has public API that is readonly but a private readwrite implementation internal to the class. The following example shows using a class extension to provide a property that is declared as read-only in the public header but is redeclared privately as read/write.

// public header file
@interface MyObject : NSObject {
    NSString *language;
}
@property (readonly, copy) NSString *language;
@end
 
// private implementation file
@interface MyObject ()
@property (readwrite, copy) NSString *language;
@end
 
@implementation MyObject
@synthesize language;
@end

Copy

If you use the copy declaration attribute, you specify that a value is copied during assignment. If you synthesize the corresponding accessor, the synthesized method uses the copy method. This is useful for attributes such as string objects where there is a possibility that the new value passed in a setter may be mutable (for example, an instance of NSMutableString) and you want to ensure that your object has its own private immutable copy. For example, if you declare a property as follows:

@property (nonatomic, copy) NSString *string;

then the synthesized setter method is similar to the following:

-(void)setString:(NSString *)newString {
    if (string != newString) {
        [string release];
        string = [newString copy];
    }
}

Although this works well for strings, it may present a problem if the attribute is a collection such as an array or a set. Typically you want such collections to be mutable, but the copy method returns an immutable version of the collection. In this situation, you have to provide your own implementation of the setter method, as illustrated in the following example.

@interface MyClass : NSObject {
    NSMutableArray *myArray;
}
@property (nonatomic, copy) NSMutableArray *myArray;
@end
 
@implementation MyClass
 
@synthesize myArray;
 
- (void)setMyArray:(NSMutableArray *)newArray {
    if (myArray != newArray) {
        [myArray release];
        myArray = [newArray mutableCopy];
    }
}
 
@end

dealloc

Declared properties fundamentally take the place of accessor method declarations; when you synthesize a property, the compiler only creates any absent accessor methods. There is no direct interaction with the dealloc method—properties are not automatically released for you. Declared properties do, however, provide a useful way to cross-check the implementation of your dealloc method: you can look for all the property declarations in your header file and make sure that object properties not marked assign are released, and those those marked assign are not released.

Note: Typically in a dealloc method you should release object instance variables directly (rather than invoking a set accessor and passing nil as the parameter), as illustrated in this example:

- (void)dealloc {
    [property release];
    [super dealloc];
}
If you are using the modern runtime and synthesizing the instance variable, however, you cannot access the instance variable directly, so you must invoke the accessor method:

- (void)dealloc {
    [self setProperty:nil];
    [super dealloc];
}

Core Foundation

As noted in “Property Declaration Attributes,” you cannot specify the retain attribute for non-object types. If, therefore, you declare a property whose type is a CFType and synthesize the accessors as illustrated in the following example:

@interface MyClass : NSObject
{
    CGImageRef myImage;
}
@property(readwrite) CGImageRef myImage;
@end
 
@implementation MyClass
@synthesize myImage;
@end

then in a reference counted environment the generated set accessor will simply assign the new value to the instance variable (the new value is not retained and the old value is not released). This is typically incorrect, so you should not synthesize the methods, you should implement them yourself.

In a garbage collected environment, if the variable is declared __strong:

...
__strong CGImageRef myImage;
...
@property CGImageRef myImage;

then the accessors are synthesized appropriately—the image will not be CFRetain'd, but the setter will trigger a write barrier.

Example

The following example illustrates the use of properties in several different ways:

Listing 4-4  Declaring properties for a class

@protocol Link
@property id <Link> next;
@end
 
 
@interface MyClass : NSObject <Link>
{
    NSTimeInterval intervalSinceReferenceDate;
    CGFloat gratuitousFloat;
    id <Link> nextLink;
}
@property(readonly) NSTimeInterval creationTimestamp;
@property(copy) __strong NSString *name;
@property CGFloat gratuitousFloat;
@property(readonly, getter=nameAndAgeAsString) NSString *nameAndAge;
 
@end
 
 
@implementation MyClass
 
@synthesize creationTimestamp = intervalSinceReferenceDate, name;
// synthesizing 'name' is an error in legacy runtimes
// in modern runtimes, the instance variable is synthesized
 
@synthesize next = nextLink;
// uses instance variable "nextLink" for storage
 
@dynamic gratuitousFloat;
// will warn unless -gratuitousFloat and -setGratuitousFloat: occur in @implementation
 
 
- (CGFloat)gratuitousFloat {
    return gratuitousFloat;
}
- (void)setGratuitousFloat:(CGFloat)aValue {
    gratuitousFloat = aValue;
}
 
 
- (NSString *)nameAndAgeAsString {
    return [NSString stringWithFormat:@"%@ (%fs)", [self name],
               [NSDate timeIntervalSinceReferenceDate] - intervalSinceReferenceDate];
}
 
 
 - (id)init {
    if (self = [super init]) {
        intervalSinceReferenceDate = [NSDate timeIntervalSinceReferenceDate];
    }
    return self;
}
 
- (void)dealloc {
    [nextLink release];
    [name release];
    [super dealloc];
}
 
@end

Subclassing with Properties

You can override a readonly property to make it writable. For example, you could define a class MyInteger with a readonly property, value:

@interface MyInteger : NSObject
{
    NSInteger value;
}
@property(readonly) NSInteger value;
@end
 
@implementation MyInteger
@synthesize value;
@end

You could then implement a subclass, MyMutableInteger, which redefines the property to make it writable:

@interface MyMutableInteger : MyInteger
@property(readwrite) NSInteger value;
@end
 
@implementation MyMutableInteger
@dynamic value;
 
- (void)setValue:(NSInteger)newX {
    value = newX;
}
@end

Performance and Threading

If you supply your own method implementation, the fact that you declared a property has no effect on its efficiency or thread safety.

If you use synthesized properties, the method implementations generated by the compiler depend on the specification you supply. The declaration attributes that affect performance and threading are retain, assign, copy, and nonatomic. The first three of these affect only the implementation of the assignment part of the set method, as illustrated below (the implementation may not be exactly as shown):

// assign
property = newValue;
 
// retain
if (property != newValue) {
    [property release];
    property = [newValue retain];
}
 
// copy
if (property != newValue) {
    [property release];
    property = [newValue copy];
}

The effect of the nonatomic attribute depends on the environment. By default, the synthesized accessors are atomic. In a reference counted environment, guaranteeing atomic behavior requires the use of a lock; moreover a returned object is retained and autoreleased, as illustrated in “Atomicity.” If such accessors are invoked frequently, this may have a significant impact on performance. In a garbage collected environment, most synthesized methods are atomic without incurring this overhead.

It is important to understand that the goal of the atomic implementation is to provide robust accessors—it does not guarantee correctness of your code. Although “atomic” means that access to the property is thread-safe, simply making all the properties in your class atomic does not mean that your class or more generally your object graph is “thread safe”—thread safety cannot be expressed at the level of individual accessor methods. For more about multi-threading, see Threading Programming Guide.

Runtime Difference

In general the behavior of properties is identical on all runtimes (see Runtime Versions and Platforms in Objective-C 2.0 Runtime Programming Guide). There is one key difference: the modern runtime supports instance variable synthesis whereas the legacy runtime does not.

For @synthesize to work in the legacy runtime, you must either provide an instance variable with the same name and compatible type of the property or specify another existing instance variable in the @synthesize statement. With the modern runtime, if you do not provide an instance variable, the compiler adds one for you. For example, given the following class declaration and implementation:

@interface MyClass : NSObject {
    float sameName;
    float otherName;
}
@property float sameName;
@property float differentName;
@property float noDeclaredIvar;
@end
 
@implementation MyClass
@synthesize sameName;
@synthesize differentName=otherName;
@synthesize noDeclaredIvar;
@end

the compiler for the legacy runtime would generate an error at @synthesize noDeclaredIvar; whereas the compiler for the modern runtime would add an instance variable to represent noDeclaredIvar.



< Previous PageNext Page > Hide TOC


© 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-05-06)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.