4. Protocols and Categories
Protocols are a language feature similar to interfaces in Java. Objective-C does not have multiple inheritance, so protocols provide a way to define a set of methods that a class should implement. Protocols are most often used in implementing the Delegate pattern (see Item 23), but there are other uses as well. Getting to know them and using them can make code a lot more maintainable, as they are a good way of documenting an interface to your code.
Categories too are an important language feature of Objective-C. They provide a mechanism for adding methods to a class without having to subclass it as you would have to in other languages. This feature is made possible by the highly dynamic nature of the runtime but also comes with pitfalls that you should understand before using categories.
Item 23: Use Delegate and Data Source Protocols for Interobject Communication
Objects will often need to talk to each other and can do so in many ways. One such programming design pattern used extensively by Objective-C developers is known as the Delegate pattern, the essence of which is to define an interface that any object can conform to in order to become the delegate of another object. This other object then talks back to its delegate to get some information or to inform the delegate when interesting things happen.
Using this pattern enables the decoupling of data from business logic. For instance, a view in a user interface to display a list of data should be responsible only for the logic of how data is displayed, not for deciding what data should be displayed or what happens in data interaction. The view object can have properties that contain objects responsible for the data and event handling. These are known as the data source and delegate, respectively.
In Objective-C, the usual way of achieving this pattern is to use the protocol language feature, which is used throughout the frameworks that make up Cocoa. If you use this feature yourself, you’ll find that your own code will fit appropriately.
As an example, consider a class that is used to fetch data from the network. A class might do so to retrieve some data from a resource on a distant server. It might take a long time for the server to respond, and it would be rather bad practice to block while the data was being retrieved. So it’s common in this scenario to use the Delegate pattern, whereby the fetcher class has a delegate that it calls back once the data has been retrieved. Figure 4.1 illustrates this concept; the EOCDataModel object is the delegate of EOCNetworkFetcher. The EOCDataModel asks the EOCNetworkFetcher to perform a task asynchronously, and EOCNetworkFetcher tells its delegate, the EOCDataModel, when that task has completed.
Figure 4.1 A delegate callback. Note that the EOCDataModel instance doesn’t necessarily have to be the delegate but could be another object.
This pattern is easy to implement using Objective-C through the use of a protocol. In the case of Figure 4.1, the protocol might look like this:
@protocol EOCNetworkFetcherDelegate
- (void)networkFetcher:(EOCNetworkFetcher*)fetcher
didReceiveData:(NSData*)data;
- (void)networkFetcher:(EOCNetworkFetcher*)fetcher
didFailWithError:(NSError*)error;
@end
A delegate protocol is usually named as the class name followed by the word delegate, using camel casing for the whole name. Following this naming pattern will make your delegate protocol feel familiar to anyone using it.
The protocol provides a property on the class that has the delegate. In our example, this is the EOCNetworkFetcher class. Therefore, the interface would look like this:
@interface EOCNetworkFetcher : NSObject
@property (nonatomic, weak)
id <EOCNetworkFetcherDelegate> delegate;
@end
It’s important to make sure that the property is defined as weak and not strong, since it must be a nonowning relationship. Usually, the object that will be the delegate will also hold onto the object. An object wanting to use an EOCNetworkFetcher, for example, will keep hold of it until finished with it. If the property holding the delegate were an owning relationship using the strong attribute, a retain cycle would be introduced. For this reason, a delegate property will always be defined using either the weak attribute to benefit from autonilling (see Item 6) or unsafe_unretained if autonilling is not required. The ownership diagram shown in Figure 4.2 illustrates this.
Figure 4.2 Ownership diagram showing delegate being nonretained in order to avoid retain cycle
Implementing the delegate is a matter of declaring that your class implements the delegate protocol and then implementing any methods you want from the protocol. You can declare that a class implements a protocol in either the interface or the class-continuation category (see Item 27). Declaring it in the interface is useful if you want to advertise to others that you implement that protocol; however, in the case of delegates, it’s usual to care only about it internally. So it’s common to declare it in the class-continuation category like so:
@implementation EOCDataModel () <EOCNetworkFetcherDelegate>
@end
@implementation EOCDataModel
- (void)networkFetcher:(EOCNetworkFetcher*)fetcher
didReceiveData:(NSData*)data {
/* Handle data */
}
- (void)networkFetcher:(EOCNetworkFetcher*)fetcher
didFailWithError:(NSError*)error {
/* Handle error */
}
@end
Usually, methods within a delegate protocol will be optional, since the object being the delegate may not care about all the methods. In the example, the DataModel class may not care that an error occurred, so might not implement the networkFetcher:didFailWithError: method. To indicate this, delegate protocols usually make most or all methods optional by applying the @optional keyword:
@protocol EOCNetworkFetcherDelegate
@optional
- (void)networkFetcher:(EOCNetworkFetcher*)fetcher
didReceiveData:(NSData*)data;
- (void)networkFetcher:(EOCNetworkFetcher*)fetcher
didFailWithError:(NSError*)error;
@end
Before any optional method is called on the delegate, introspection (see Item 14) should be used to determine whether the delegate responds to that selector. In the case of EOCNetworkFetcher, it would look like this:
NSData *data = /* data obtained from network */;
if ([_delegate respondsToSelector:
@selector(networkFetcher:didReceiveData:)])
{
[_delegate networkFetcher:self didReceiveData:data];
}
The respondsToSelector: method is used to determine whether the delegate has implemented that method. If yes, it’s called; otherwise, nothing happens. In that way, the delegate method is truly optional, and nothing will break if it’s not implemented. Even if no delegate is set, it will still function perfectly well, since sending a message to nil will make the if statement evaluate to false.
Getting the name of your delegate methods correct is also important. The name should indicate exactly what is happening and why the delegate is being told something. In the example, the delegate method reads very clearly, saying that a certain network fetcher object has just received some data. You should also always pass the instance that is delegating, just as in the example, so that the delegate method implementation can switch based on the specific instance. For example:
- (void)networkFetcher:(EOCNetworkFetcher*)fetcher
didReceiveData:(NSData*)data
{
if (fetcher == _myFetcherA) {
/* Handle data */
} else if (fetcher == _myFetcherB) {
/* Handle data */
}
}
Here, the object being the delegate has two network fetchers and so must be told which network fetcher is telling it that data has been received. Without being told this information, the object would be able to use only one network fetcher at a time, which would not be ideal.
The delegate methods can also be used to obtain information from the delegate. For example, the network fetcher class might want to provide a mechanism such that if it encounters a redirect while fetching the data, it asks its delegate whether the redirect should occur. The delegate method for this may look like the following:
- (BOOL)networkFetcher:(EOCNetworkFetcher*)fetcher
shouldFollowRedirectToURL:(NSURL*)url;
This example should make it easy to see why it’s called the Delegate pattern, since the object is delegating responsibility for an action to another class.
Protocols can also be used to provide an interface through which the data that a class requires is obtained. This other use of the Delegate pattern is referred to as the Data Source pattern because its aim is to provide data to the class. The flow of information is toward the class; with a normal delegate, the flow of information is away from the class. This flow is illustrated in Figure 4.3.
Figure 4.3 Flow of information is out of the class for a delegate and into the class for a data source.
For example, a list view object in a user interface framework might use a data source protocol to provide the data to show in the list. The list view may also have a delegate to handle user interaction with the list. The separation of the data source and delegate protocols provides a cleaner interface by separating distinct portions of logic. In addition, you could have one object be the data source and another be the delegate. However, the same object usually ends up being both.
With the Delegate and Data Source patterns where implementing any of the methods is optional, you will have a lot of code that looks like the following:
if ([_delegate respondsToSelector:
@selector(someClassDidSomething:)])
{
[_delegate someClassDidSomething];
}
Checking whether the delegate responds to a certain selector is pretty quick, but if you do this repeatedly, responses after the first one are potentially redundant. If the delegate hasn’t changed, it’s extremely unlikely that it has suddenly started or stopped responding to the given selector. For this reason, it is common to make the optimization of caching whether the delegate responds to the methods in the protocol. For example, the network fetcher in the example has a delegate method that is called back as a progress indicator, telling the delegate every so often how far the network fetch has progressed. This method may get called many times during the life cycle of the fetcher, and checking each time whether the delegate responds to the selector is redundant.
Consider the expanded delegate protocol for the selector defined like so:
@protocol EOCNetworkFetcherDelegate
@optional
- (void)networkFetcher:(EOCNetworkFetcher*)fetcher
didReceiveData:(NSData*)data;
- (void)networkFetcher:(EOCNetworkFetcher*)fetcher
didFailWithError:(NSError*)error;
- (void)networkFetcher:(EOCNetworkFetcher*)fetcher
didUpdateProgressTo:(float)progress;
@end
The single, optional method networkFetcher:didUpdateProgressTo: has been called in here. The best way for the caching to be done is to use the bitfield data type. This feature from C is often overlooked but is excellent for this purpose. It allows you to define that a certain field of a structure should be sized a certain number of bits. It looks like this:
struct data {
unsigned int fieldA : 8;
unsigned int fieldB : 4;
unsigned int fieldC : 2;
unsigned int fieldD : 1;
};
In this structure, fieldA will use exactly 8 bits, fieldB will use 4 bits, fieldC will use 2 bits, and fieldD will use 1 bit. So fieldA will be able to hold values from 0 to 255, and fieldD will be able to hold either 0 or 1. The latter is of interest for caching what methods a delegate implements. If you create a structure that uses only 1-bit bitfields, you can pack in a lot of Booleans into a small amount of data. For the example of the network fetcher, you would make the instance have a structure as one of its instance variables containing the bitfield, one variable for each delegate method. The structure would look like this:
@interface EOCNetworkFetcher () {
struct {
unsigned int didReceiveData : 1;
unsigned int didFailWithError : 1;
unsigned int didUpdateProgressTo : 1;
} _delegateFlags;
}
@end
Here, I have used the class-continuation category to add the instance variable, as explained in Item 27: The instance variable that has been added is a structure containing three fields, one for each of the optional delegate methods. The structure can be queried and set by using the following from within the EOCNetworkFetcher class:
// Set flag
_delegateFlags.didReceiveData = 1;
// Check flag
if (_delegateFlags.didReceiveData) {
// Yes, flag set
}
This structure is used to cache whether the delegate responds to the given selectors. This can be done from within the setter accessor method for the delegate property:
- (void)setDelegate:(id<EOCNetworkFetcher>)delegate {
_delegate = delegate;
_delegateFlags.didReceiveData =
[delegate respondsToSelector:
@selector(networkFetcher:didReceiveData:)];
_delegateFlags.didFailWithError =
[delegate respondsToSelector:
@selector(networkFetcher:didFailWithError:)];
_delegateFlags.didUpdateProgressTo =
[delegate respondsToSelector:
@selector(networkFetcher:didUpdateProgressTo:)];
}
Then, instead of checking whether the delegate responds to the given selector each time a delegate method is called, the flags are checked instead:
if (_delegateFlags.didUpdateProgressTo) {
[_delegate networkFetcher:self
didUpdateProgressTo:currentProgress];
}
If this is called many times, it’s a worthwhile optimization to make. The need to make this optimization depends on your code. You should instrument your code and determine where the hot spots are and keep this under your belt as a potential for a speed improvement. It’s most likely to make an impact in data source protocols in which the data source is asked repeatedly for each individual piece of data.
Things to Remember
Use the Delegate pattern to provide an interface to your objects that need to tell other objects about pertinent events.
Define a protocol with potentially optional methods to define the interface that your delegate should support.
Use the Delegate pattern when an object needs to obtain data from another object. In this case, it is often referred to as a “data source protocol.”
If required, implement the bitfield structure approach to cache which methods a delegate responds to from the protocol.
Item 24: Use Categories to Break Class Implementations into Manageable Segments
A class can easily become bloated with many methods all interspersed throughout a huge implementation file. Sometimes, that’s just the way it is, and no amount of refactoring to split up the class can make the situation better. In that case, Objective-C categories can be used to great effect to split up a class into logical subsections that aid not only development but also debugging.
Consider a class that models a person. The person might have a few methods available on it:
#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
@property (nonatomic, strong, readonly) NSArray *friends;
- (id)initWithFirstName:(NSString*)firstName
andLastName:(NSString*)lastName;
/* Friendship methods */
- (void)addFriend:(EOCPerson*)person;
- (void)removeFriend:(EOCPerson*)person;
- (BOOL)isFriendsWith:(EOCPerson*)person;
/* Work methods */
- (void)performDaysWork;
- (void)takeVacationFromWork;
/* Play methods */
- (void)goToTheCinema;
- (void)goToSportsGame;
@end
The implementation for such a class would contain all methods in a long list in one big file. As more methods are added to the class, the file will only get longer and more unmanageable. So it is often useful to split up such a class into separate, distinct portions. For example, the preceding could be rewritten to make use of categories:
#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
@property (nonatomic, strong, readonly) NSArray *friends;
- (id)initWithFirstName:(NSString*)firstName
andLastName:(NSString*)lastName;
@end
@interface EOCPerson (Friendship)
- (void)addFriend:(EOCPerson*)person;
- (void)removeFriend:(EOCPerson*)person;
- (BOOL)isFriendsWith:(EOCPerson*)person;
@end
@interface EOCPerson (Work)
- (void)performDaysWork;
- (void)takeVacationFromWork;
@end
@interface EOCPerson (Play)
- (void)goToTheCinema;
- (void)goToSportsGame;
@end
Now, each distinct part of the class is split into separate categories of methods. Not surprisingly, this language feature is called categories! In the example, the bases of the class, including the properties and initializer, are declared within the main implementation. Additional sets of methods, relating to different types of actions, are split into categories.
The entire class could still be defined within one interface file and one implementation file, but as the categories grow, the single implementation file could easily become unmanageable. In that case, categories could be extracted into their own files. For example, the categories in EOCPerson could be extracted into separate files:
EOCPerson+Friendship(.h/.m)
EOCPerson+Work(.h/.m)
EOCPerson+Play(.h/.m)
For example, the friendship category would look like this:
// EOCPerson+Friendship.h
#import "EOCPerson.h"
@interface EOCPerson (Friendship)
- (void)addFriend:(EOCPerson*)person;
- (void)removeFriend:(EOCPerson*)person;
- (BOOL)isFriendsWith:(EOCPerson*)person;
@end
// EOCPerson+Friendship.m
#import "EOCPerson+Friendship.h"
@implementation EOCPerson (Friendship)
- (void)addFriend:(EOCPerson*)person {
/* ... */
}
- (void)removeFriend:(EOCPerson*)person {
/* ... */
}
- (BOOL)isFriendsWith:(EOCPerson*)person {
/* ... */
}
@end
The class has been split up into much more manageable chunks of code that can be inspected individually. You must then remember to import the EOCPerson.h header along with any category headers anywhere that the category methods are required. But this is a good way of making your code more manageable.
Even if a class is not too large, using categories to split it into subsections can be a useful way to segment code into functional areas. An example of such a class that does this within Cocoa is NSURLRequest and its mutable counterpart, NSMutableURLRequest. This class performs requests to obtain data from a URL and is used mostly with HTTP to obtain data from a server on the Internet somewhere, but the class is generic and can be used with other protocols as well. However, HTTP requests require additional information to be set on them beyond what standard URL requests require, such as the HTTP methods (GET, POST, etc.) or HTTP headers.
But NSURLRequest is not easily subclassed, since it wraps a set of C functions that act on a CFURLRequest data structure, including all the HTTP methods. So the HTTP-specific methods are simply added to NSURLRequest as a category called NSHTTPURLRequest, and the mutation methods are added to NSMutableURLRequest as a category called NSMutableHTTPURLRequest. This way, all the underlying CFURLRequest functions are wrapped within the same Objective-C class, but the HTTP-specific methods are split into a separate area to avoid confusion such that users might wonder why they are able to set the HTTP method on a request object that uses the protocol FTP.
Another useful reason to split up a class into categories is for debugging purposes; the category name is added to the symbol for all methods within that category. For example, the addFriend: method has the following symbol name:
-[EOCPerson(Friendship) addFriend:]
When it appears in backtraces in a debugger, it will look something like this:
frame #2: 0x00001c50 Test'-[EOCPerson(Friendship) addFriend:]
+ 32 at main.m:46
The category name within the backtrace can be extremely useful for pinpointing exactly which functional area of the class the method relates to, which is particularly useful when certain methods should be regarded as private. In that case, it might be useful to create a category called Private that contains all these methods. Such categories’ methods are generally used only internally to a class or framework and not exposed. If a user finds such a method—perhaps by reading a backtrace—the private in the name should help to indicate that the method should not be used directly. In a way, it is a method of self-documenting code.
The idea of a Private category is useful when creating a library that will be shared by other developers. Often, some methods should not form part of the public API but are quite nice to use within the library itself. In this scenario, creating a Private category is a good option, since its header can be imported wherever the methods need to be used within the library. If that category header is not released as part of the library’s release, consumers of the library would have no idea that those private methods exist.
Things to Remember
Use categories to split a class implementation into more manageable fragments.
Create a category called Private to hide implementation detail of methods that should be considered as private.
Item 25: Always Prefix Category Names on Third-Party Classes
Categories are commonly used to add functionality to an existing class for which you don’t own the source code. This is an extremely powerful feature, but it is also very easy to overlook a problem that can arise when doing so. The problem stems from the fact that methods in a category are added to the class as if they were part of the class itself. This happens at runtime when the category is loaded. The runtime goes through each method implemented by a category and adds it to the class’s method list. If a method in a category already exists, the category method wins and becomes the implementation of that method. This overriding can happen over and over again in fact, so a method in one category can override a method in another category that overrides a method in the main class implementation. The last category to be loaded wins.
For example, suppose that you decide to add a category on NSString to provide some helper methods for dealing with strings related to HTTP URLs. You may decide to define the category like this:
@interface NSString (HTTP)
// Encode a string with URL encoding
- (NSString*)urlEncodedString;
// Decode a URL encoded string
- (NSString*)urlDecodedString;
@end
This reads absolutely fine here, but consider what might happen if another category is added that also adds methods to NSString. The second category might also add a method called urlEncodedString but might implement it slightly differently and incorrectly for your purposes. If loaded after your category, the second category will win, and your code will be calling the implementation defined in that category. This may cause your own code to not work properly, as it will get unexpected results. This bug may be hard to track down, as you may not know that your implementation of urlEncodedString is not the code being run.
A typical way to overcome this problem is to namespace the category name and the methods it defines. The only way within Objective-C to namespace is to prepend names with a common prefix. Just as you should do with classes (see Item 15), you should choose a prefix that suits the situation. Often, the prefix will be the same one used with the rest of your application or library. Thus, the NSString category might become the following for a prefix of ABC:
@interface NSString (ABC_HTTP)
// Encode a string with URL encoding
- (NSString*)abc_urlEncodedString;
// Decode a URL encoded string
- (NSString*)abc_urlDecodedString;
@end
Technically, you don’t have to namespace the name of the category itself. Nothing goes wrong if you have two categories named the same. But it’s not good practice, and you will get a compiler warning that looks like this:
warning: duplicate definition of category 'HTTP' on interface
'NSString'
It is not impossible for another category to override your method, but it’s now much less likely, since another library is unlikely to use the same prefix. It also avoids the potential for the developer of the class to add in an update a method that clashes with your added method. For instance, if Apple were to add the method urlEncodedString to NSString, your method of the same name would override Apple’s, which is undesirable, since other users of NSString may be expecting the output from Apple’s implementation. Or Apple’s implementation may include side effects that your method does not provide and potentially produce inconsistencies internally, producing bugs that are hard to find.
You also need to remember that methods added to a class in a category are available to every single instance of that class within your application. If you add methods to a system-supplied class, such as NSString, NSArray or NSNumber, for example, every instance of those classes will be able to call your added methods even if they are not created from your own code. If you accidentally override a method by using the category or clash with a category added by a third-party library, strange bugs can occur because you think it’s your code being executed when it’s not. Similarly, purposefully overriding methods in a category is bad practice, especially if your code will form a library used by other developers who might rely on the existing functionality, for instance. Worse still, another developer may override the same method, in which case the method winning in the overriding process is undefined. This is another example of why you should namespace method names in categories.
Things to Remember
Always prepend your naming prefix to the names of categories you add to classes that are not your own.
Always prepend your naming prefix to the method names within categories you add to classes that are not your own.
Item 26: Avoid Properties in Categories
A property is a way of encapsulating data (see Item 6). Although it is technically possible to declare a property in a category, you should avoid doing so if possible. The reason is that it is impossible for a category, except the class-continuation category (see Item 27), to add instance variables to a class. Therefore, it is also impossible for the category to synthesize an instance variable to back the property.
Suppose that after reading Item 24, you have decided to use categories to split up into distinct fragments your implementation of a class representing a person. You might decide to have a friendship category for all the methods relating to manipulating the list of friends associated with a person. Without knowing about the problem described earlier, you may also put the property for the list of friends within the friendship category, like so:
#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
- (id)initWithFirstName:(NSString*)firstName
andLastName:(NSString*)lastName;
@end
@implementation EOCPerson
// Methods
@end
@interface EOCPerson (Friendship)
@property (nonatomic, strong) NSArray *friends;
- (BOOL)isFriendsWith:(EOCPerson*)person;
@end
@implementation EOCPerson (Friendship)
// Methods
@end
If you compile this, however, you would end up with compiler warnings:
warning: property 'friends' requires method 'friends' to be
defined - use @dynamic or provide a method implementation in
this category [-Wobjc-property-implementation]
warning: property 'friends' requires method 'setFriends:' to be
defined - use @dynamic or provide a method implementation in
this category [-Wobjc-property-implementation]
This slightly cryptic warning means that instance variables cannot be synthesized by a category and, therefore, that the property needs to have the accessor methods implemented in the category. Alternatively, the accessor methods can be declared @dynamic, meaning that you are declaring that they will be available at runtime but cannot be seen by the compiler. This might be the case if you are using the message-forwarding mechanism (see Item 12) to intercept the methods and provide the implementation at runtime.
To get around the problem of categories not being able to synthesize instance variables, you can use associated objects (see Item 10). For the example, you would need to implement the accessors within the category as follows:
#import <objc/runtime.h>
static const
char *kFriendsPropertyKey = "kFriendsPropertyKey";
@implementation EOCPerson (Friendship)
- (NSArray*)friends {
return
objc_getAssociatedObject(self, kFriendsPropertyKey);
}
- (void)setFriends:(NSArray*)friends {
objc_setAssociatedObject(self,
kFriendsPropertyKey,
friends,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
This works, but it’s less than ideal. It’s a fair amount of boilerplate and is prone to errors with memory management because it’s easy to forget that the property is implemented like this. For example, you may change the memory-management semantics by changing the property attributes. However, you would also need to remember to change the memory-management semantics of the associated object in the setter. So although this is not a bad solution, it is not one that I recommend.
Also, you may want the instance variable backing the friends array to be a mutable array. You could take a mutable copy, but it’s yet another vector for confusion to enter your code base. It is therefore much cleaner to define properties in the main interface definition rather than to define them in categories.
In this example, the correct solution is to keep all the property definitions in the main interface declaration. All the data encapsulated by a class should be defined in the main interface, the only place where instance variables (the data) can be defined. Since they are just syntactic sugar for defining an instance variable and associated accessor methods, properties fall under the same rule. Categories should be thought of as a way to extend functionality of the class, not the data that it encapsulates.
That being said, sometimes read-only properties can be successfully used within categories. For example, you might want to create a category on NSCalendar to return an array containing the months as strings. Since the method is not accessing any data and the property is not backed by an instance variable, you could implement the category like this:
@interface NSCalendar (EOC_Additions)
@property (nonatomic, strong, readonly) NSArray *eoc_allMonths;
@end
@implementation NSCalendar (EOC_Additions)
- (NSArray*)eoc_allMonths {
if ([self.calendarIdentifier
isEqualToString:NSGregorianCalendar])
{
return
@[@"January", @"February",
@"March", @"April",
@"May", @"June",
@"July", @"August",
@"September", @"October",
@"November", @"December"];
} else if ( /* other calendar identifiers */ ) {
/* return months for other calendars */
}
}
@end
Autosynthesis of an instance variable to back the property will not kick in, because all the required methods (only one in this read-only case) have been implemented. Therefore, no warnings will be emitted by the compiler. Even in this situation, however, it is generally better to avoid using a property. A property is meant to be backed by data held by the class. A property is for encapsulating data. In this example, you would instead declare the method to retrieve the list of months within the category:
@interface NSCalendar (EOC_Additions)
- (NSArray*)eoc_allMonths;
@end
Things to Remember
Keep all property declarations for encapsulated data in the main interface definition.
Prefer accessor methods to property declarations in categories, unless it is a class-continuation category.
Item 27: Use the Class-Continuation Category to Hide Implementation Detail
Often, you will want a class to contain more methods and instance variables than are exposed externally. You could expose them externally and document that they are private and shouldn’t be relied on. After all, no method or instance variable is truly private in Objective-C, owing to the way the dynamic messaging system works (see Item 11). However, it is good practice to expose publicly only what needs to be exposed. So what do you do about methods and instance variables that should not be exposed publicly but whose existence you still want to document? That’s where a special category—the “class-continuation category”—can come in handy.
The class-continuation category, unlike normal categories, must be defined in the implementation file of the class for which it is a continuation. Importantly, it is the only category that is allowed to declare extra instance variables. Also, this category doesn’t have a specific implementation. Any method defined within it is expected to appear in the main implementation of the class. Unlike other categories, a class-continuation category has no name. A class-continuation category for a class named EOCPerson would look like this:
@interface EOCPerson ()
// Methods here
@end
Why is such a category useful? It’s useful because both methods and instance variables can be defined there. This is possible only because of the nonfragile ABI (see Item 6 for more detail about this), which means that the size of an object does not have to be known in order to use it. Therefore, instance variables do not have to be defined in the public interface, since consumers of the class do not have to know their layout. For this reason, adding instance variables to a class was made possible in the class-continuation category, as well as in the implementation of a class. To do this, you simply need to add some braces in the right place and put the instance variables in there:
@interface EOCPerson () {
NSString *_anInstanceVariable;
}
// Method declarations here
@end
@implementation EOCPerson {
int _anotherInstanceVariable;
}
// Method implementations here
@end
What is the point of doing this? You could define instance variables in the public interface. But a benefit to hiding them away in the class-continuation category or implementation block is that they are known about only internally. Even if you mark them as private in the public interface, you are still leaking implementation detail. For example, suppose that you don’t want others to know about the existence of a supersecret class used only internally. If one of your classes owns an instance of this class and you were to declare the instance variable in the public interface, it would look like this:
#import <Foundation/Foundation.h>
@class EOCSuperSecretClass;
@interface EOCClass : NSObject {
@private
EOCSuperSecretClass *_secretInstance;
}
@end
The fact that a class named EOCSuperSecretClass exists has been leaked. You could get around this by not strongly typing the instance variable and declaring it of id type instead of EOCSuperSecretClass*. However, this would be less than ideal because you would lose any help from the compiler when using it internally. And why should you miss out on help like that just because you don’t want to expose something? This is where the class-continuation category can help. Now it can be declared like so:
// EOCClass.h
#import <Foundation/Foundation.h>
@interface EOCClass : NSObject
@end
// EOCClass.m
#import "EOCClass.h"
#import "EOCSuperSecretClass.h"
@interface EOCClass () {
EOCSuperSecretClass *_secretInstance;
}
@end
@implementation EOCClass
// Methods here
@end
Similarly, the instance variable could have been added to the implementation block, semantically equivalent to adding it to the class-continuation category and more a matter of preference. I prefer adding it to the category because it keeps all data definitions in the same place. You may also have properties defined in the class-continuation category, so it’s good to declare extra instance variables here also. This instance variable is not truly private, since it is always possible to hack around using methods from the runtime, but to all intents and purposes, it is private. Also, since it is not declared in the public header, it is much more hidden if you were to ship this code as part of a library.
Another place where this is particularly useful is with Objective-C++ code. In this hybrid of Objective-C and C++, code written in both languages can be used. Often, back ends to games are written in C++ for portability reasons. Other times, you may need to use C++ because you’re interfacing with a third-party library that has only C++ bindings. For these times, the class-continuation category can come in handy as well. Suppose that previously, you would have written the class like so:
#import <Foundation/Foundation.h>
#include "SomeCppClass.h"
@interface EOCClass : NSObject {
@private
SomeCppClass _cppClass;
}
@end
The implementation file for this class would be called EOCClass.mm, where the .mm extension indicates to the compiler that the file should be compiled as Objective-C++. Without this, the inclusion of SomeCppClass.h would be impossible. However, note that the C++ class SomeCppClass has had to be imported fully because the definition needs to be fully resolved such that the compiler knows how big the _cppClass instance variable is. So any file that includes EOCClass.h to use the class also needs to be compiled as Objective-C++, since it will also be including the SomeCppClass header file. This can easily spiral out of control and end up with your entire application being compiled as Objective-C++. This is perfectly fine, but I consider this to be fairly ugly, especially if the code is being shipped as a library for use in other applications. It’s not very nice for a third-party developer to have to rename all files to have the .mm extension.
You may think that one way to get around the problem described is to forward declare the C++ class instead of importing its header and then make the instance variable a pointer to an instance, as follows:
#import <Foundation/Foundation.h>
class SomeCppClass;
@interface EOCClass : NSObject {
@private
SomeCppClass *_cppClass;
}
@end
The instance variable needs to be a pointer now, since if it were a nonpointer, the compiler wouldn’t be able to work out the size the instance variable needed to be and would cause an error. Pointers are all of fixed size, so the compiler simply needs to know the type to which the pointer is pointing. But this exhibits the same problem in that any class importing the EOCClass header will meet the class keyword, which is a C++ keyword and therefore will need to be compiled as Objective-C++. This is ugly and unnecessary, since the instance variable is private anyway, so why should other classes even care that it exists? Well, class-continuation category to the rescue once again. Using it makes the class look like so:
// EOCClass.h
#import <Foundation/Foundation.h>
@interface EOCClass : NSObject
@end
// EOCClass.mm
#import "EOCClass.h"
#include "SomeCppClass.h"
@interface EOCClass () {
SomeCppClass _cppClass;
}
@end
@implementation EOCClass
@end
Now the EOCClass header is free from any C++, and consumers of that header will not even be aware that underneath, it is riddled with C++. This pattern is seen in some of the system libraries, such as where WebKit, the web browser framework, is written largely in C++ and exposed through a clean Objective-C interface. This pattern is also seen in CoreAnimation, where a lot of the back-end code is written in C++ but exposed through a pure Objective-C interface.
Another good use of the class-continuation category is to expand properties that are read-only in the public interface but need to be set internally. Usually, you will want to set via the setter accessor method rather than direct access to the instance variable (see Item 7) because it will trigger Key-Value Observing (KVO) notifications, to which another object may be listening. A property that appears in the class-continuation or any other category and in the class interface must have the exact same attributes, with the exception that the read-only status can be expanded to read-write. For example, consider a class representing a person with a public interface like this:
#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
- (id)initWithFirstName:(NSString*)firstName
lastName:(NSString*)lastName;
@end
Usually, a class-continuation category would be defined to expand the status of the two properties to read-write:
@interface EOCPerson ()
@property (nonatomic, copy, readwrite) NSString *firstName;
@property (nonatomic, copy, readwrite) NSString *lastName;
@end
That is all that needs to be done. Now the implementation of EOCPerson is free to use the setter accessor either by calling setFirstName: or setLastName: or by using the property dot syntax. Doing so can be a very useful way to keep your objects immutable publicly but still be able to manage the data internally as required. Therefore, the data encapsulated by the class is controlled by the instance itself rather than being able to be changed externally. See Item 18 for more about this topic. Note that there is a potential for race conditions to be introduced with this approach if an observer reads a property at the same time as the property is written internally. Sensible use of synchronization (see Item 41) will mitigate this problem.
Another good use of the class-continuation category is to declare private methods that are going to be used only inside the implementation of the class. This is useful because it documents the methods available inside the class implementation. It looks like this:
@interface EOCPerson ()
- (void)p_privateMethod;
@end
Here, the prefix idea from Item 20 is used to indicate a private method. You don’t strictly need to declare methods before using them in recent compiler versions. However, it is still often a good idea to do so in a class-continuation category like this, as it is a way to document what methods exist in one place. I often like to write the method prototypes like this first, before implementing the class. Then I go through and implement the methods. It is a great way to improve readability of a class.
Finally, the class-continuation category is a good place to state that your object conforms to protocols that should be considered private. Often, you don’t want to leak information that you conform to a certain protocol in the public interface, maybe because the protocol is part of your private API. For example, consider that EOCPerson conformed to a protocol called EOCSecretDelegate. Using the public interface, it would look like this:
#import <Foundation/Foundation.h>
#import "EOCSecretDelegate.h"
@interface EOCPerson : NSObject <EOCSecretDelegate>
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
- (id)initWithFirstName:(NSString*)firstName
lastName:(NSString*)lastName;
@end
You may think that you could just forward declare the EOCSecretDelegate protocol rather than importing it (or rather, the header file in which it is defined). You would forward declare it like this, instead of the import:
@protocol EOCSecretDelegate;
However, any place that the EOCPerson header would be imported, the compiler would emit the following warning:
warning: cannot find protocol definition for 'EOCSecretDelegate'
It’s warning you because it knows that it will never be able to know what methods are included in the protocol without being able to see its definition. But since this is a private internal protocol, you don’t even want to leak its name. Class-continuation category to the rescue again! Instead of declaring that EOCPerson conforms to EOCSecretDelegate in the public interface, you simply do it in the class-continuation category:
#import "EOCPerson.h"
#import "EOCSecretDelegate.h"
@interface EOCPerson () <EOCSecretDelegate>
@end
@implementation EOCPerson
/* ... */
@end
The public interface can have all references to EOCSecretDelegate removed. The private protocol is no longer exposed, and consumers would have to do deep introspection to find out that it exists.
Things to Remember
Use the class-continuation category to add instance variables to a class.
Redeclare properties in the class-continuation category as read-write if they are read-only in the main interface, if the setter accessor is required internally within the class.
Declare method prototypes for private methods within the class-continuation category.
Use the class-continuation category to declare protocols that your class conforms to that you want to keep private.
Item 28: Use a Protocol to Provide Anonymous Objects
Protocols define a set of methods that a conforming object should (or must if not optional) implement. They can therefore be used to hide implementation detail in your code’s API by returning objects that are typed as a plain id that conforms to a protocol. This way, the specific class name is not leaked in the API. This can be useful when you want to have many different classes behind the interface and don’t want to specify the full class. It might change, for example, or many different classes can be returned that don’t fit into a standard class hierarchy where you can supply the common base class as the type.
This concept is often referred to as anonymous objects, which is not like the concept of anonymous objects in other languages, where it refers to the ability to create a class inline with no name. In Objective-C, this is not the case. In Item 23, where delegates and data sources are explained, the use of these anonymous objects can be seen. For example, a property definition for a delegate might look like this:
@property (nonatomic, weak) id <EOCDelegate> delegate;
The type is id<EOCDelegate>, and the class of the object can therefore be absolutely anything; it doesn’t even have to derive from NSObject. It’s fine so long as it conforms to EOCDelegate. To the class that has this property, the delegate is anonymous. It could, if it so desired, work out the class of the object at runtime (see Item 14). However, doing so would be seen as bad practice, since the contract dictated by the property type indicates that the class doesn’t matter.
Another example of this concept in action is with NSDictionary. The standard memory-management semantics of the keys in a dictionary is that they are copied while the values are retained. In a mutable dictionary, therefore, the method signature to set a key-value pair is this:
- (void)setObject:(id)object forKey:(id<NSCopying>)key
The key parameter is typed as id<NSCopying>, since it just needs to be any object type that conforms to NSCopying so that the copy message can be sent to it successfully (see Item 22). The key can be thought of as being anonymous. Just as with the delegate property, the dictionary doesn’t care about the class, and it should never need to. It simply needs to know that it can send the instance the copy message.
An example of using this idea of anonymous objects being used for objects returned from a library is where the library handles database connections. You may not want to leak the class that handles the connection, since it might be a different one for different databases. Without a sane way for them to inherit from the same base class, you would be forced to return something of type id. However, you could create a protocol to define the common methods that all database connections have and declare that the object conforms to it. Such a protocol may look like this:
@protocol EOCDatabaseConnection
- (void)connect;
- (void)disconnect;
- (BOOL)isConnected;
- (NSArray*)performQuery:(NSString*)query;
@end
Then you may have a database handler singleton that is used to provide database connections. Its interface may look like this:
#import <Foundation/Foundation.h>
@protocol EOCDatabaseConnection;
@interface EOCDatabaseManager : NSObject
+ (id)sharedInstance;
- (id<EOCDatabaseConnection>)connectionWithIdentifier:
(NSString*)identifier;
@end
This way, the class that handles the database connections is not leaked, and different classes from potentially different frameworks can be returned by the same method. All the consumer of this API cares about is that the object returned can be connected and disconnected and queries performed. That last point is particularly important. In this example, the back-end code to handle database connections could be using many different third-party libraries to connect to each different database type (e.g., MySQL, PostgreSQL). It may therefore be impossible to make all the connection classes inherit from the same base class as they are in these third-party libraries. So the anonymous-object approach could be used where simple wrappers are created as subclasses of each of these third-party classes, to make them conform to the EOCDatabaseConnection protocol. Then these classes can be returned by the connectionWithIdentifier: method. In future versions, the back-end classes can be swapped out without any change to the public API.
These anonymous types can also be useful when you want to convey that it is unimportant what the type of the object is but rather that it’s more important that the object implements certain methods. Even if the type is always a certain class in your implementation, you might still want to use an anonymous type with a protocol to indicate that the type is not important.
An example of this approach is seen in the CoreData framework. The class called NSFetchedResultsController handles the results of a query to a CoreData database and splits the data into sections, if required. The sections can be accessed through a property on the results controller called sections. Rather than being an array of fully typed objects, this is an array of objects that conform to the NSFetchedResultsSectionInfo protocol. Using the controller to obtain section information looks like this:
NSFetchedResultsController *controller = /* some controller */;
NSUInteger section = /* section index to query */;
NSArray *sections = controller.sections;
id <NSFetchedResultsSectionInfo> sectionInfo = sections[section];
NSUInteger numberOfObjects = sectionInfo.numberOfObjects;
The sectionInfo object is anonymous. In making the API like this, the object is making it clear that it is an object providing access to the section information. Behind the scenes, the object is likely to be an internal state object created by the results controller. Exposing a public class that represents this data is not necessary, since the consumer of the results controller never needs to be concerned with how the section data is stored. All it needs to be able to do is query the data. Using a protocol to form an anonymous object like this means that the internal state object can be returned in the sections array. The consumer then knows only that it implements certain methods, and the rest of the implementation of the object is hidden.
Things to Remember
Protocols can be used to provide some level of anonymity to types. The type can be reduced to an id type that implements a protocol’s methods.
Use anonymous objects when the type (class name) should be hidden.
Use anonymous objects when the type is irrelevant, and the fact that the object responds to certain methods (the ones defined in the protocol) is more important.