Using Declared Properties
One of the most significant new features of Objective-C 2.0 was the introduction of declared properties. The concept is quite simple and eliminates a great deal of tedious typing for developers. The feature is best demonstrated by showing before-and-after examples.
Declaring a Property
Listing 3.1 shows a typical interface using legacy syntax. This is the .h file, and it contains the interface in a section starting with the compiler directive @interface. @ always introduces compiler directives; note the @end at the end of the file. Interface code can appear in other places, but the .h file for a class is the primary place.
Listing 3.1 Legacy Class Declaration
// // My_First_ProjectAppDelegate.h // My First Project // // Created by Sams on 6/14/11. // Copyright 2011 __MyCompanyName__. All rights reserved. // #import <Cocoa/Cocoa.h> @interface My_First_ProjectAppDelegate : NSObject <NSApplicationDelegate> { @private NSWindow *window; } - (IBAction)saveAction:sender; @end
The class shown here, My_First_ProjectAppDelegate, has an interface with one variable. After that, a single method, (IBAction)saveAction:sender, is declared. By convention, variable names that begin with underscores are private and should not be used directly. Also, note that all the variables are references—the * indicates that at runtime, a reference to the underlying object's structure is to be used and resolved as needed. The @private directive means that these variables are private to this class; by contrast, @protected would allow descendants of this class to use them. @public, which is rarely used (and which is considered poor syntax), allows any object to access these variables directly. This syntax is described later in this hour.
As you can see, there is no method declared that will allow another object to access the data inside this object. Listing 3.2 adds accessor methods to access the data. These are referred to generally as accessors and specifically as getters or setters. By using this coding best practice, the variables are encapsulated and can be accessed only through these methods.
Listing 3.2 Legacy Class Declaration with Accessors
// // My_First_ProjectAppDelegate.h // My First Project // // Created by Sams on 6/14/11. // Copyright 2011 __MyCompanyName__. All rights reserved. // #import <Cocoa/Cocoa.h> @interface My_First_ProjectAppDelegate : NSObject <NSApplicationDelegate> { @private NSWindow *window; } - (NSWindow*) getWindow; - (NSWindow*) setWindow: (NSWindow*)newindow; - (IBAction)saveAction:sender; @end
Listing 3.3 demonstrates the use of declared properties in Objective C 2.0.
Listing 3.3 Modern Class Declaration
// // My_First_ProjectAppDelegate.h // My First Project // // Created by Sams on 6/14/11. // Copyright 2011 __MyCompanyName__. All rights reserved. // #import <Cocoa/Cocoa.h> @interface My_First_ProjectAppDelegate : NSObject <NSApplicationDelegate> { } @property NSWindow* window; - (IBAction)saveAction:sender; @end
The individual declarations of the variables are gone; they are replaced by declared properties that are implemented with compiler directives. As compiler directives, these are merely instructions to the compiler. They are not part of the program's syntax.
Synthesizing a Property
A declared property directive works together with a companion synthesize directive that appears in the implementation file. The companion synthesize directive to the property declaration is shown in Listing 3.4.
Listing 3.4 Synthesize Directives to Match Listing 3.3
@synthesize window;
When the program is compiled, these two directives generate code. If, as in Listing 3.3, the variables are not declared, the declarations are created. They will look just like the code that has been typed into Listings 3.1 and 3.2. In addition, getters and setters will be automatically generated. They will look exactly like those typed at the bottom of Listing 3.2.
And, perhaps most important, the declared properties allow for the use of dot syntax that automatically invokes the relevant accessors. It also provides the appearance of direct access to the encapsulated data of the object. Given the code in Listings 3.3 and 3.4, you could write the following code to reference the data within an object of type My_First_ProjectAppDelegate that has been instantiated with the name jf_My_First_ProjectAppDelegate:
jf_ My_First_ProjectAppDelegate.managedObjectContext
The appropriate accessor (getter or setter) will be invoked as needed. Note that within the implementation code of an object, you can always use self to refer to the object itself. Thus, you can write
self.managedObjectContext = <another managedObjectContext>;
Or
<myManagedObjectContext > = self.managedObjectContext;
You save a great deal of typing and make your code much more readable by using declared properties.
You can still declare the variables if you want to. At compile time, the same-named variables you have declared will be accessed by the property. However, a common use of properties is to reinforce the hiding of internal variables. The property declaration can provide a name that is used by programmers while the underlying variable is not accessed. This is common in the framework code you deal with.
For example, here is a declaration of a private variable:
NSWindow *__window;
Here is a companion property declaration:
@property nonatomic, retain, readonly NSWindow* window;
The synthesize directive would normally create the window variable because there is none. However, you can use the following synthesize directive to have the property's accessors, which are created during compilation by the synthesize directive, point to __window if you have declared it, as shown in Listing 3.5.
Listing 3.5 Using a Private Variable in a Property
@synthesize window = __window;
You can access the private variable by using its name if you are allowed to do so, which in practice generally means for code in the class itself. Thus, you can write:
__window = <something>;
Using dot syntax, you go through the property and, as a result, the following code can have the same effect:
self.window = <something>;
In practice, synthesize directives usually are a bit more complex. You can provide attributes by placing them in parentheses after the property directive. A common set of attributes in a synthesize directive is the following:
property (nonatomic, strong, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
Attributes reflect the reality of today's environment and the features of modern Objective-C. It is no longer enough to know that a variable is of a specific type. Many other attributes come into play, and the property directive allows you to set them. Its syntax also allows for the expansion of attributes in the future as the language evolves. Thus, property directives together with the appropriate attributes combine to create rich, useful objects that are easy to use and maintain over the lifespan of the app.
Table 3.1 shows the current set of attributes and the available values. The default values (having a synthesize directive create the accessors, assign, and atomic) are most commonly used. Notice also that the opposite of the atomic attribute is to omit it—in other words, there is no separate "nonatomic" attribute. (Over time, these attributes have changed to add new features. Consult the release notes for new versions of Xcode for these changes.)
Table 3.1. Attributes for Declared Properties
Attribute |
Values |
Notes |
Accessor |
getter = <name of your getter> |
Accessors are synthesized for you unless you provide your own. |
setter = <name of your setter> |
You can go further by specifying your own custom accessors. You will have to write them, but it might be worthwhile in special cases. |
|
Writability |
readwrite |
|
readonly |
||
Setter |
assign (default) |
|
Semantics |
retain |
Retains the object after assignment and releases the previous value. |
copy |
Copies the object and releases the previous value. |
|
Atomicity |
nonatomic |
Default is atomic so that getters and setters are thread-safe. |
Using Dynamic Properties
Instead of a synthesize directive, you can use a dynamic directive for any property. The format is as follows:
@dynamic myValue;
The dynamic directive indicates that your code is going to be providing the appropriate values for the property at runtime. This entails writing some rather complex code, but there is an alternative. Core Data implements the functionality promised by the dynamic directive, so you do not have to worry—just keep reading.