Declared Properties in Objective-C

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

.

The following information was most recently updated in September 2013.

.

Your professor recommends that you read this note, at least once every week, until at least the study week break.

.

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

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 Programming With Objective-C

.

Declaring a property

In your .h interface, assume that you need a publicly-visible 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;

Also, later in this document, we will explain what (nonatomic, copy) means.

.

The compiler will create an instance variable to hold the declared property’s value. The naming convention is to use the declared property’s name, preceded by an underscore. For example, _firstName is the name of the instance variable for the “firstName” declared property above.

Do you ever use the instance variable in your code?

Yes. In the class initializer.

The compiler will also create getter and setter methods for the declared property. The dot syntax is is simply a convenient way to call the getter and setter methods.

Two “do not” explanations:

Do not explicitly 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 declaring an instance variable for a declared property is not necessary.

Also, do not ‘synthesize’ the property in the .m implementation. That’s not necessary any longer.

.

Declaring a property for an outlet

An outlet is an instance variable (e.g. in a controller) that points to another object (e.g. a text field in a view). Apple recommends that an outlet be declared using the declared property feature.

When you create an outlet using Interface Builder in Xcode, the following code is generated when you Control+drag a user interface control to the code editor. Alternatively, if you need to do this manually for some reason, continue reading this section.

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


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

.

Then, in the implementation, 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  delegate;

.

Using a property in your program code

Use a dot syntax, similar to a struct accessor.

.

Scenario: You are using an instance of a class that has 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 semantic attributes, for behaviour and memory management.

For behaviour, in iOS apps, choose nonatomic.

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

.

For memory management (aka “setter semantics”), continue reading.

.

Which memory management option – copy? strong? weak? assign?

In the sections that follow, you will learn that copy should be your first choice if possible. You would use strong (or its older synonym, retain) if you have to, or you explicitly want to. Weak is used often for outlets to user interface objects. 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.

.

strong

If the property’s type does NOT conform to the NSCopying protocol, use strong. (Readers with some past iOS programming experience will recognize that “retain” is a synonym for “strong”.)

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 strong, 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 strong.

.

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 strong: 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 maintain a strong reference to the object, so you must use strong.

.

weak

When creating outlets for user interface controls, Xcode uses weak. That is, your view controller property maintains a weak reference to the user interface object.

.

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

.

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 strong 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 strong, 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 “strong” 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 “strong” memory management.

Here are the coding tactics:

Interface .h:

@property (nonatomic, strong) NSMutableArray *myCollection;

Implementation .m:

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

.

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:

// 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: