- Classes Aren't Special
- Types and Pointers
- Defining Classes
- Memory Management
- Summary
Defining Classes
Objective-C class definitions have an interface and an implementation section. C++ has something slightly similar, but mixes the two somewhat. The interface in Objective-C only defines the bits that definitely need to be public. For implementation reasons, this includes private instance variables in most implementations, since you can't subclass a class unless you know how big it is. More recent implementations, such as Apple's 64-bit runtime, don't have this limitation.
The interface of an Objective-C object looks something like this:
@interface AnObject : NSObject <AProtocol, AnotherProtocol> { @private int integerivar @public id anotherObject; } + (id) aClassMethod; - (id) anInstanceMethod:(NSString*)aString with:(id)anObject @end
The first line of this contains three parts: The identifier AnObject is the name of the new class. The name after the colon is NSObject. (This is optional, but pretty much every Objective-C object should extend NSObject.) The names in angle brackets are protocols—similar to interfaces in Java—that are implemented by this class.
As with C++, instance variables (fields, in C++) can have access qualifiers. Unlike in C++, these qualifiers are prefixed with @ to avoid collisions with valid C identifiers.
Objective-C doesn't support multiple inheritance, so there's only one superclass. Therefore, the layout for the first part of an object is always identical to the layout of instances of the superclass. This used to be defined statically, meaning that changing the instance variables in one class required all subclasses to be recompiled. In newer runtimes this definition isn't required, at the cost of slightly more expensive access to instance variables. The other side-effect of this decision is that it breaks one of the other features of Objective-C:
struct _AnObject { @defs(AnObject); };
The @defs directive means "Insert all the fields of the specified object into this structure," so struct _AnObject has exactly the same in-memory layout as an instance of the AnObject class. You can use this rule, for example, to access instance variables directly. A common use is to allow a C function to manipulate an Objective-C object directly, for performance reasons.
Another thing you can do with this feature, as I hinted earlier, is create objects on the stack. Since the structure and the object have the same in-memory layout, you simply create a structure, set its isa pointer to the correct class, and then cast a pointer to it to an object pointer. Then you can use it as an object, although you have to be very careful that nothing retains pointers to it beyond the end of the structure's scope. (I've never found a real-world use for this trick; it's purely as an academic exercise.)
Unlike C++, Objective-C has no private or protected methods. Any method on an Objective-C object can be called by any other object. If you don't declare method in the interface, it's informally private. You'll get a compile-time warning that the object may not respond to this message, but you can still call it.
The interface is similar to the forward declaration in C. It still needs an implementation, which, unsurprisingly, uses @implementation to define:
@implementation AnObject + (id) aClassMethod { ... } - (id) anInstanceMethod:(NSString*)aString with:(id)anObject { ... } @end
Notice how parameters types are specified, in brackets. This is reusing the casting syntax from C to show that the values are cast to that type; they might not actually be that type. Exactly the same rules apply here as when casting. This means that casting between incompatible object pointer types will cause a warning (not an error).