Apple Developer Connection
Member Login Log In | Not a Member? Contact ADC

< Previous PageNext Page > Hide TOC

Property Declaration and Implementation

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

In this section:

Property Declaration
Property Implementation Directives
Property Declaration Attributes
Copy
Property Re-declaration
Performance and Threading
Markup and Deprecation
Core Foundation
Example


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.

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

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: The default value is @dynamic. If, therefore, 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
 
// assume using garbage collection
@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 Differences”):

  • 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, 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

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 garbage collection, you can use the storage modifiers __weak and __strong in a property’s declaration, but they are not a formal part of the list of attributes.

getter=getterName, setter=setterName

getter= and setter= specify respectively the names of get and set accessors for the property. The getter must return a type matching the property’s type and take no arguments. The setter method must take a single argument of a type matching the property’s type and must return void.

The default names are propertyName and setPropertyName: respectively—for example, given a property “foo”, the accessors would be foo and setFoo:. Typically you should specify accessor method names that are key-value coding compliant (see Key-Value Coding Programming Guide)—a common reason for using this decorator is to adhere to the isPropertyName convention for Boolean values.

readonly

Indicates that the property is read-only. The default is read/write.

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

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, or if you use @synthesize the getter and setter methods are synthesized.

assign

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

If your application uses garbage collection, if you want to use assign for a property whose class adopts the NSCopying protocol you should specify the attribute explicitly rather than simply relying on the default—otherwise you will get a compiler warning. (This is to reassure the compiler that you really do want to assign the value, even though it’s copyable.)

retain

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

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

nonatomic

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

By default, accessors are atomic. This is so that default 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 simultaneously. For more details, see “Performance and Threading.”

If you do not specify nonatomic, then in a managed memory environment a synthesized get accessor for an object property retains and autoreleases the returned value; if you specify nonatomic, then a synthesized accessor for an object property simply returns the value directly.

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

You can use the interface attributes getter=, setter=, readonly, readwrite in a class, category or protocol declaration. You can only use one of readonly and readwrite in the attribute list. setter=/getter= are both optional and may appear with any other attribute save for readonly. If you specify that a property is readonly then also specify a setter with setter=, you will get a compiler warning.

assign, retain, and copy are mutually exclusive. Different constraints apply depending on whether or not you use garbage collection:

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:(NSArray *)newArray
{
    if (myArray != newArray)
    {
        [myArray release];
        myArray = [newArray mutableCopy];
    }
}
 
@end

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

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 (note that 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 managed memory environment, guaranteeing atomic behavior requires the use of a lock; moreover a returned object is retained and autoreleased. 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.

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__((...));

Core Foundation

As noted in “Property Implementation Directives,” 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 managed memory 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];
}
 
 
 - init
{
    if (self = [super init])
    {
        intervalSinceReferenceDate = [NSDate timeIntervalSinceReferenceDate];
    }
    return self;
}
 
@end


< Previous PageNext Page > Hide TOC


Last updated: 2008-02-05




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.
Get information on Apple products.
Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Copyright © 2007 Apple Inc.
All rights reserved. | Terms of use | Privacy Notice