Types and Pointers
Objective-C doesn't officially allow defining objects on the stack. Well, this isn't quite true—it's possible to define objects on the stack, but it's very difficult to do correctly because it breaks assumptions about how memory management works. As a result, every Objective-C object is a pointer. A few types are defined by Objective-C; these are defined in a header as C types (remember the "no magic" thing).
The three most commonly used new types in Objective-C are id, Class, and SEL. An id is a pointer to an Objective-C object. It's the equivalent of a void* in C, in that you can cast any object pointer type to it and cast it to any other object pointer type. You can try sending any message to an id, but you'll get a runtime exception if it isn't supported.
A Class is a pointer to an Objective-C class. Classes are objects, so they also can receive messages. The name of a class is a type, not a variable. The identifier NSObject is the type of an NSObject instance, but it also can be used as a message receiver. You can get a class like this:
This sends a +class message to the NSObject class, which then returns a pointer to the Class structure representing class. This is useful for introspection, as we'll see in part 2 of this series.
The third type, SEL, represents a selector—an abstract representation of a method name. You can construct one at compile time with the @selector() directive, or at runtime by calling a runtime library function with a C string or by using the OpenStep NSSelectorFromString() function, which gives a selector for an Objective-C string. This technique allows you to call methods by name. You can do this in C by using something like dlsym(), but it's much more difficult in C++. In Objective-C, you can do something like this:
This is equivalent to the following:
Obviously, the second form will be slightly faster, since the first does two message sends. Later on, we'll look in a bit more detail at some of the things you can do with selectors.
C++ has no equivalent of the id type, because objects must always be typed. In Objective-C, you have an optional type system. Both of the following are valid:
id object = @"a string"; NSString *string = @"a string";
The constant string is really an instance of the NSConstantString class, which is a subclass of NSString. Assigning it to an NSString* enables compile-time type checking for messages and access to public instance variables (which are almost never used in Objective-C). Note that you can subvert this setup by doing something like the following:
NSArray *array = (NSArray*)string;
If you send messages to array, the compiler will check that they're messages that NSArray understands. This isn't very useful, because the object is a string. If you send it messages that both NSArray and NSString implement, however, it will work. If you send it messages that NSString doesn't implement, an exception will be thrown.
While this may seem like a strange thing to do (and it is—so don't do it), it highlights a very important difference between Objective-C and C++. Objective-C has typed-value semantics, while C++ has typed-variable semantics. In Objective-C, the type of an object is exclusively a property of the object. In C++, the type depends on the type of the variable. When you assign a pointer to an object in C++ to a variable defined as being a pointer to a superclass, the two pointers may not have the same numerical value. (This is done to allow multiple inheritance, which Objective-C doesn't support.)