Home > 2011 Fall DPS913, iOS, iOS Cocoa Touch > Declared Properties in Objective-C

Declared Properties in Objective-C

September 19, 2011 Leave a comment Go to comments

In Objective-C classes, declared properties provide a convenient syntax for the creation of instance variable accessors (i.e. getters and setters).

.

If you are writing a class, and intend to maintain state, then you should use declared properties.

.

The following information was most recently updated in October 2011. It is valid for the modern runtime that was in wide use from mid-2010 onwards.

The following Apple documentation covers declared properties. Where there is a coding style and implementation difference (e.g. the @synthesis coding style), please follow the guidance in this note, because the guidance is based on the latest best practices.

Declared property, in Cocoa Core Competencies

Declared Properties, in The Objective-C 2.0 Programming Language

Dot Syntax, same document

.

Declaring a property

In your .h interface, assume that you need a string instance variable. Declare it as a property, as follows:

@property (nonatomic, copy) NSString *firstName;

Locate your properties between your @interface block, and the @end keyword.

#import

@interface MyCustomClass : NSObject

@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@property int age;

@end

.

Note: A property declaration can include a comma-separated list of variable names. This means that the two string property declarations above could be written like this:

@property (nonatomic, copy) NSString *firstname, *lastname;

.

Do not declare an instance variable for the property. In the past, with older versions of the compiler and runtime, it was necessary to do so. Not now. You will see many older code samples (including Apple’s) that do this, but this is now deprecated and unnecessary.

Note: Later in this document, we will explain what (monatomic, copy) means.

.

Then, in your .m implementation, just below the “@implementation…” statement, write this code (which creates the instance variable and its accessors at compile time):

@implementation MyCustomClass

@synthesize firstName = _firstName;
@synthesize lastName = _lastName;
@synthesize age = _age;

.

This statement declares “_firstName” as the name of the instance variable that backs the “firstName” property. This encourages coding clarity and removes ambiguity: “firstName” is the property’s name. “_firstName” is the name of the instance variable that backs the property.

Where do you, or can you, use the underscore-prefixed name (“_firstName”)?

In the init methods, where you set the value of the instance variable. Do not use the property.

In the dealloc method; where you send the release message to the instance variable, as shown below.

.

Finally, in your dealloc method, write this code to perform memory management on the declared property:

[_firstName release];

.

Declaring a property for an outlet

Apple recommends that outlets be declared using the declared property feature.

Include the IBOutlet type qualifier when you declare the property in your .h interface. Insert “IBOutlet” before the type name, as follows:


@property (nonatomic, retain) IBOutlet UILabel *myLabel;

.

Then, in the implementation, follow the same rules that were covered in the previous section.

Then, there is one additional step. In the viewDidUnload method (which improves the effectiveness of memory management tasks), set the property to nil, as shown below:


self.myLabel = nil;

.

Declaring a delegate property

Soon, you will learn how to enable a class to become a delegate. One of the coding tasks is to create a property named “delegate”. It is declared as follows:


@property (nonatomic, assign) id <NameOfDelegateProtocol> delegate;

.

Notice the memory management – “assign”. This means that you do NOT release the delegate property in the implementation’s – dealloc method.

.

Using a property in your program code

Use dot syntax, similar to a struct accessor.

.

Scenario: You are using an instance of a class with a property

Use the instance name, dot, property name.

For example, if a class has a “firstName” property, and you have an instance of that class, called “student1”:


NSLog(@"The student's name is: %@", student1.firstName);

.

Scenario: You are using a property that’s in your class

When you are referring to the property from within its containing class, use the “self.” prefix.

For example, if you want to set the “text” property of the “myLabel” property you created in the previous section, write this code:


self.myLabel.text = @"Hello, world!";

.

An additional example: If you want to get the length of the “myString” property from above, write this code:


NSLog(@"length %d", [self.myString length]);

.

Behaviour and memory management

This is a topic which is difficult for a new iOS developer to understand. As an iOS developer with some experience, I also think that it is a difficult topic to explain. However, in this post, I’ll try.

Kudos to Chris Hanson from the Stack Overflow community. I have read his explanation in the past, but it wasn’t until recently that I experimented in code (yes, I know; my bad). Then, I fully understood.

.

Which behaviour? nonatomic

When you decide to declare a property in a class, you need to specify semantics, including behaviour and memory management.

For behaviour, in iOS apps, choose nonatomic.

Note: I may revise this guidance, after further study of, and experience with, GCD and blocks.

.

For memory management (aka “setter semantics), read on.

.

Which memory management option – copy? retain? assign?

In the sections that follow, you will learn that copy should be your first choice if possible. You would use retain if you have to, or you explicitly want to. And, you would use assign in only some situations.

.

copy

This may be the most suitable option for most property types.

Near the end of this post, there is a section titled “Memory management for collection properties”. Read that section for information about memory management for arrays and dictionaries.

.

When you use copy, you are assured that your property’s value can only be changed (mutated) by using its setter accessor.

To use this memory management option, the property’s type must conform to the NSCopying protocol.

.

retain

If the property’s type does NOT conform to the NSCopying protocol, use retain.

Check the property type’s class reference documentation to see whether it conforms to the NSCopying protocol.

Many commonly-used types conform to NSCopying, including NSString, NSArray, and NSDictionary (and their mutable subclasses).

Notably, NSObject does not conform to NSCopying. Many model objects inherit from NSObject, so a model object property will use retain, unless you enable the model object to conform to the NSCopying protocol (by implementing the methods discussed in the NSCopying Protocol Reference documentation).

Additionally, NSManagedObject does not conform to NSCopying. Nor will a generated subclass. Therefore, use retain.

.

While you have learned (above) that copy may be the most suitable option, there is a situation where you may decide that you want to use retain: Assume that your property’s value was set to an object, maybe during initialization, or at another time. And, further assume that the object can change (mutate), but you want your property to follow that change. Therefore, in this situation, you really do want to retain a reference to the object, so you must use retain.

.

assign

For primitive C types (int, float, …), you must use assign. Also, use assign for BOOL and NSNumber.

For a delegate, use assign. That way, you’ll avoid a “retain cycle”. (A retain cycle is bad.)

Assign is the default, if you do not provide a memory management option.

Assign can be thought of as an antonym for retain.

.

What does this really mean?

As stated earlier, a declared property is a convenient way to create accessors (i.e. getter and setter) for an instance variable.

Then, in a statement in your program code, you will set a value for the property. This will be done during initialization, or later.

For example, assume that you have a model class, and it has a property called “school”. In one of the methods, you wish to assign a string (that was passed in as an argument) to the property. Here’s the code:


self.school = @“Seneca”;

.

In this situation, using retain or copy will be equivalent. The right side of the statement is an immutable string, so it (@”Seneca”) won’t change in the future.

However, consider a slightly different scenario to the one above. In the class initializer, you wish to assign a value that was passed in as an argument (called “someName” in this scenario), and the original value (that was passed in) is mutable. The code looks like this:


_school = someName;

Notes:

“_school” is the name of the instance variable that backs the “school” property. You learned above that you use the instance variable name in an initializer.

someName is a pointer to a mutable object, probably an instance of NSMutableString. The object’s value could change over time.

.

In this situation, using copy is almost always preferred. You get a copy of the original value, and the property is set to this value. If the original value changes, the property’s value will not change.

Alternatively, if you use retain, and change the original value, the property’s value changes too. That’s probably a bad thing. (You almost always want to use the property accessor to mutate its value.)

.

Memory management for collection properties

We noted earlier that copy “may be the most suitable option for most property types”.

What about NSArray, NSDictionary, and their mutable variants? Well, it depends.

As you have learned, a collection class is essentially a collection of pointers to objects.

  • These pointers, in an immutable NSArray or NSDictionary,  cannot be changed (although the objects pointed to can change).
  • In contrast, in NSMutableArray and NSMutableDictionary, these pointers can be removed, replaced, or added to (and as above, the objects pointed to can change).

.

If you want an NSArray property, and do not intend to change its pointers, then it’s acceptable to use “copy” memory management. Conceptually, you are using it as a “read only” property.

However, you typically use properties for mutable state in a class. Therefore, you would think that you should use a mutable type, like an NSMutableArray. That would be correct. Then, you would think that you would use “copy” memory management. That would be conceptually correct, but it won’t work. This is because the “copy” method returns an immutable version of the collection that was copied.

For a diagram, and more information, see this linked section from the Collections Programming Topics document.

.

So, what are your options for mutable collections?

.

Option 1 – use “retain” memory management: 

This works well if the array that was copied will not mutate (change) by actions or events that are beyond or outside your control.

For example, if you set an NSMutableArray property’s value with a class method (such as arrayWithObjects:), then you know that the array that was created by the class method will not mutate. In this situation, you can use “retain” memory management.

You must also send the array (that was returned from the class method) the “retain” message, so that it will exist beyond the local scope.

Here are the coding tactics:

Interface .h:

@property (nonatomic, retain) NSMutableArray *myCollection;

Implementation .m:

@synthesize myCollection = _myCollection;

// Then, later, when it is initialized...
self.myCollection = [[NSMutableArray arrayWithObjects:@"Hello", @"world", nil] retain];

.

Option 2 – provide your own implementation of the property’s setter method:

The other option is to provide your own implementation of the property’s setter method.

Here are the coding tactics:

Interface .h:

@property (nonatomic, copy) NSMutableArray *myCollection;

Implementation .m:

@synthesize myCollection = _myCollection;

// Implement the setter method...
- (void)setMyCollection:(NSMutableArray *)newArray {
    if (self.myCollection != newArray) {
        [myCollection release];
        myCollection = [newArray mutableCopy];
    }
}

// Then, elsewhere, when it is initialized...
self.myCollection = [NSMutableArray arrayWithObjects:@"Hello", @"world", nil];

.

In summary, the guidance from above, to use “copy” memory management, will work for most types. However, be careful when working with mutable collection types.

.


.

.

.

Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: