A root, or base, class defines an interface and behaviors common to all classes below it in a class hierarchy. The Foundation Framework provides two root classes: NSObject and NSProxy. NSObject is the root class of most Objective-C class hierarchies, while NSProxy is a specialized class used to implement proxy objects. Both root classes adopt the NSObject protocol, which declares methods common to all Objective-C objects.
NSObject Protocol
The NSObject protocol specifies the basic API required for any Objective-C root class. As a protocol declares methods and properties that are not specific to any particular class, this design facilitates the definition of multiple root classes (even user-defined root classes). The methods declared by theNSObject protocol can be grouped into the following categories:
- Identifying classes and proxies
- Identifying and comparing objects
- Describing objects
- Object introspection
- Sending messages
The protocol also declares a set of obsolete methods used for manual memory management. Because ARC is the preferred memory management mechanism, these methods should no longer be used.
NSObject
The NSObject class is the root class of most Objective-C hierarchies; in fact, almost all of the Foundation Framework classes are subclasses of NSObject. It defines a set of class and instance methods that provide the following functionality:
- Basic object behaviors
- (Object, class) creation and initialization
- Dynamic (runtime system) features
You have used many of the NSObject methods in the preceding chapters, so by now you’re probably pretty familiar with some of the functionality this class provides. What you’ll do in the following paragraphs is examine some of the NSObject methods that I have yet to discuss in this book.
The NSObject description class method returns a string that represents the contents of the class. The default implementation simply prints the name of the class. As an example, the following statement will display the string NSArray in the Xcode output pane.
NSLog(@"%@", [NSArray description]);
The NSObject protocol declares a description instance method; the default implementation (provided by the NSObject class) prints the object name and its address in memory. ThedebugDescription instance method returns a string that represents the contents of the object for presentation to the debugger; its default implementation is the same as that for the descriptioninstance method.
The isEqual: method returns a Boolean YES if the receiver and the input parameter (object) are equal. Two objects that are equal must have the same hash value. The default (NSObject) implementation returns YES if the receiver and the input parameter have the same memory address.
The hash method returns a value of type NSUInteger that can be used in a hash table structure. As noted earlier, two objects that are equal must have the same hash value. If hash values are used to determine the positions of objects in a collection, the value returned by the hash method of an object in a collection must not change while it is in the collection.
NSObject defines a family of performSelector: instance methods used to send a message to an object. Each specifies a selector parameter identifying the message to send. The performSelector:method is equivalent to a standard Objective-C object message; for example, the following statements are equivalent:
[atom logInfo]; [atom performSelector:@selector(logInfo)];
The performSelector: method differs from a standard object message in that it enables you to send messages that are determined at runtime, as a variable selector value can be passed as the argument.
Several variations of the performSelector: method are declared in the NSObject protocol. These differ in the number of arguments (0–2) specified for the message (performSelector:,performSelector:withObject:, performSelector:withObject:withObject:). The NSObject classalso defines multiple performSelector: methods. These provide additional functionality when sending a message to an object, specifically:
- Thread selection (current, background, user-specified)
- Method invocation semantics (synchronous, blocking)
- Event processing mode
- Method invocation delay
For example, the performSelector:withObject:afterDelay: method sends a message to an object after a specified delay. The following statement demonstrates use of this method to send a message to an object after a delay of 5 seconds.
[atom performSelector:@selector(logInfo) withObject:nil afterDelay:5.0];
Creation and Initialization
Now you’re going to examine a few NSObject methods used for class loading and initialization. Theinitialize class method is used to initialize a class after it is loaded but before it is first used—that is, before it or any class that inherits from it is sent its first message. This method is called on a class at most once during program execution; in fact, if the class is not used, the method is not invoked.The initialize method is thread-safe and always sent to all of a class’s superclasses before it is sent to the class itself. To prevent the possibility of the method being called twice (by a superclass in addition to the target class) the initialize method should be implemented using logic that verifies the caller is the target class, and not a superclass (as shown in Listing 10-1).
Listing 10-1. NSObject initialize Method Design
+ (void)initialize
{
if (self == [MyClass class])
{
// Initilization logic
}
}
Listing 10-1 shows that the conditional test verifies that the initialization logic is performed only on a receiver whose class is that for which the initialize method is implemented.
The NSObject load class method, if implemented, is also invoked one time, after a class is loaded. It differs from the initialize method in several ways:
- The load method is invoked very shortly after a class is loaded, prior to theinitialize method. In fact, for classes that are statically linked (i.e., part of the program executable) the load method is called prior to the main() function. If theload method is implemented in a class packaged in a loadable bundle, it will be run when the bundle is dynamically loaded. Using the load method requires great care because it is called so early during application startup. Specifically, when this method is called, the program’s autorelease pool is (usually) not present, other classes may not have been loaded, and so forth.
- The load method can be implemented for both classes and categories; in fact, every category of a class can implement its own load method. The initialize method should never be overriden in a category.
- The load method is invoked one time, if implemented, after a class is loaded. Theinitialize method is invoked one time, if implemented, when a class receives its first message; if the class is not used, the method is not invoked.
The NSObject new class method allocates a new instance of an object and then initializes it. It combines the alloc and init methods in a single method. The following two object creation and initialization statements are equivalent.
Atom *atom = [[Atom alloc] init];
Atom *atom = [Atom new];
The new method invokes a class’s default init method; it does not invoke a custom init method.
NSProxy
The NSProxy class is an abstract root class used to implement the proxy pattern. It implements a minimal set of methods, thereby enabling almost all messages sent to it being captured and proxied to the target subject. NSProxy implements the methods declared by the NSObject protocol and declares two methods that must be implemented by subclasses:
- (void)forwardInvocation:(NSInvocation *)anInvocation;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
Note An abstract class is defined as a class that cannot be directly instantiated; it may have either no implementation or an incomplete implementation. It may also have methods that are required to be implemented. Objective-C does not have language-level support for abstract classes; by convention, you specify an abstract class in Objective-C as one with no initializer and one or more methods that require implementation. Hence, a concrete subclass for an abstract class (e.g., NSProxy) must implement the unimplemented methods and at least one initializer (i.e., one initmethod).
Additionally, an NSProxy subclass (i.e., a concrete subclass) should declare and implement at least one init method to conform to Objective-C conventions for object creation and initialization. The Foundation Framework includes several concrete subclasses of NSProxy:
- NSDistantObject: Defines proxies for objects in other applications or threads.
- NSProtocolChecker: Defines an object that restricts the messages that can be sent to another object.
In Chapter 9, you implemented an NSProxy concrete subclass, AspectProxy, to provide cross-cutting (AOP) functionality around a proxied object. These examples illustrate just a few of the many uses for the NSProxy class.