So far, you have declared all of your properties, instance variables, and methods in the class header file. The header is where a class advertises its properties and methods so that other objects will know how to interact with it.
However, not every property or method should be advertised in a class’s header. Some properties or methods may only be intended for use by the class or instances of the class. Such internal details are better declared in a class extension. A class extension is a set of declarations that is private. Only the class or instances of the class are able to use the properties, instance variables, or methods declared in a class extension.
For example, the officeAlarmCode property of BNREmployee should be private. The employee object needs to be able to access its alarm code, while non-employee objects do not need access to the alarm code and should not have it. You can make this happen by moving the declaration of officeAlarmCode from the header (BNREmployee.h) to a class extension.
Typically, class extensions are added to the class implementation file above the @implementation
block where methods are implemented.
In BNREmployee.m, create a class extension.
Then declare the officeAlarmCode property there:
#import "BNREmployee.h" // A class extension @interface BNREmployee () @property (nonatomic) unsigned int officeAlarmCode; @end @implementation BNREmployee ...
A class extension starts with @interface
and finishes off with @end
.
In fact, an extension looks a lot like a header, and both are known as “interfaces.”
However, in an extension, instead of the colon and the superclass name found in the header, there is a pair of empty parentheses.
In BNREmployee.h, remove the officeAlarmCode declaration:
#import "BNRPerson.h" @class BNRAsset; @interface BNREmployee : BNRPerson { NSMutableArray *_assets } @property (nonatomic) unsigned int employeeID;@property (nonatomic) unsigned int officeAlarmCode; @property (nonatomic) NSDate *hireDate; @property (nonatomic, copy) NSArray *assets; - (void)addAsset:(BNRAsset *)a; - (unsigned int)valueOfAssets; @end
Build and run the program. Its behavior has not changed. However, moving the officeAlarmCode declaration to a class extension has two related effects:
First, objects that are not instances of BNREmployee can no longer see this property. For instance, a non-BNREmployee object could attempt to access an employee’s alarm code like this:
BNREmployee *mikey = [[BNREmployee alloc] init]; unsigned int mikeysCode = mikey.officeAlarmCode;
This attempt would result in a compiler error that reads “No visible @interface declares the instance method officeAlarmCode”. The only interface that is visible to a non-BNREmployee object is the BNREmployee header. And because the officeAlarmCode property is declared in a class extension rather than in the header, it is not visible (and as such, is unavailable) to non-BNREmployee objects.
Second, the BNREmployee header has one less declaration and thus is a little bit simpler. This is a good thing. The header is intended to be a billboard; its job is to advertise what other developers need to know to make your class work in the code that they write. Too much information makes a header difficult to for other developers to read and use.
Now let’s look at a slightly different case for putting a declaration in a class extension instead of the class’s header. In BNREmployee.h, you declared an assets property that is an NSArray, an addAsset: method, and an _assets instance variable that is an NSMutableArray. A developer will see both the property and the instance variable advertised in the header and will be uncertain which you intended outsiders to use.
Now that you know about class extensions, the solution is simple: move the _assets instance variable to BNREmployee’s class extension. In BNREmployee.m, add this declaration:
#import "BNREmployee.h" // A class extension @interface BNREmployee () { NSMutableArray *_assets; } @property (nonatomic) unsigned int officeAlarmCode; @end @implementation BNREmployee ...
In BNREmployee.h, remove the _assets declaration:
#import "BNRPerson.h" @class BNRAsset; @interface BNREmployee : BNRPerson{ NSMutableArray *_assets} @property (nonatomic) unsigned int employeeID; @property (nonatomic) NSDate *hireDate; @property (nonatomic, copy) NSArray *assets; - (void)addAsset:(BNRAsset *)a; - (unsigned int)valueOfAssets; @end
Now the array of assets is only advertised as an immutable array, so non-BNREmployee objects will need to use the addAsset: method to manipulate this array. The fact that there is an NSMutableArray instance backing the assets property is a private implementation detail of the BNREmployee class.