Why do I care so much about NeXT computers? Because we at id Software developed the groundbreaking titles DOOM and Quake on the NeXTSTEP 3.3 OS running on a variety of hardware for about 4 years. I still remember the wonderful time I had coding DoomEd and QuakeEd in Objective-C; there was nothing like it before and there still is no environment quite like it even today.To get an impression of how far ahead of its time Objective-C and what is now known as Cocoa was ahead of its time, consider this:
In fact, with the superpower of NeXTSTEP, one of the earliest incarnations of DoomEd had Carmack in his office, me in my office, DoomEd running on both our computers and both of us editing one map together at the same time. I could see John moving entities around on my screen as I drew new walls. Shared memory spaces and distributed objects. Pure magic.Consider how long time ago that was, CORBA didn't support C++ for remote method invokation until 1996. At the time id did this it was just in its infant stage. Not to mention this was long long before Java RMI made remote objects populare and mainstream. It isn't even common to do this kind of thing today, and yet this was something that was quite trivial to do in Objective-C and Cocoa back in 1992.
Introducing Objective-CEvery time someone introduce you to Objective-C they will tell you about what a small and simple language it is. How easy it is to learn etc. But that was not my impression when I first tried to learn it. At the time I knew C++. When I saw an example of how to declare a class: I didn't think it looked easy or simple at all. Especially when I saw some code examples: The code below is just to give an idea of what Objective-C syntax is like. What the code does is not important. It has been simplified a bit to understand better. It basically draws several rows with black lines on a white background. When I first saw this kind of code I didn't think it looked easy at all. And I thought whoever said that Objective-C was a much simpler language than C++ must have been smoking something that wasn't good for them. The problem was that I was too caught up in the syntax. The syntax is indeed very unusual, but if one looks beyond that, the structure of the syntax is actually quite simple. Perhaps the best way to understand the syntax is to understand, why it looks so strange.
BackgroundUnlike C++, Objective-C wasn't really a new language at all originally. There was no Objective-C compiler. Instead Objective-C was just plain old C, with an added preprocessor. As any C/C++ developer worth his/her salt should know all statements starting with # specifies a preprocessor directive in C/C++. E.g.
#include. The preprocessor runs before the compiler and replaces the directives with actual C/C++ code. What the makers of Objective-C did was to add another preprocessor, but instead of marking the directives with #, they marked them with @. That way their special preprocessor could find all the new directives easily. While this does make Objective-C look like some alien entity inside C, it does make it trivial to distinguish between pure C code and the add-ons from Objective-C. Unlike C++, where what is C and what is C++ is blurred. Actually C++ isn't a strict superset of C like Objective-C although it usually compiles C code. The reason being among other things that C++ reinterprets a lot of regular C code into new C++ concepts. E.g. a struct isn't just a struct anymore but actually a class in C++.
Syntax inspiration from smalltalkThe second stumble block in order to understand Objective-C syntax is to realize it is derived from Smalltalk, while C/C++ syntax is derived from Algol. Algol based languages separate arguments with comma, while smalltalk separates with name of argument and colon. Below is a code snippet that demonstrates setting the position and dimension of a rectangle object. The Smalltalk/Objective-C syntax improves readability of code. When specifying a rectangle some libraries use the start and end coordinates while others use start coordinates and size. With Smalltalk/Objective-C it is made quite clear what is done. While with C++ it is not clear whether the first or the second line of Objective-C code is used. Unlike Smalltalk Objective-C methods are called by enclosing call with . This was originally to aid the preprocessor in extracting what was regular C code and what was Objective-C specific code.
A small and simple languageWhat do we exactly mean by saying that Objective-C is a small an simple language. C++ looks simpler syntax wise for the novice. However as said before syntax in deceiving. C++ add a host of new features to the C language: classes, virtual methods, non-virtual methods, constness, templates, friend classes, multiple inheritance, pure virtual functions, operator overloading, function overloading, private, public and protected members, constructors etc. Objective-C on the other hand add very little, it is just classes, methods, categories and protocols. There is only one way to create classes and to inherit form them. You can specify whether inheritance is public or private e.g. There is only one kind of methods. There is no distinction between virtual and non-virtual. They can't be const or not const. The concept of constructors and destructors don't exist. Instead these are just normal methods. As mentioned Objective-C is so simple they didn't even need to create a new compiler. A preprocessor was enough. This can make Objective-C seem overly simple. How can you do much with a language like that? The answer is the Objective-C runtime. The runtime is in fact what makes most of the Objective-C magic happen. Most of Objective-Cs features is provided at runtime and not done at compile time.
Methods and SelectorsObjective-C differs from C++ in that one distinguish between methods and selectors. To call a method on an Objective-C method you send a message. The method is not called directly. Instead Objective-C determines based on the selector which method to call. Conceptually one can think of a message (selector) as simply a string with a list of arguments. The code below e.g. send the message
rectangle. This will invoke a method on rectangle if it understands the message. If we think of Objective-C as just a preprocessor as it originally was, all message sending is replaced with a call to a C function: So when the preprocessor encounters our rectangle message it is translated into something like this:
objc_msgSendqueries rectangle for its class and then queries the class for methods it contains. Then it finds out which method corresponds to the given selector. The method is nothing but a function pointer of the form: Of course this is a simplified explanation. In reality every selector is registered. Meaning we don't pass newly created strings to
objc_msgSendeach time we invoke a method but a pointer to a string. This pointer has to be unique. So we can't pass any string pointer. So if we got å string we can find the unique pointer to this string by using the function: Which returns the pointer if it exist or registers the selector as a unique string pointer if it doesn't exist. The benefit of this is that selectors can be looked up quickly and compared quickly by just comparing their pointer addresses rather than comparing each character of the string.
Unique dynamic features of Objective-CI don't intend to go into every minor of the Objective-C runtime library but the example above should give an idea of how the dynamic features of Objective-C works.
ClassesIn C++ classes don't exist passed compile time. Or at least in modern C++ compilers which support runtime type identification classes exist in a watered down sense in that one can query if two classes have a relationship. In Objective-C it is almost opposite. Classes don't exist at compile time but rather are runtime entities. Classes are registered at runtime with function: Likewise methods and selectors are registered at runtime. So classes can in fact be modified at runtime. Of course users don't call
objc_addClass. These methods along with the ones that registers methods are generated by the preprocessor from the class definition provided by the programmer. But it is this fact that classes and methods exist as structures in memory at runtime that allows the programmer at runtime to query classes about their member variables and functions and whether they respond to a selector or not. In fact an Objective-C developer could create an application which could let user specify classes to call and functions to call. User could just type in the name of a class. Then developer could use C function NSClassFromString() to get corresponding class. NSSelectorFromString() could be used to retrieve the selector. With this one could query class further about arguments existing for selector etc. To learn more about Objective-C look at wikipedia