In Objective C, id is defined as (NSObject *)-- literally, a pointer to an object.

Objective C is nifty in that it has a language-sponsored institutional polymorphic base class; all classes inherit from NSObject, which provides a few extremely basic functions like memory allocation and prototypes a number of methods which nearly all objects can be expected to follow, for example init. Between this and the dynamic nature of the language and runtime, Polymorphism becomes extremely flexible and easy to do.

Because all objects of any type are descendents of type NSObject, ANY object in objective c can be treated as having type id. The compiler assumes that any methods or method arguments without explicitly stated return types are of type id (C, by contrast, assumes int), and so you tend to find yourself passing objects around as id whereever at all possible. Of course, if you are going to be using class-specific methods in the passed object then you will pass a more specific class, but in general objective c programmers tend to choose to store objects as the most abstract class possible, resulting in wonderfully flexible code.

This doesn't sound like much on paper, but in practice it is a wonderful, wonderful thing, simply and elegantly serving the purpose of C++ templates without ANY of the hassle. If you are programming in Objective C, the exact type of an object you are manipulating doesn't have to matter; all that matters is how you interact with it. (And if it DOES matter, you can actually check types at runtime with an ordinary if() statement.)

(All of this, keep in mind, excludes base C types-- but that doesn't become a problem, because in Objective C you just don't *use* base C types. Using non-object types while performing object-oriented tasks is simply bad software design, and the Objective C language recognizes this; it encourages you to use objects for object things and non-objects for non-object things, and because the language supports the idea elegantly (Unlike in C++) you never really get the urge to do otherwise (Unlike in C++).)
Two further interesting byproducts of all this:
  • You can add a method to all classes simultaneously, by defining a category to NSObject. This is usually a fairly bad idea unless you REALLY know what you're doing; however, it has its uses. Most notably, this technique is used to create what is called an informal protocol.
  • Because NSObject is defined wholly within the language itself instead of as a "magical" construct, you can write your own polymorphic base class which acts in its own wholly different way, have it conform to the NSObject protocol, and then pass its descendents around masquerading as a real NSObject. This is a REALLY bad idea if you don't know what you're doing, but offers some interesting possibilities.