(Note: This article originally appeared in cscene, and Adam Fedor has permitted us to publish here under GNU FDL.)
Like C, this flexibility allows you to perform great tricks, while forcing the programmer to introduce his own conventions to prevent errors. But, ahh, there is fun in danger, isn't there?
Note: In the following article, I describe a particular variant of Objective-C known as OpenStep (or GNUstep, the free implementation of OpenStep). This is by far the most popular implementation of Objective-C (there is no standard Objective-C language yet...).
[anObject doSomethingWith: anotherObject];This statement simply sends the message doSomethingWith: to anObject, with an argument of anotherObject. An Objective-C message call can be used anywhere a C statement can be used, such as in a conditional statement or within another Objective-C message. On compilers that support it, Objective-C messages can be mixed in C++ code, allowing the programmer to pick and choose the best aspects of either language.
In addition, there are some keywords that have been added to allow for the definition and implementation of classes, such as the definition for the class of which anObject is a member:
@interface MyClass : NSObject { int aVariable; id subObject; } + alloc; + defaultObject; - init; - (int) doSomethingWith: (id)anotherObject; @endHere, @interface tells the compiler we are defining an interface for the class named MyClass, which is a subclass of the class NSObject. NSObject is the root class used by GNUstep (The traditional Objective-C root class Object, is not used at all in GNUstep, although it is available). The root class is not a subclass of any other class. Although it is not required that a class descend from a root class like NSObject, this hierarchy is so firmly ingrained in the language, that it is rare to see classes that are a root class by themselves. The root class contains some of the most basic and often used functionality, which gives you the advantage of knowing that all your classes will responds to these basic messages. Out of the 300 or so classes in the GNUstep core library, only two are root classes.
The curly brackets after the class definition enclose the data that is encapsulated in the class. These variables are called instance variables, because they are variables that belong to an instance of a class.
The rest of the interface defines the methods implemented by the class. A method is a description of a message a class responds to. A '+' indicates a class method. Class methods are often called factory methods, because typically a factory method is used to create or manufacture instances of the class. The most popular class method is alloc, which allocates space for an instance of the class (but does not initialize the class -- that is usually reserved for the instance method init). A '-' indicates an instance method. For example, the doSomethingWith: method defined above takes one argument, the object anotherObject, and returns an integer. Class messages can only be sent to a class. Instance messages can only be sent to an instance of a class.
Dynamic binding comes about because in every Objective-C program, there is a runtime that works behind the scenes to connect a specific message with a specific object. Every time a message like doSomethingWith: is sent to an object, the Objective-C runtime looks up the definition of the class of the object, finds the function (method) that corresponds to the message doSomethingWith:, and then calls that function.
It may seem like this type of lookup could really slow a program down. In practice, however, most runtimes are optimized for this sort of lookup, and in general, it can be shown that message lookup is often not the dominant time factor of a program (particularly in GUI programs). Anywhere that time is critical, it's possible in Objective-C to pre-bind a message to its implementation, thus avoiding the expensive message lookup.
An interesting aspect of a method definition, is that once a method is defined, that method name and definition are, in a sense global, and can be used anywhere. For instance, say I had an object newObject that did not recognize the doSomethingWith: message. Well I could send the message to the object anyway, and the compiler would not complain about it:
id newObject; // could be any object; newObject = [[NewObject alloc] init]; // create and initialize the object [newObject doSomethingWith: anotherObject]; // send it a message.Only when the program was run would the Objective-C runtime discover that newObject did not recognize the doSomethingWith: message. However, instead of immediately issuing an error, the runtime instead sends another message to newObject, called the forwardInvocation: message. This gives newObject a chance to handle messages sent to it that it doesn't understand. In this case, if newObject knew about anObject, it could forward the message on to anObject and let it handle the message:
- (void) forwardInvocation: (NSInvocation*)anInvocation { if ([anObject respondsToSelector: [anInvocation selector]]) return [anInvocation invokeWithTarget: anObject]; else return [self doesNotRecognizeSelector: [anInvocation selector]]; }Here, the variable self refers to the object which received the message (similar to the this variable in C++). anInvocation is another object which contains all the information about the message that was sent, including the name of the message (the selector) and any arguments.
This type of usage is called delegation, and it is a powerful method of implementing various different constructs such as multiple inheritance, journaling, and dispatching messages to dynamically loaded code.
Incidentally, there are proper ways to handle unrecognized message, using Protocols, that allow for compile time checking and more robust code. Most programmers avoid defining un-typed objects unless it's really useful. A better way to write the first code segment would be:
NewObject *newObject; // newObject will be an instance of the NewObject class newObject = [[NewObject alloc] init]; // create and initialize the object [newObject doSomethingWith: anotherObject];In this case, the compiler would issue a warning stating that newObject does not respond to the message doSomethingWith:.
@implementation MyClass - (int) doSomethingWith: anotherObject { return [anotherObject multiply: 3 by: 4]; } @endThe @implementation keyword, is a counterpart to the @interface keyword (Note that it is not necessary to indicate that MyClass is a subclass of NSObject. The compiler already knows this from the interface definition). You can implement as many methods as you like within an @implementation block (even methods that were not described in the class interface). Methods that are implemented in the implementation section but are not described in the interface become, in essence, private methods that can only be used by that class and not by another class.
Objective-C provides a very simple and elegant way to add functionality to an existing class using Categories. A category is defined simply with an @interface line and the name of the category (e.g. MyAdditionalMethods):
@interface MyClass (MyAdditionalMethods) - (int) doSomethingWith: thisObject andThenWith: thatObject; @endThe additional methods are then implemented in a complementary @implementation section. There is one restriction, however. You can't define additional instance variables, which would change the amount of data held by the class. Other than this restriction, the things you can do with categories is incredible (and bizarre). For instance, if a method is defined both in the main class interface and a category, the category implementation wins. A message sent to the class will use the category implementation and ignore the original class implementation. If a method is defined in two separate categories, the implementation that is used is indeterminate, since you never know which category will be loaded first. In a situation where dynamic loading is used, this sort of behavior can create all sorts of headaches for a security conscious programmer...
Most often, however, categories are used to implement additional functionality that may be useful to only one part of the program. For instance, some additional methods may be defined to allow a class to better interact with the GUI section of the program, or the distributed objects section of the program.
To learn more, I'd encourage you to visit the GNUstep home page (www.gnustep.org) or the Apple Developers page ( http://developer.apple.com/techpubs/macosxserver/macosxserver.html) to look for more documentation on the Objective-C language and the OpenStep libraries.
More Objective-C basics: Garbage collection: reference counting, etc. Protocols: Publishing an interface not tied to a specific class GNUstep Foundation basics: Distributed Objects Bundles and dynamic loading Multithreaded applications. Posing: How GNUstep defines an abstract GUI interface allowing plug-in concrete back ends for X11, Display PostScript, and Windows. GUI programming in Objective-C Windows, Views, and Buttons.