Objective-C: the More Flexible C++

by Armin Roehrl

It is a surprising fact that anyone studying GNUstep or the Cocoa Framework will notice they are nearly identical to the NEXTSTEP APIs that were defined ten years ago. A decade is an eternity in the software industry. If the framework (and its programming language--Objective C) came through untouched these past ten years, there must be something special about it. And Objective-C has done more than survive; some famous games including Quake and NuclearStrike were developed using Objective-C.

Why Should I Learn Objective-C?

Objective-C gives you the full power of a true object-oriented language with exactly one syntax addition to C and, unlike C++, about a dozen additional keywords.

Since Apple purchase Next for $400 million and Mac OS X ships with Objective-C, recycling NEXTSTEP (later called OpenStep), as well as the fact that GNUstep is delivering the rock-solid window-manager Window Maker, Objective-C is (rightly) getting more attention because it is more flexible than C++ at the cost of being slower.

In reality, Objective-C is Object C and is as close to Smalltalk as a compiled language can be. This is no surprise as Brad J. Cox added object-oriented, Smalltalk-80-based extensions to the C language.

So objective-C is a hybrid between Smalltalk and C. A string can be represented as a `char *' or as an object, whereas in Smalltalk everything is an object. As with Java (int, double,.. are no objects) this leads to faster performance.

In contrast, C++ traditionally is associated with the Simula 67 school of object-oriented programming. In C++, the static type of an object fixes what messages it can receive. In Objective-C the dynamic type of an object determines what messages it can receive. The Simula 67 format allows problems to be detected at compile time. The Smalltalk approach delays typing until runtime and therefore is more flexible.

A GNU version was written by Dennis Gladding in 1992 and then Richard Stallman took over the development. The current GNU version is derived from the version written by Kresten Thorup when he was a still a university student in 1993. He ported that version to the NeXTcube and joined NeXT.

Apple chose Objective-C for Cocoa, as NEXTSTEP was based on Objective-C. But, even if they had written it from scratch, they might have decided to use Objective-C because it is object-oriented, which is undoubtedly a must for big software projects. It extends the standard ANSI C, so that existing C programs can be adapted to use the frameworks, and programmers can chose when to stick to procedural programming and when to go the object-oriented way. C was intended to be a good language for system programming. C is fine as it allows the programmer to do exactly what she wants, all the way down to the hardware. C also keeps the gold old pointers, which can be used for efficient code.

Objective-C is simple, unambiguous and easy to learn. But most of all, it is the most dynamic language of all object-oriented languages based on C. Its dynamic late binding offers flexibility and power. Messages are not constrained by either the class of the receiver or the method selector, allowing rapid change and offering access to information about running applications.

The following is a short introduction to OOP in Objective-C, starting with the basics. Procedural programs consist of data and operations on data. OOP works at a higher level by grouping data into units, which are called objects. Several objects combined and their interactions form a program.

Interface and Implementation

Abstraction is at the root of all understanding; it helps us capture the bigger image as the details are hidden. Object-Oriented Programming and the Objective-C Language (see Resources) shows the image of a clock cut half-open: what we normally see (the external view) is the interface, and the implementation (the internal workings) is hidden inside.

@interface declares a new class. It indicates the name of the class and its superclass, the protocols adhered to, the layout of the instance variables and declares the methods implemented by this class. Traditionally a class interface is stored in a file called <classname>.h.

Only externally visible methods are listed in the interface section. However, there are visibility keywords for instance variables:

  • @private: the instance variable is accessible only within the class that declares it.

  • @protected: the instance variable is accessible within the class that declares it and with the class that inherits it.

  • @public: the instance variable is accessible everywhere.

  • @implementation' defines a class. The implementation is a collection of method definitions stored in a file called <classname>.m.

Messages

The only way to tell an object to do something is to send it a message. The well known metaphor of objects as active actors can be used. An actor communicates through a message to another to request that she do something. The receiving object is called the "receiver" of that message. The receiver will determine the concrete behavior. It will make a big difference if you send "I love you" to Catherine instead of Katia. Different receivers cause different implementations.

[receiver message];
[receiver message: arg1];

Arguments are used after colon-terminated message selectors.

If a message is declared as -message, it can be sent to objects. A leading plus sign (+) means that the message can be sent to class objects. The default return type for methods is "id". If a method has nothing useful to return, it returns "self", which is a pointer to the object to which the message was sent (similar to "this" in C++).

By implementing the method -doesNotUnderstand:

-doesNotUnderstand: msg { [msg sentTo:delegate]; }

an object can forward messages to another object ("delegate"), including messages that return C structs, doubles, chars and so on.

Classes and Inheritance

A class is used to produce similar objects, called instances of the class. Classes are used to encapsulate data and methods that belong together. Methods are the operations that Objective-C applies to data and are identified by their message selectors.

Objective-C supports polymorphism: several classes can have a method with the same name.

Inheritance is used for code reuse. Classes inherit variables and methods from a higher-level class called a super-class. A class that inherits some or all of the methods and variables is a sub-class. In Objective-C, all classes are a sub-class of a primal super-class called Object.

Dynamism

Compile-time and link-time constraints are limiting because they force issues to be decided from information found in the programmer's source code, rather than from information obtained by the running program. Such static languages usually refuse to introduce new modules or new types during runtime. Objective-C makes as many decisions as possible at runtime:

  • Dynamic typing: waiting until runtime to determine the class of an object.

  • Dynamic binding: determining at runtime what method to invoke--no need to bother about what is done when. This allows it to change the components of a program incrementally.

  • Dynamic loading: program segments are not loaded into memory until they are actually used, thereby minimizing the system resources required for an instance of a program.

If your program offers a framework for others to use at runtime, you can discover what they have implemented and load it at runtime as needed.

Objective-C programs are structured through inheritance (how objects relate by type) and the pattern of message passing (explains how program works).

Objects

As the name implies, object-oriented programs are built around objects. Objects are the root of the Objective-C family tree.

id is an object identifier that is a distinct data type. It is a pointer to an object (in reality a pointer to an object's data--its instance variables). The actual class of the Object does not matter because Objective-C does runtime binding.

nil is the null object. The id of nil is 0. Other basic types of Objective-C are defined in the header file, objc/Objc.h.

Every object also carries an is an instance variable that identifies the object's class (what kind of object is it?). This allows objects to be typed dynamically at runtime. isa also allows objects to introspect themselves so they can find out what methods they have.

Usually, you create objects with a call to alloc:

id MyRect;
myRect=[Rectangle alloc];

alloc allocates and clears memory for the instance variable. Initialization typically follows allocation immediately.

myRect=[[Rectangle alloc] init];

Here is an extended example of the lists.

---List.h----
#import <objc/Object.h> 
@interface List : Object 
{
  int list[100];          // These are instance variables.
  int size;
} 
/* Public methods */
- free;
- (int) addEntry: (int) num;
- print; 
@end
---------List.m---------------
#import "List.h" 
@implementation List 
+ new   
{
  self = [super new];
  [self resetSize];
  return self;
} 
- free
{
  return [super free];
} 
- (int) addEntry: (int) num
{
  if (size<100)
    list[size++] = num;
  return size;
} 
- print
{
  int i;   
  printf("         \n");
  for (i = 0; i < size; ++i)
    printf ("%i ", list[i]);
  return self;                // Always return self
                              // if nothing else makes sense.
} 
- resetSize
{
  size = 0;
  return self;
}
------------------------------

List2.h, below, inherits from the above Lists. -doesNotRecognize is the standard method that gets called if a class does not have the required method. magicMethod in main.m does not exist so doesNotRecognize gets called. This allows for an easy way to create Delegatorpatterns or to use Proxy-setups.

------------List2.h-----------
#import <objc/Object.h> 
#import "List.h"
@interface List2 : List  // List2 is a subclass of List
- free;
- (int) sum;
- doesNotRecognize: msg;
@end
-------------------------------
------------List2m-------------
#import "List2.h"
#import "List.h"
@implementation List2 : List
- (int) sum
{
  int i=0;
  int sum=0;
  for (i=0; i<size; i++) 
    sum+=list[i];
  return sum;
} 
-doesNotRecognize: msg
{
    printf("no clue!\n");
    return self;
}
----------------------------
-----------main.m-----------
import <objc/Object.h>
#import "List.h"         
#import "List2.h"
main(int argc, char** argv, char** env)
{
  int i;
  id list, list2;        // id is a general data type for objects.   
  list = [List new];     // create an instance of class List.
  [list addEntry: 5];    // send a message to the object list
  [list addEntry: 3];
  [list print];
  [list free];           // get rid of object
  list2 = [List2 new];
  [list2 addEntry: 6];
  [list2 addEntry: 4];
  printf("\nsum: %d\n",[list2 sum]);
  [list2 magicMethod];
}
-----------------------
Compiling the Samples

There are four major Objective-C compilers: Stepstone, GNU, Apple and Portable Object Compiler (POC). Stepstone and POC are preprocessors that emit vanilla C code. GNU and Apple are "native" compilers, creating intermediate code for the GNU code generator.

To compile the previous examples, enter:

gcc -Wno-import -c List.m
gcc -Wno-import -c List2.m
gcc -Wno-import -c main.m
gcc -o List  -Wno-import List2.o List.o main.o -lobjc -lpthread
Categories and Protocols

Categories compartmentalize a class definition or extend an existing one, possibly at runtime. They denote a set of method definitions that is segregated from the rest of the class definition.

Protocols allow you to organize related methods into groups. Protocol hierarchy is unrelated to class hierarchy. They are used to declare methods that others are expected to implement, declare the interface to an object while concealing its class and capture the similarities among classes that are not hierarchically related.

Protocols address the lack of multiple inheritance. They are equivalent to multiple inheritance for purely "abstract" classes. The closest you can get to multiple inheritance is to create a class with instance variables that are references to other objects. Instances can specifically redirect messages to any combination of the objects of which they are compounded. The Objective-C philosophy is that you do not need multiple inheritance because it creates more problems than benefits. Using protocols, one can have type-checking features without sacrificing dynamic binding. "Any object implementing messages in Protocol X is okay for this use" constrains the functionality and not the implementation or the inheritance.

@protocol Archiving
   -read: (Stream *) stream;
   -write: (Stream *) stream;
@end

and refer to it with:

/* MyClass inherits from Object and conforms to the
   Archiving protocol.  */
@interface MyClass: Object <Archiving>
@end

Example that spots incompatibility changes:

MyClass *obj1 = [MyClass new];
// OK: obj1 conforms to the Archiving protocol.
id <Archiving> obj2 = obj1;
// Error: obj1 does not conform to the TargetAction protocol.
id <TargetAction> obj3 = obj1;
Garbage Collection

Starting with version 0.9.0, GNUstep's libFoundation comes with two garbage-collecting mechanisms that provide you with the ability to solve memory management problems. libFoundation is a library that provides an almost complete implementation of the OpenStep specification, plus many other extensions that can be found in the newer versions of Rhapsody's (Mac OS X) Foundation.

From Unit-testing Frameworks a là extreme programming (XP) to XML parsers, one can take advantage of a large existing codebase. And if all fails, one can take advantage of the entire existing C code-base, as the GNUstep XML parser has demonstrated. It is an Objective-C wrapper around libxml.

We think that Objective-C is the better C++, which is really worth giving a try. GNUstep is extremely powerful.

An extended comparison of all the compilers discussed in this article can be found on our home page.

Resources

Simula

developer.apple.com/techpubs/macosx/Cocoa/ObjectiveC/ObjC.pdf

Foundation Library, Objective C API

libFoundation

Object-Oriented Programming and the Objective-C Language

FAQ

www.slip.net/~dekorte/Objective-C/Documentation/2_Comparisons/Comparisons.html

Articles on Objective-C by Brad Cox

Benchmarks for C++, Java and Objective-C by Sven Koehler

Ruby Bindings for GNUstep

Unit-testing framework

GNUstep XML Parser

Window Maker

Games Written in Objective-C

Objective-C Blocks

Object Oriented Programming in Objective-C

Objective C versus Smalltalk, C++ and Java

Compilers

GNU Compiler Collection

Portable Object Compiler

Books

Object Oriented Programming: An Evolutionary Approach. Brad J. Cox, Andrew J. Novobilski. Addison-Wesley, 1991.

Objective-C: Object Oriented Programming Techniques. Lewis J. Pinson, Richard S. Wiener. Addison-Wesley, 1991.

An Introduction to Object-Oriented Programming. 3rd Edition. Timothy A. Budd. Addison-Wesley, 2002.

Developing Business Applications With Openstep. N. Gervae. Springer Verlag Telos, 1996.

Openstep for Enterprises. Nancy Craighill. John Wiley & Sons, 1996.

Learning Cocoa. Apple Computer Technical Writers. O'Reilly, 2001.

Armin Roehrl (armin@approximity.com) and Stefan Schmiedl (stefan@approximity.com) lead the software company Approximity that optimises big distributed webventures.

Load Disqus comments

Firstwave Cloud