Next article: VoodooPad Acquisition
Previous article: Friday Q&A 2013-10-11: Why Registers Are Fast and RAM Is Slow
Tags: fridayqna objectivec
Reader Tomas Bouda asks: what's the deal with the
NSObject protocol? There are two
NSObjects in Cocoa, a class and a protocol. Why both? What purpose do they serve? In today's article, I'll explore the answer to this question.
First, let's look at how these two entities with the same name can coexist. Classes and protocols in Objective-C inhabit entirely separate namespaces. You can have a class and a protocol, which are unrelated at the language level, with the same name. That's the case with
If you look at the language, there are no places where you can use either a class name or a protocol name. Class names can be used as the target of message sends, in
@interface declarations, and as type names. Protocols can be used in some of the same places, but always in a different way. There's no ambiguity in having one of each with the same name.
NSObject the class is a root class. A root class is a class at the very top of the hierarchy, meaning that it has no superclass. In Objective-C, unlike some languages like Java, there can be more than one root class.
Java has a single root class,
java.lang.Object, which every other class directly or indirectly inherits from. Because of this, Java code can count on any object it encounters implementing the basic methods in
Cocoa has multiple root classes. In addition to
NSObject there's also
NSProxy and some other assorted root classes. This is part of the reason for the
NSObject protocol. The
NSObject protocol defines a set of basic methods that all root classes are expected to implement. This way, code can count on those methods being there.
NSObject class conforms to the
NSObject protocol, which means that the
NSObject class implements these basic methods:
@interface NSObject <NSObject>
NSProxy also conforms to the
@interface NSProxy <NSObject>
NSObject protocol contains methods like
description, etc. The fact that
NSProxy conforms to
NSObject means that you can still count on instances of
NSProxy implementing these basic
An Aside About Proxies
While we're at it, just why is there an
NSProxy root class?
There are some cases where it's useful to have a class that doesn't implement very many methods. As the name suggests, proxy objects are a common case where this is useful. The
NSObject class implements a lot of stuff beyond the
NSObject protocol, such as key-value coding, that you don't necessarily want.
When building a proxy object, the goal is generally to leave most methods unimplemented so that they can be forwarded in bulk using a method like
NSObject would pull in a lot of baggage that would interfere.
NSProxy helps to avoid this by giving you a simpler superclass that doesn't have so much extra stuff in it.
The fact that the
NSObject protocol is useful for root classes isn't all that interesting for most Objective-C programming, since we don't use other root classes very often. However, it becomes really handy when making your own protocols. Say you have a protocol like this:
@protocol MyProtocol - (void)foo; @end
And now you have a pointer to an object that conforms to it:
You can tell this object to foo:
However, you cannot ask the object for its description:
[obj description]; // no such method in the protocol
And you can't check it for equality:
[obj isEqual: obj2]; // no such method in the protocol
In general you can't ask it to do any of the stuff that a normal object can do. Sometimes this doesn't matter, but sometimes you actually want to be able to do this stuff.
This is where the
NSObject protocol Comes in. Protocols can inherit from other protocols. You can make
MyProtocol inherit from the
@protocol MyProtocol <NSObject> - (void)foo; @end
This says that not only do objects that conform to
MyProtocol respond to
-foo, but they also respond to all those common messages in the
NSObject protocol. Since every object in your app typically inherits from the
NSObject class and it conforms to the
NSObject protocol, this doesn't impose any additional requirements on people implementing
MyProtocol, while allowing you use these common methods on instances.
The fact that there are two different
NSObjects is an odd corner of the frameworks, but it makes sense when you get down to it. An
NSObject protocol allows multiple root classes to all have the same basic methods, and also makes it easy to declare a protocol which also includes basic functionality expected of any object. The
NSObject class conforms to the
NSObject protocol, bringing everything together.
That's it for today. Friday Q&A is driven by reader suggestions as always, so if you have a topic you'd like to see covered in a future edition, please send it in!
Is the choice between a new root class and a protocol just a historical detail?
Anyone have any examples where it might be a good idea to leave it off?
Also, you'd still want the NSObject protocol for the purpose of inheriting into other protocols, as Mike explained. So you wouldn't gain anything by having a One True Root Class.
Maybe there's more overhead in consolidating these shared methods than I am aware of, or perhaps the implementation details are so radically different for each of these methods that the code is entirely different. On the surface though, things like retain, release, autorelease, self, perform selector, etc would seem to have a near universal implementation between NSObject and NSProxy.
Granted, this is GNUStep and not Apple's code and it's not that much code. But assuming Apple shares this amount of duplication between the two, it seems odd to me not have a OneTrueRootClass.
The reason NSProxy doesn't subclass the NSObject class is because, as Mike points out, there's a lot of extra baggage that comes with the class itself. But I'm still failing to see any significant impact in moving just the NSObject protocol methods to a OneTrueRootClass, aside from historical considerations.
Wouldn't this have the same effect, assuming NSObject is the root class?
In fact, due to the protocol and class namespaces being different, you could even do:
typedef NSObject<MyProtocol> MyProtocol;
Obviously this is a bad idea for readability and probably other reasons, but it does work - you can even send messages the "MyProtocol" class, though obviously they will end up being sent to NSObject itself.
I've actually used this method when implementing a UIViewController class-cluster type thing, where I couldn't cleanly implement a common superclass because some of the classes had to be subclasses of other UIViewControllers. For example, one is a subclass of QLPreviewController and yet another is a subclass of PKAddPassesViewController.
So I implemented the class cluster as a category on UIViewController itself, created a protocol for the "subclasses" implement, and then typedef'd UIViewController<DCPushPreviewController> to DCPushPreviewController, and because UIViewController itself adopts DCPushPreviewController, everything works out perfectly, including sending messages to the "DCPushPreviewController" class.
I'd be thrilled to hear if there was a cleaner way to do this.
Two possibilities I've seen canvassed are (1) that Apple anticipates some protocols may be used with classes not themselves using NSObject as a root class, or (2) to differentiate (semantically) between protocols specifying whole application roles (eg. delegate protocols) and those specifying only individual behaviours (eg. being enumerable).
Comments RSS feed for this page
Add your thoughts, post a comment:
Spam and off-topic posts will be deleted without notice. Culprits may be publicly humiliated at my sole discretion.