GNUstep Base Library Programming Manual
GNUstep Base Library Programming Manual
Table of Contents
1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1 What is Object-Oriented Programming? . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.1 Some Basic OO Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 What is Objective-C? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3 History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.4 What is GNUstep? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.4.1 GNUstep Base Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.4.2 GNUstep Make Utility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.4.3 A Word on the Graphical Environment . . . . . . . . . . . . . . . . . . . . 7
1.4.4 The GNUstep Directory Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.5 Building Your First Objective-C Program . . . . . . . . . . . . . . . . . . . . . . 8
5 Advanced Messaging . . . . . . . . . . . . . . . . . . . . . . . . . . 49
5.1 How Messaging Works . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
5.2 Selectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
5.2.1 The Target-Action Paradigm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
5.2.2 Obtaining Selectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
5.2.3 Avoiding Messaging Errors when an Implementation is Not
Found . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
5.3 Forwarding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
5.4 Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
iii
7 Distributed Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
7.1 Object Interaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
7.2 The GNUstep Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
7.2.1 Code at the Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
7.2.2 Code at the Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
7.2.3 Using a Protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
7.2.4 Complete Code for Telephone Directory Application . . . . . . 70
7.2.5 GNUstep Distributed Objects Name Server . . . . . . . . . . . . . . . 71
7.2.6 Look Ma, No Stubs! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
7.3 A More Involved Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
7.3.1 Protocol Adopted at Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
7.3.2 Protocol Adopted at Server. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
7.3.3 Code at the Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
7.3.4 Code at the Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
7.4 Language Support for Distributed Objects . . . . . . . . . . . . . . . . . . . . . 81
7.4.1 Protocol Type Qualifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
7.4.2 Message Forwarding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
7.5 Error Checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
7.5.1 Vending the Server Object. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
7.5.2 Catching Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
7.5.3 The Connection Fails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
iv Objective-C GNUstep Base Programming Manual
8 Base Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
8.1 Copying, Comparing, Hashing Objects. . . . . . . . . . . . . . . . . . . . . . . . . 85
8.2 Object Containers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
8.3 Data and Number Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
8.3.1 NSData . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
8.3.2 NSValue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
8.3.3 NSNumber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
8.3.4 NSRange, NSPoint, NSSize, NSRect . . . . . . . . . . . . . . . . . . . . . . 89
8.4 Date/Time Facilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
8.5 String Manipulation and Text Processing . . . . . . . . . . . . . . . . . . . . . . 89
8.5.1 NSScanner and Character Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
8.5.2 Attributed Strings. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
8.5.3 Formatters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
8.6 File Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
8.7 Persistence and Serialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
8.7.1 Property List Serialization. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
8.7.2 Archives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
8.8 Utility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
8.9 Notifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
8.10 Networking and RPC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
8.10.1 Basic Networking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
8.10.2 Remote Process Communications . . . . . . . . . . . . . . . . . . . . . . . . 96
8.11 Threads and Run Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
8.11.1 Run Loops and Timers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
8.11.2 Tasks and Pipes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
8.11.3 Threads and Locks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
8.11.4 Using NSConnection to Communicate Between Threads . . 99
8.12 GNUstep Additions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
1 Introduction
The aim of this manual is to introduce you to the Objective-C language and the GNUstep
development environment, in particular the Base library. The manual is organised to give
you a tutorial introduction to the language and APIs, by using examples whenever possible,
rather than providing a lengthy abstract description.
While Objective-C is not a difficult language to learn or use, some of the terms may be unfa-
miliar, especially to those that have not programmed using an object-oriented programming
language before. Whenever possible, concepts will be explained in simple terms rather than
in more advanced programming terms, and comparisons to other languages will be used to
aid in illustration.
Rather than ’calling’ one of its methods, an object is said to ’perform’ one of its methods
in response to a message. (A method is known as a ’member function’ in C++.)
Classes
All objects of the same type are said to be members of the same class. To continue with
the rectangle example, every rectangle could belong to a rectangle class, where the class
defines the instance variables and the methods of all rectangles.
A class definition by itself does not create an object but instead acts like a template for
each object in that class. When an object is created an ’instance’ of that class is said to
exist. An instance of a class (an object) has the same data structure (instance variables)
and methods as every other object in that class.
Inheritance
When you define a new class you can base it on an existing class. The new class would then
’inherit’ the data structure and methods of the class that you based it on. You are then
free to add instance variables and methods, or even modify inherited methods, to change
the behavior of the new class (how it reacts to messages).
The base class is known as the ’superclass’ and the new class as the ’subclass’ of this
superclass. As an example, there could be a superclass called ’shapes’ with a data structure
and methods to size, position and draw itself, on which you could base the rectangle class.
Polymorphism
Unlike functions in a procedural program such as C, where every function must have a
unique name, a method (or instance variable) in one class can have the same name as that
in another class.
This means that two objects could respond to the same message in completely different ways,
since identically named methods may do completely different things. A draw message sent
to a rectangle object would not produce the same shape as a draw message sent to a circle
object.
Encapsulation
An object hides its instance variables and method implementations from other parts of the
program. This encapsulation allows the programmer that uses an object to concentrate on
what the object does rather than how it is implemented.
Also, providing the interface to an object does not change (the methods of an object and how
they respond to received messages) then the implementation of an object can be improved
without affecting any programs that use it.
Dynamic Typing and Binding
Due to polymorhism, the method performed in response to a message depends on the class
(type) of the receiving object. In an OO program the type, or class, of an object can be
determined at run time (dynamic typing) rather than at compile time (static typing).
The method performed (what happens as a result of this message) can then be determined
during program execution and could, for example, be determined by user action or some
other external event. Binding a message to a particular method at run time is known as
dynamic binding.
Chapter 1: Introduction 5
return(0);
}
Objective-C source files are compiled using the standard GNU gcc compiler. The compiler
recognises Objective-C source files by the .m file extension, C files by the .c extension and
header files by the .h extension.
As an example, the command $gcc -o testfile testfile.m -lobjc would compile the Objective-
C source file testfile.m to an executable named testfile. The -lobjc compiler option
is required for linking an Objective-C program to the runtime library. (On GNU/Linux
systems you may also need the -lpthreads option.)
The GNUstep make utility provides an alternative (and simple) way to compile large
projects, and this useful utility is discussed in the next section.
Relative to other languages, Objective-C is more dynamic than C++ or Java in that it binds
all method calls at runtime. Java gets around some of the limitations of static binding with
explicit runtime “reflection” mechanisms. Objective-C has these too, but you do not need
them as often as in Java, even though Objective-C is compiled while Java is interpreted.
More information can be found in Appendix Appendix C [Objective-C Java and C++],
page 107.
1.3 History
Objective-C was specified and first implemented by Brad Cox and his company Stepstone
Corporation during the early 1980’s. They aimed to minimally incorporate the object-
oriented features of Smalltalk-80 into C. Steve Jobs’s NeXT licensed Objective-C from
StepStone in 1988 to serve as the foundation of the new NeXTstep development and oper-
ating environment. NeXT implemented its own compiler by building on the gcc compiler,
6 Objective-C GNUstep Base Programming Manual
modifications that were later contributed back to gcc in 1991. No less than three runtime
libraries were subsequently written to serve as the GNU runtime; the one currently in use
was developed by Danish university student Kresten Krab Thorup.
Smalltalk-80 also included a class library, and Stepstone’s Objective-C implementation con-
tained its own library based loosely on it. This in turn influenced the design of the NeXTstep
class libraries, which are what GNUstep itself is ultimately based on.
After NeXT exited the hardware business in the early 1990s, its Objective-C class library
and development environment, NeXTstep, was renamed OpenStep and ported to run on
several different platforms. Apple acquired NeXT in 1996, and after several years figuring
out how to smooth the transition from their current OS, they released a modified, enhanced
version of the NeXTstep operating system as Mac OS X, or “10” in 1999. The class libraries
in OS X contain additions related to new multimedia capabilities and integration with Java,
but their core is still essentially the OpenStep API.
This API consists of two parts: the Foundation, a collection of non-graphical classes for
data management, network and file interaction, date and time handling, and more, and
the AppKit, a collection of user interface widgets and windowing machinery for developing
full-fledged graphical applications. GNUstep provides implementations of both parts of
this API, together with a graphical engine for rendering AppKit components on various
platforms.
Classes in the base library are easily identified since they begin with the upper case charac-
ters ’NS’, as in NSString. Some examples in this manual use classes from the base library,
but for complete documentation on the base library see the API documentation.
2. Create the following Objective-C source code using your favourite text editor and save
it in the project directory with the filename source.m.
#include <stdio.h>
/*
* The next #include line is generally present in all Objective-C
* source files that use GNUstep. The Foundation.h header file
* includes all the other standard header files you need.
*/
#include <Foundation/Foundation.h>
/*
* Declare the Test class that implements the class method (classStringValue).
*/
@interface Test
+ (const char *) classStringValue;
@end
/*
* Define the Test class and the class method (classStringValue).
*/
@implementation Test
+ (const char *) classStringValue;
{
return "This is the string value of the Test class";
}
@end
/*
* The main() function: pass a message to the Test class
* and print the returned string.
*/
int main(void)
{
printf("%s\n", [Test classStringValue]);
Chapter 1: Introduction 9
return 0;
}
The text between comment markers (/* */) is ignored by the compiler but indicates
to someone reading the source file what each part of the program does. The program
is an example of a (class) method responding to a message. Can you see how it works?
A message is sent to the Test class as an argument to printf(), requesting the string
value of that class. The Test class performs its classStringValue method in response
to this message and returns a string that is finally printed. No object is created in this
program since a class method does not require an instance of a class in order to respond
to a message.
You will learn more about class methods in the next chapter.
3. Now create the makefile, again using your favourite text editor, and save it in the same
project directory with the filename GNUmakefile.
include $(GNUSTEP_MAKEFILES)/common.make
TOOL_NAME = LogTest
LogTest_OBJC_FILES = source.m
include $(GNUSTEP_MAKEFILES)/tool.make
If you look at the makefile above you will notice the two lines that tell the make utility
to build a tool with the filename LogTest from the Objective-C source file source.m.
You could copy and modify this makefile for later projects you may have: just change
the tool name and list the new source files.
The two ’include’ lines are just a way of keeping your makefile simple, by including two
’ready-made’ makefiles that someone else created.
4. Before you can execute this makefile you must first set your GNUstep environment
variables. Among other things this defines the GNUSTEP_MAKEFILES variable referenced
above. The simplest way to do this is to execute one of the following commands (you
must first locate your GNUstep installation manually):
C shell:
source <GNUstep root>/System/Library/Makefiles/GNUstep.csh
Bourne shell:
. <GNUstep root>/System/Library/Makefiles/GNUstep.sh
On most Unix systems, GNUstep is installed in /usr/lib/GNUstep. (Directory layout
documentation.)
10 Objective-C GNUstep Base Programming Manual
5. You can now compile the project using make. At the system command prompt, change
to the project directory and enter the make command.
6. Run the program (on Unix enter ./obj/LogTest at the command prompt). The message
"This is the string value of the Test class" will be displayed (assuming there were no
errors).
You have now compiled and run your first Objective-C program. Hungry for more? Then
read on.
Chapter 2: The Objective-C Language 11
2.2 Objects
Object-oriented (OO) programming is based on the notion that a software system can be
composed of objects that interact with each other in a manner that parallels the interaction
of objects in the physical world.
This model makes it easier for the programmer to understand how software works since it
makes programming more intuitive. The use of objects also makes it easier during program
design: take a big problem and consider it in small pieces, the individual objects, and how
they relate to each other.
Objects are like mini programs that can function on their own when requested by the
program or even another object. An object can receive messages and then act on these
messages to alter the state of itself (the size and position of a rectangle object in a drawing
program for example).
In software an object consists of instance variables (data) that represent the state of the
object, and methods (like C functions) that act on these variables in response to messages.
As a programmer creating an application or tool, all you need do is send messages to the
appropriate objects rather than call functions that manipulate data as you would with a
procedural program.
The syntax for sending a message to an object, as shown below, is one of the additions that
Objective-C adds to ANSI C.
[objectName message];
Note the use of the square [ ] brackets surrounding the name of the object and message.
Rather than ’calling’ one of its methods, an object is said to ’perform’ one of its methods
in response to a message. The format that a message can take is discussed later in this
section.
12 Objective-C GNUstep Base Programming Manual
2.2.2 Messages
A message in Objective-C is the mechanism by which you pass instructions to objects. You
may tell the object to do something for you, tell it to change its internal state, or ask it for
information.
A message usually invokes a method, causing the receiving object to respond in some way.
Objects and data are manipulated by sending messages to them. Like C-functions they
have return types, but function specific to the object.
Objects respond to messages that make specific requests. Message expressions are enclosed
in square brackets and include the receiver or object name and the message or method name
along with any arguments.
To send a message to an object, use the syntax:
[receiver messagename];
where receiver is the object.
The run-time system invokes object methods that are specified by messages. For example,
to invoke the display method of the mySquare object the following message is used:
[mySquare display];
Messages may include arguments that are prefixed by colons, in which case the colons are
part of the message name, so the following message is used to invoke the setFrameOrigin::
method:
Chapter 2: The Objective-C Language 13
A message to nil does NOT crash the application (while in Java messages to null raise
exceptions); the Objective-C application does nothing.
For example:
[nil display];
will do nothing.
If a message to nil is supposed to return an object, it will return nil. But if the method is
supposed to return a primitive type such as an int, then the return value of that method
when invoked on nil, is undefined. The programmer therefore needs to avoid using the
return value in this instance.
2.2.3 Polymorphism
Polymorphism refers to the fact that two different objects may respond differently to the
same message. For example when client objects receive an alike message from a server ob-
ject, they may respond differently. Using Dynamic Binding, the run-time system determines
which code to execute according to the object type.
2.3 Classes
A class in Objective-C is a type of object, much like a structure definition in C except that in
addition to variables, a class has code – method implementations – associated with it. When
you create an instance of a class, also known as an object, memory for each of its variables
is allocated, including a pointer to the class definition itself, which tells the Objective-C
runtime where to find the method code, among other things. Whenever an object is sent a
message, the runtime finds this code and executes it, using the variable values that are set
for this object.
2.3.1 Inheritance
Most of the programmer’s time is spent defining classes. Inheritance helps reduce coding
time by providing a convenient way of reusing code. For example, the NSButton class
defines data (or instance variables) and methods to create button objects of a certain type,
so a subclass of NSButton could be produced to create buttons of another type - which
may perhaps have a different border colour. Equally NSTextField can be used to define
a subclass that perhaps draws a different border, by reusing definitions and data in the
superclass.
Inheritance places all classes in a logical hierarchy or tree structure that may have the
NSObject class at its root. (The root object may be changed by the developer; in GNUstep
14 Objective-C GNUstep Base Programming Manual
it is NSObject, but in “plain” Objective-C it is a class called “Object” supplied with the
runtime.) All classes may have subclasses, and all except the root class do have superclasses.
When a class object creates a new instance, the new object holds the data for its class,
superclass, and superclasses extending to the root class (typically NSObject). Additional
data may be added to classes so as to provide specific functions and application logic.
When a new object is created, it is allocated memory space and its data in the form of its
instance variables are initialised. Every object has at least one instance variable (inherited
from NSObject) called isa, which is initialized to refer to the object’s class. Through this
reference, access is also afforded to classes in the object’s inheritance path.
In terms of source code, an Objective-C class definition has an:
• interface declaring instance variables, methods and the superclass name; and an
• implementation that defines the class in terms of operational code that implements the
methods.
Typically these entities are confined to separate files with .h and .m extensions for Interface
and Implementation files, respectively. However they may be merged into one file, and a
single file may implement multiple classes.
The example will produce an NSString called gprsChannel holding the string "The GPRS
channel is 5".
stringWithFormat: recognises the %@ conversion specification that is used to specify an
additional NSString:
NSString *one;
NSString *two;
one = @"Brainstorm";
two = [NSString stringWithFormat: @"Our trading name is %@", one];
The example assigns the variable two the string "Our trading name is Brainstorm." The %@
specification can be used to output an object’s description - as returned by the NSObject
-description method), which is useful when debugging, as in:
NSObject *obj = [anObject aMethod];
char *result;
NSString *string;
string = @"Hi!";
result = [string cString];
2.8.4 NSMutableString
NSStrings are immutable objects; meaning that once they are created, they cannot be
modified. This results in optimised NSString code. To modify a string, use the subclass of
NSString, called NSMutableString. Use a NSMutableString wherever a NSString could
be used.
An NSMutableString responds to methods that modify the string directly - which is not
possible with a generic NSString. To create a NSMutableStringuse stringWithFormat::
NSString *name = @"Brainstorm";
NSMutableString *str;
str = [NSMutableString stringWithFormat: @"Hi!, %@", name];
While NSString’s implementation of stringWithFormat: returns a NSString,
NSMutableString’s implementation returns an NSMutableString.
Note. Static strings created with the @"..." construct are always immutable.
NSMutableStrings are rarely used because to modify a string, you normally create a new
string derived from an existing one.
A useful method of the NSMutableString class is appendString:, which takes an NSString
argument, and appends it to the receiver:
NSString *name = @"Brainstorm";
NSString *greeting = @"Hello";
NSMutableString *s;
[s appendString: name];
This code produces the same result as:
NSString *name = @"Brainstorm";
NSString *greeting = @"Hello";
NSMutableString *s;
int
main (void)
{
CREATE_AUTORELEASE_POOL(pool);
NSString *name = @"This string was created by GNUstep";
int
main (void)
{
CREATE_AUTORELEASE_POOL(pool);
NSString *string;
22 Objective-C GNUstep Base Programming Manual
/*
* <missing code: do something with string...>
*/
RELEASE(pool);
return 0;
}
Chapter 3: Working with Objects 23
Objective-C and GNUstep provide a rich object allocation and memory management frame-
work. Objective-C affords independent memory allocation and initialization steps for ob-
jects, and GNUstep supports three alternative schemes for memory management.
This approach is generally not recommended since the Retain/Release style of memory
management is significantly less leak-prone while still being quite efficient.
3.2.2.3 Summary
The following summarizes the retain/release-related methods:
Method Description
-retain increases the reference count of an object by 1
-release decreases the reference count of an object by 1
-autorelease decreases the reference count of an object by 1 at some stage in
the future
+alloc and allocates memory for an object, and returns it with retain count
+allocWithZone: of 1
-copy, -mutableCopy, makes a copy of an object, and returns it with retain count of 1
copyWithZone: and -
mutableCopyWithZone:
-init and any initialises the receiver, returning the retain count unchanged. -
method whose name init has had no effect on the reference count.
begins with init
-new and any method allocates memory for an object, initialises it, and returns the
whose name begins result.
with new
-dealloc deallocates object immediately (regardless of value of retain count)
convenience allocate memory for an object, and returns it in an autoreleased
constructors state (retain=1, but will be released automatically at some stage
in the future). These constructors are class methods whose name
generally begins with the name of the class (initial letter converted
to lowercase).
The following are the main conventions you need to remember:
• If a unit of code allocates, retains, or copies an object, the same unit, loosely speaking,
is responsible for releasing or autoreleasing it at some future point. It is best to balance
retains and releases within each individual block of code.
• If you receive an object, it should remain valid until the object which provided it is
sent another message or until the autorelease pool which was in use at the point when
you received it is emptied. So you can usually expect it to remain valid for the rest
of the current method call and can even return it as the result of the method. If
you need to store it away for future use (e.g. as an instance variable, or to use after
emptying/destroying an autorelease pool, or to be used after sending another message
to the object’s owner), you must retain it.
• The retain counts mentioned are guidelines only ... more sophisticated classes often
perform caching and other tricks, so that +alloc methods may retain an instance from
a cache and return it, and -init methods may release their receiver and return a
different object (possibly obtained by retaining a cached object). In these cases, the
retain counts of the returned objects will obviously differ from the simple examples,
but the ownership rules (how you should use the returned values) remain the same.
4.1 Interface
The interface is included in the source using #include:
#include "SomeClass.h"
To ensure that a Header file is included only once, it is usual to protect it with pre-compiler
defines:
#ifndef _MY_CLASS_H_INCLUDED
#define _MY_CLASS_H_INCLUDED
/* HEADER FILE */
#endif
This is the standard C technique to protect header files from being included more than once.
A cleaner alternative, introduced in Objective-C, is to use the #import directive instead
of #include. The compiler will automatically include #imported files no more than once,
even if multiple import statements are encountered. Thus, you can do away with the messy
preprocessor conditionals in the header file.
You should be careful, however, to only use #import for Objective-C interface headers, and
continue using #include for standard C files. It is possible, though not likely, that regular
C headers may rely on being included multiple times in some cases. Also, you may need
to include the compiler directive -Wno-import to gcc to avoid a didactic warning to this
effect.
// class methods
+ (id) new;
+ (id) newWithX: (float)x0 Y: (float)y0;
+ (Point*) point;
+ (Point*) pointWithX: (float)x0 Y: (float)y0;
// instance methods
- (id) init;
- (id) initWithX: (float)x0 Y: (float)y0;
- (float) x; // (field accessor)
- (float) y;
- (void) setX: (float)newX;
- (void) setY: (float)newY;
@end
• The interface file should import the interface of the superclass of the class it is defining.
• The interface is enclosed between the compiler directives @interface and @end.
• @interface Point : Object names the class and links it to the superclass. If no su-
perclass is named, and the directive is without a colon, the compiler assumes that a
root class is being created. You more than likely don’t want to do this.
• Braces enclose declared instance variables; each class’s instance will have all these
instance variables including instance variables inherited from the superclass, and from
the superclass of the superclass, extending to the root class.
• Instance variables may be declared as private, protected, or public. An instance’s
private variables may only be accessed by instances of this class. An instance’s protected
variables may be accessed by instances of this class or instances of subclasses of this
class. Public variables may be accessed by any code. This is analogous to the usage
in C++ and Java. If you do not mark your instance variable declaration explicitly, it is
made protected by default.
• Method declarations that begin with a "+" sign are class methods, and are defined
for the class object. Thus, you can call them without creating an instance, and their
implementations do not have access to any instance variables. A class object inherits
class methods from superclasses.
Chapter 4: Writing New Classes 33
• Method declarations that begin with a "-" sign are instance methods, and are defined
for class instances. Class instances inherit instance methods from superclasses.
• A method may share the name of an instance variable.
• A method return type is declared using the C syntax for type casts:
- (float) x;
which is a method returning a float.
• Argument types can be declared in the same way as method return types:
- (void) setX: (float)newX;
which is a method that returns nothing, and takes a single float as its argument.
Note. The default type for methods and messages (id) is assumed when a return
or argument type is not explicitly declared. For example, ’-name’ implicitly means a
method returning id (i.e. an object). It is usually better to avoid this and use explicit
typing as in
- (NSString*) name;
Class names may also appear in interface files at times when instance variables, return
values and arguments are statically typed:
#import "Foundation/NSObject.h"
@class Point
- (Point *) lowerLeft;
- (float) sideLength;
- (void) setLowerLeft: (Point *)newLowerLeft;
- (void) setSideLength: (float)newSideLength;
@end
Here, we see the Point class we declared earlier being used as a component in Square’s
definition. Because this class is only referred to here to declare variables and method
signatures, it suffices to reference it only using the @class directive. On the other hand,
the implementation file may need to send messages to Point instances and would be better
of importing the interface in this case.
The compiler will produce a warning if you don’t include it, and no type checking can
be performed (to see if class instances respond to the messages you send to them), but
compilation will succeed. It is best to take advantage of type-checking when you can,
however, and include interfaces that messages are to be sent to.
There is one situation where you must include the interface however. If you are implement-
ing a new class, you always need to include the interface of the superclass; @class cannot
be used in this case because the compiler needs to know the details of the superclass and
its instance variables etc., so as to create a fully working new class. If you try using @class
in this situation, compilation will abort.
4.2 Implementation
An interface file declares a class, while an implementation file implements it. The separa-
tion between the interface and implementation file yields a black box concept where the
programmer using the class need only be concerned with the interface and its declared
methods, superclasses, and instance variables. The implementation of classes is transparent
to the programmer who may use them without detailed knowledge of their structures.
Implementation files use the .m extension, to distinguish them from ordinary C files.
Chapter 4: Writing New Classes 35
// ...
- (void)setY: (float)newY
{
// statements ...
}
@end
The implementation file uses #import to include a named interface file holding all decla-
rations. Then it places method implementations for the class between @implementation
and @end directives. Each method declared in the interface must be implemented. Instance
variables may be referred to in instance methods (the ones with a “-” in front of them) but
not class methods (the ones with a “+”).
- (float) x
{
return x;
}
if (![self fooIsInitialized])
[self initializeFoo];
return foo;
}
Super is used to refer to method implementations in the superclass of the instance. It
is useful when overriding methods and when writing initializers, as discussed in the next
section.
+ point
{
Point *point;
Chapter 4: Writing New Classes 37
- init
{
return [self initWithX: 0.0 Y: 0.0];
}
instance, and should be the one that all other initializers “ground out” in. In other words,
all other initializers should be chained so that they either call the designated initializer, or
they call another initializer that (eventually) calls it.
The importance of having a designated initializer is this: when a subclass is created, it
need only override the designated initializer to ensure that all of its instances are properly
initialized. If this is not done, external code could invoke an initializer that initializes
only the superclass’s instance variables, and not the subclass’s. To avoid this, each class
designates a “ground out” initializer to which other initializers ultimately delegate. Then
the subclass overrides this initializer, and in its own designated initializer, makes a call to
it, to ensure that the superclass is initialized properly. Thus:
@implementation SuperClass
- initWithA: (int)a
{
return [self initWithA:a B:0]; // 0 is default value
}
@implementation SubClass
Finally, notice we end with a call to [super dealloc]. This should always be done in
dealloc implementations, and you should never concern yourself with deallocating struc-
tures that are associated with a superclass, since it will take care of this itself.
4.3 Protocols
Protocols in Objective-C provide a level of flexibility beyond class structure in determining
what messages objects respond to. They are similar to interfaces in Java but more flexible.
There are two types of protocol in Objective-C: informal protocols, where we document
methods to which objects will respond, and specify how they should behave, and formal
protocols, where we provide a list of methods that an object will support in a format where
the compiler can check things, and the runtime can also check that an object conforms to
the protocol. Informal protocols are merely convention, but are useful where we want to say
that some system will work as long as it (or its delegate) implements some subset of a group
of methods. Formal protocols are of more use when we want the compiler or runtime to
check that an object implements all of a group of methods itself. Formal protocols form an
inheritance hierarchy like classes, and a given class may conform to more than one protocol.
Thus, formal protocols are identical in many respects to Java interfaces.
Informal protocols are closely associated with Categories, another Objective-C language
facility, and will be discussed in the next section.
@protocol List
- (void) add: (id) item;
- (void) remove: (id) item;
- getAtIndex: (int)idx;
- (void) clear;
@end
...
@implementation BiQueue
// must implement both List’s and LinkedList’s methods ...
- add: (id) item
{
// ...
}
- addFirst: (id)item
{
// ...
}
@end
To declare conformance to multiple protocols, do something like this:
@interface ContainerWindow < List, Window >
...
@end
The implementation must include all methods in both protocols.
4.4 Categories
Categories provide a way in Objective-C to add new methods to an existing class, without
declaring a subclass. Once the category is declared and implemented, all instances of the
existing class that are created will include the capability to respond to the new methods.
Furthermore, subclasses of the class will inherit these methods. However, it is not possible
to add instance variables to a class using a category. Categories do not have an obvious
parallel in other major object-oriented languages (with the exception of Ruby), but it is
well worth taking the trouble to understand them and the benefits they can provide.
A category is declared in connection with the class it is going to modify. (You can think of
it as a new “category” of instances of this class.)
#import "Point.h"
y += ty;
return self;
}
#import "Point.h"
@implementation Point
// public method implementations ...
@end
they will need to put up with the compiler warnings. Alternatively, you could declare the
Protected category in a separate interface file (e.g., “PointProtected.h”), and provide
this interface file with the understanding that it should only be imported and used by a
subclass’s interface file.
Using Convention
Another approach to providing protected methods that the class or subclass can use is to
prefix these methods with an underscore (’ ’). These methods will still be visible publicly,
but programmers will know, by convention, not to use them externally, and the Appendix A
[GSDoc], page 101 will automatically mark these in API documentation as off-limits.
An alternative approach to providing private methods is to simply declare them as functions
within the implementation file itself. The catch to this is that these functions will not have
access to the class’s instance variables. You will need to pass these in manually whenever
you invoke them from an ordinary method.
...
Chapter 5: Advanced Messaging 49
5 Advanced Messaging
Objective-C provides some additional possibilities for message routing besides the capabil-
ities described so far (inheritance and categories). One of the most important is that it is
possible for an object, upon receiving a message it has not been set up to respond to, to
forward that message to another object. A second important capability, which forwarding
relies on, is the ability to represent method implementations directly in code. This supports
various reflective operations as well as optimization where messages are sent many times.
In fact, when the method implementation is actually called, it additionally receives two
implicit arguments: the receiver and the selector. These additional hidden arguments may
be referred to in the source code by the names self and _cmd.
The process of looking up the method implementation in the receiver at runtime is known
as dynamic binding. This is part of what makes the language powerful and flexible, but it
is inevitably (despite clever caching strategies used in the runtime library) a little slower
than a simple function call in C. There are, however, ways of short-circuiting the process
in cases where performance is at a premium. Before discussing this, we must first cover the
concepts of selectors and implementations in greater detail.
50 Objective-C GNUstep Base Programming Manual
5.2 Selectors
So far we have been using the following syntax to send messages to objects:
[myArray removeObjectIdenticalTo: anObject];
The example sends the message named removeObjectIdenticalTo: to myArray with the
argument anObject.
An alternative method of writing this is the following:
SEL removalSelector = @selector(removeObjectIdenticalTo:);
[myArray performSelector: removalSelector withObject: anObject];
Here, the first line obtains the desired method selector in the form of a compiled repre-
sentation (not the full ASCII name), and the second line sends the message as before, but
now in an explicit form. Since the message that is sent is now effectively a variable set at
runtime, this makes it possible to support more flexible runtime functioning.
adaptor class, you just register the method you want called directly with the actuating
element. If you need to send the event to multiple objects, however, you would need to
write a special method to multiplex the event out. This would be approximately comparable
effort to what is always required in Java, and is only needed in the minority of cases.
In this case, the compiler will not issue a warning, because it only knows that anObject is
of type id . . . so it doesn’t know what methods the object implements.
At runtime, if the Objective-C runtime library fails to find a method implementation for the
alert: message in the SomeClass class or one of its superclasses, an exception is generated.
This can be avoided in one of two ways.
The first way is to check in advance whether the method is implemented:
if ([anObject respondsToSelector: @selector(alert:)] == YES)
{
[anObject alert: additionalObject]; // send it a message.
}
else
{
// Do something else if the object can’t be alerted
}
The second way is for the object the message was sent to to forward it somewhere else.
5.3 Forwarding
What actually happens when the GNU Objective-C runtime is unable to find a method
implementation associated with an object for a given selector is that the runtime instead
sends a special forwardInvocation: message to the object. (Other Objective-C runtimes
do the same, but with a slightly different message name and structure.) The object is
then able to use the information provided to handle the message in some way, a common
mechanism being to forward the message to another object known as a delegate, so that
the other object can deal with it.
- (void) forwardInvocation: (NSInvocation*)invocation
{
if ([forwardee respondsToSelector: [invocation selector]])
return [invocation invokeWithTarget: forwardee];
else
return [self doesNotRecognizeSelector: [invocation selector]];
}
• invocation is an instance of the special NSInvocation class containing all the infor-
mation about the original message sent, including its selector and its arguments.
• forwardee is an instance variable containing the id of an object which has been de-
termined to be likely to implement methods that this object does not.
• The NSInvocation class has a convenience method that will pass the message on to a
target object given as argument.
• The doesNotRecognizeSelector method is a fallback which is implemented in
NSObject. Unless it has been overidden, its behavior is to raise a runtime exception
(a NSInvalidArgumentException to be exact), which generates an error message and
aborts.
Forwarding is a powerful method for creating software patterns. One of these is that for-
warding can be used to in effect provide a form of multiple inheritance. Note, however that,
unlike inheritance, a forwarded method will not show up in tests like respondsToSelector
Chapter 5: Advanced Messaging 53
and isKindOfClass:. This is because these methods search the inheritance path, but ignore
the forwarding path. (It is possible to override them though.)
Another pattern you may come across is surrogate object: surrogates forward messages to
other objects that can be assumed to be more complex. The forwardInvocation: method
of the surrogate object receives a message that is to be forwarded; it determines whether
or not the receiver exists, and if it does not, then it will attempt to create it. A proxy
object is a common example of a surrogate object. A proxy object is useful in a remote
invocation context, as well as certain scenarios where you want one object to fulfill functions
of another.
5.4 Implementations
Recall that when a message is sent, the runtime system searches for a method implemen-
tation associated with the recipient object for the specified selector. (Behind the scenes
this is carried out by a function “objc_msgSend()”.) This may necessitate searches across
multiple superclass objects traversing upwards in the inheritance hierarchy, and takes time.
Once the runtime finds an implementation for a class, it will cache the information, sav-
ing time on future calls. However, even just checking and accessing the cache has a cost
associated with it. In performance-critical situations, you can avoid this by holding on to
an implementation yourself. In essence, implementations are function pointers, and the
compiler provides a datatype for storing them when found at runtime:
SEL getObjSelector = @selector(getObjectAtIndex:);
// get the ’getObjectAtIndex’ implementation for NSArray ’taskArray’
IMP getObjImp = [taskArray methodForSelector: getObjSelector];
// call the implementation as a function
id obj = (getObjImp)( taskArray, getObjSelector, i );
Here, we ask the runtime system to find the ’taskArray’ object’s implementation of
’getObjectAtIndex’. The runtime system will use the same algorithm as if you were
performing a method call to look up this code, and then returns a function pointer to it.
In the next line, this pointer is used to call the function in the usual C fashion. Notice
that the signature includes both the object and the selector – recall that these are the
two implicit arguments, self and _cmd, that every method implementation receives. The
actual type definition for IMP allows for a variable number of additional arguments, which
are the explicit arguments to the method call:
typedef id (*IMP)(id, SEL, ...);
The return type of IMP is id. However, not all methods return id; for these others you
can still get the implementation, but you cannot use an IMP variable and instead must cast
it yourself. For example, here is such a cast for a method taking a double and returning
’double’:
double (*squareFunc)( id, SEL, double );
double result;
You need to declare such a function pointer type for any method that returns something
besides id or int. It is not necessary to declare the argument list (double) as we did above;
the first line could have been “double (*squareFunc)( id, SEL, ... )” instead.
An excellent exposition of the amount of time saved in using methodForSelector and
other details of the innards of Objective-C and the Foundation may be found here:
http://www.mulle-kybernetik.com/artikel/Optimization/opti-3.html.
You should realize that it is only worth it to acquire the IMP if you are going to call it
a large number of times, and if the code in the method implementation itself is not large
compared with the message send overhead. In addition, you need to be careful not to call
it when it might be the wrong function. Even when you are sure of the class of the object
you are calling it on, Objective-C is sufficiently dynamic that the correct function could
change as a program runs. For example, a new category for a class could be loaded, so that
the implementation of a method changes. Similarly, a class could be loaded that poses as
another, or one that was posing stops doing so. In general, IMPs should be acquired just
before they are to be used, then dropped afterwards.
Chapter 6: Exception Handling, Logging, and Assertions 55
6.1 Exceptions
GNUstep exception handling provides for two things:
1. When an error condition is detected during execution, control is passed to a special
error-handling routine, which is given information on the error that occurred.
2. This routine may itself, if it chooses, pass this information up the function call stack to
the next higher level of control. Often higher level code is more aware of the context
in which the error is occurring, and can therefore make a better decision as to how to
react.
value that will hopefully be recognized as invalid, and perhaps using an errno-like strategy
where the caller knows to examine the value of a certain global variable. This is inelegant,
difficult to enforce, and leads to the need, with void methods, to document that “the caller
should check errno to see if any problems arose”.
6.2 Logging
GNUstep provides several distinct logging facilities best suited for different purposes.
6.2.1 NSLog
The simplest of these is the NSLog(NSString *format, ...) function. For example:
NSLog(@"Error occurred reading file at line %d.", lineNumber);
This would produce, on the console (stderr) of the application calling it, something like:
2004-05-08 22:46:14.294 SomeApp[15495] Error occurred reading file at line 20.
The behavior of this function may be controlled in two ways. First, the user default
GSLogSyslog can be set to “YES”, which will send these messages to the syslog on sys-
tems that support that (Unix variants). Second, the function GNUstep uses to write the
log messages can be overridden, or the file descriptor the existing function writes to can be
overridden:
// these changes must be enclosed within a lock for thread safety
NSLock *logLock = GSLogLock();
[logLock lock];
[logLock unlock];
Due to locking mechanisms used by the logging facility, you should protect these changes
using the lock provided by GSLogLock() (see Chapter 8 [Threads and Run Control], page 85
on locking).
The NSLog function was defined in OpenStep and is also available in Mac OS X Cocoa,
although the overrides described above may not be. The next set of logging facilities to be
described are only available under GNUstep.
At runtime, whether or not logging is enabled, a debug log method is called like this:
NSDebugLLog(@"ParseError", @"Error parsing file at line %d.", lineNumber);
Here, the first argument to NSDebugLog, “ParseError”, is a string key that specifies the
category of message. The message will only actually be logged (through a call to NSLog())
if this key is in the set of active debug categories maintained by the NSProcessInfo object
for the application. Normally, this list is empty. There are three ways for string keys to
make it onto this list:
• Provide one or more startup arguments of the form --GNU-Debug=<key> to the pro-
gram. These are processed by GNUstep and removed from the argument list before
any user code sees them.
• Call [NSProcessInfo debugSet] at runtime, which returns an NSMutableSet. You
can add (or remove) strings to this set directly.
• The GNU-Debug user default nay contain a comma-separated list of keys. However, note
that [NSUserDefaults standardUserDefaults] must first be called before this will
take effect (to read in the defaults initially).
While any string can be used as a debug key, conventionally three types of keys are com-
monly used. The first type expresses a “level of importance” for the message, for example,
“Debug”, “Info”, “Warn”, or “Error”. The second type of key that is used is class name.
The GNUstep Base classes used this approach. For example if you want to activate debug
messages for the NSBundle” class, simply add ’NSBundle’ to the list of keys. The third
category of key is the default key, ’dflt’. This key can be used whenever the specificity of
the other key types is not required. Note that it still needs to be turned on like any other
logging key before messasges will actually be logged.
There is a family of NSDebugLog functions with slightly differing behaviors:
NSDebugLLog(key, format, args,...)
Basic debug log function already discussed.
NSDebugLog(format, args,...)
Equivalent to NSDebugLLog with key “dflt” (for default).
NSDebugMLLog(level, format, args,...)
Equivalent to NSDebugLLog but includes information on which method the log-
ging call was made from in the message.
NSDebugMLog(format, args,...)
Same, but use ’dflt’ log key.
NSDebugFLLog(level, format, args,...)
As NSDebugMLLog but includes information on a function rather than a method.
NSDebugFLog(format, args,...)
As previous but using ’dflt’ log key.
The implementations of the NSDebugLog functions are optimized so that they consume little
time when logging is turned off. In particular, if debug logging is deactivated at compile
time, there is NO performance cost, and if it is completely deactivated at runtime, each call
entails only a boolean test. Thus, they can be left in production code.
Chapter 6: Exception Handling, Logging, and Assertions 61
There is also a family of NSWarn functions. They are similar to the NSDebug functions
except that they do not take a key. Instead, warning messages are shown by default un-
less they are disabled at compile time by setting ’warn = no’ or undefining GSWARN, or at
runtime by adding “NoWarn” to [NSProcessInfo debugSet]. (Command-line argument
--GNU-Debug=NoWarn and adding “NoWarn” to the GNU-Debug user default will also work.)
NSWarnLog(), NSWarnLLog(), NSWarnMLLog, NSWarnMLog, NSWarnFLLog, and NSWarnFLog
are all similar to their NSDebugLog counterparts.
6.3 Assertions
Assertions provide a way for the developer to state that certain conditions must hold at
a certain point in source code execution. If the conditions do not hold, an exception is
automatically raised (and succeeding code in the block is not executed). This avoids an
operation from taking place with illegal inputs that may lead to worse problems later.
The use of assertions is generally accepted to be an efficient means of improving code quality,
for, like unit testing, they can help rapidly uncover a developer’s implicit or mistaken
assumptions about program behavior. However this is only true to the extent that you
carefully design the nature and placement of your assertions. There is an excellent discussion
of this issue bundled in the documentation with Sun’s Java distribution.
The assertion facilities are similar to but a bit more flexible than those in Java/JDK 1.4
since you can override the assertion handler.
Chapter 7: Distributed Objects 65
7 Distributed Objects
Until now we have been concentrating on using the Objective-C language to create programs
that execute in a single process. But what if you want your program to interact with objects
in other processes, perhaps running on different machines?
As a simple example, we may have a client process that needs to access a telephone directory
stored on a remote server. The client process could send a message to the server that
contained a person’s name, and the server could respond by returning that person’s number.
The GNUstep base library provides a powerful set of classes that make this type of remote
messaging not only possible, but easy to program. So what do these classes do and how
can we use them? To answer that we must first look at the way code interacts with objects
in a single process, and then look at how we can achieve the same interaction with objects
that exist in different processes.
/*
* Get the default NSConnection object
* (a new one is automatically created if none exists).
*/
NSConnection *connXion = [NSConnection defaultConnection];
/*
* Set the responding server object as
* the root object for this connection.
*/
Chapter 7: Distributed Objects 67
/*
* Try to register a name for the NSConnection,
* and report an error if this is not possible.
*/
if ([connXion registerName: @"DirectoryServer"] == NO)
{
NSLog(@"Unable to register as ’DirectoryServer’");
NSLog(@"Perhaps another copy of this program is running?");
exit(1);
}
{
CREATE_AUTORELEASE_POOL(pool);
/*
* Create a new socket port for your connection.
*/
NSSocketPort *port = [NSSocketPort port];
/*
* Create a connection using the socket port.
*/
NSConnection *connXion = [NSConnection connectionWithReceivePort: port
sendPort: port];
/*
* Set the responding server object as
* the root object for this connection.
*/
[connXion setRootObject: telephoneDirectory];
/*
* Try to register a name for the NSConnection,
* and report an error if this is not possible.
*/
if ([connXion registerName: @"DirectoryServer"
withNameServer: [NSSocketPortNameServer sharedInstance]] == NO)
{
NSLog(@"Unable to register as ’DirectoryServer’");
NSLog(@"Perhaps another copy of this program is running?");
exit(1);
}
RELEASE(pool);
return 0;
}
In the above example, we specify that the socket port name server is used to register the
name for the connection ... this makes the connection name visible to processes running on
other machines.
The client side code is as follows
/* Create an instance of the NSAutoreleasePool class */
CREATE_AUTORELEASE_POOL(pool);
rootProxyForConnectionWithRegisteredName: registeredServerName
host: hostName
usingNameServer: [NSSocketPortNameServer sharedInstance]];
/*
* Declare the TelephoneDirectory class that
* implements the ’teleNumber’ instance method.
*/
@interface TelephoneDirectory : NSObject <TelephoneDirectory>
@end
/*
* Define the TelephoneDirectory class
* and the instance method (teleNumber).
*/
@implementation TelephoneDirectory : NSObject
- (char *) teleNumber: (char *) personName
{
if (strcmp(personName, "Jack") == 0) return " 0123 456";
else if (strcmp(personName, "Jill") == 0) return " 0456 789";
else return " Number not found";
}
@end
/*
* The main() function: Get the telephone number for
* ’personName’ from the server registered as ’DirectoryServer’.
*/
int main(int argc, char *argv[])
{
char *personName = argv[1];
char *returnedNumber;
id<TelephoneDirectory> proxyForDirectory;
CREATE_AUTORELEASE_POOL(pool);
if (proxyForDirectory == nil)
printf("\n** WARNING: NO CONNECTION TO SERVER **\n");
else printf("\n** Connected to server **\n");
For the default connection type (a connection only usable on the local host between processes
run by the same person), a private file (or the registry on ms-windows) is used to hold the
name registration information.
For connections using socket ports to communicate between hosts, an auxiliary process will
automatically be started on each machine, if it isn’t running already, that handles this,
allowing the server to register and the client to send a query behind the scenes. This
GNUstep Distributed Objects Name Server runs as ’gdomap’ and binds to port 538. See the
manual page or the HTML “GNUstep Base Tools” documentation for further information.
• The server also makes sure the client is not already connected and playing the game
(i.e. they cannot play two games at the same time - that would be cheating).
• In addition to a proxy for the server being obtained at the client, a proxy for the client
is received at the server. This allows two-way messaging, where the client can send
messages to the server and the server can send messages to the client (e.g. the state of
play may be affected by the actions of other players, or by other events at the server).
Two protocols will therefore be required, one for the methods implemented at the server
and one for those implemented at the client.
Have a look at the program code in the following sections and added comments. Can you
work out what is happening at the server and client? If you have any difficulties then refer
to the relevant sections in this manual, or to class documentation here or at the Apple web
site.
Chapter 7: Distributed Objects 73
@end
The protocol will be saved as GameClient.h.
@end
The protocol will be saved as GameServer.h.
/*
* GameClient class declaration:
* Adopt the GameClient protocol.
*/
@interface GameClient : NSObject <GameClient>
@end
74 Objective-C GNUstep Base Programming Manual
/*
* GameClient class implementation.
*/
@implementation GameClient
/*
* Implement clientMessage: as declared in the protocol.
* The method simply prints a message at the client.
*/
- (void) clientMessage: (NSString*)theMessage
{
printf([theMessage cString]);
}
/*
* Implement clientReply: as declared in the protocol.
* The method simply returns the character entered
* at the client keyboard.
*/
- (int) clientReply
{
return getchar();
}
@end // End of GameClient class implementation.
/*
* The main function of the client program.
*/
int main(int argc, char **argv)
{
CREATE_AUTORELEASE_POOL(pool);
id<GameServer> server;
int result;
NSString *name;
id client;
/*
* The NSUserName() function returns the name of the
* current user, which is sent to the server when we
* try to join the game.
*/
name = NSUserName();
/*
* Create a GameClient object that is sent to
* the server when we try to join the game.
*/
Chapter 7: Distributed Objects 75
/*
* Try to get a proxy for the root object of a server
* registered under the name ’JoinGame’. Since the host
* is ’*’, we can connect to any server on the local network.
*/
server = (id<GameServer>)[NSConnection
rootProxyForConnectionWithRegisteredName: @"JoinGame"
host: @"*"
usingNameServer: [NSSocketPortNameServer sharedInstance]];
if (server == nil)
{
printf("\n** No Connection to GameServer **\n");
result = 1;
}
/*
* Try to join the game, passing a GameClient object as
* the client, and our user-name as name. The ’client’
* argument will be received as a proxy at the server.
*/
else if ([server mayJoin: client asPlayer: name] == NO)
{
result = 1; // We cannot join the game.
}
else
{
/*
* At this point, we would actually start to play the game.
*/
[server startGame: name]; // Start playing game.
[server endGame: name]; // Finally end the game.
result = 0;
}
RELEASE(pool);
return result;
}
To summarise the code at the client:
• We obtained a proxy for the server and can now communicate with the server using
the methods declared in the GameServer protocol.
• We passed a GameClient object and our user-name to the server (the GameClient
object is received as a proxy at the server). The server can now communicate with the
client using the methods declared in the GameClient protocol.
76 Objective-C GNUstep Base Programming Manual
• When the game is in progress, the server can alter the state of the client object to
reflect the success of the player.
/*
* GameServer class declaration:
* Adopt the GameServer protocol and declare
* GameServer instance variables.
*/
@interface GameServer : NSObject <GameServer>
{
NSMutableDictionary *delayUntil; // Delays to re-joining GameServer.
NSMutableDictionary *currentPlayers; // Proxies to each client.
NSMutableDictionary *hasWon; // Success in game for each player.
}
@end
/*
* GameServer class implementation.
*/
@implementation GameServer
/*
* Create a dictionary that will record
* a win for any of these named players.
*/
hasWon = [[NSMutableDictionary alloc]
initWithCapacity: 10];
}
return self;
}
/*
* Implement mayJoin:: as declared in the protocol.
* Adds the client to the list of current players.
* Each player is represented at the server by both
* name and by proxy to the received client object.
* A player cannot join the game if they are already playing,
* or if joining has been delayed until a later date.
*/
- (BOOL) mayJoin: (id)client asPlayer: (NSString*)name
{
NSDate *delay; // The time a player can re-join the game.
NSString *aMessage;
if (name == nil)
{
NSLog(@"Attempt to join nil user");
return NO;
}
{
/* Inform the client that they cannot join. */
aMessage = @"\nSorry, but you are already playing GameServer!\n";
[client clientMessage: aMessage];
return NO;
}
/*
* Can the player join the game? Yes if there is
* no restriction or if the time delay has passed;
* otherwise no, they cannot join.
*/
if (delay == nil || [delay timeIntervalSinceNow] <= 0.0)
{
/* Remove the old restriction on re-joining the game. */
[delayUntil removeObjectForKey: name];
/*
* Implement startGame: as declared in the protocol.
* Simply ask the player if they want to win, and get
* there reply.
*/
- (int) startGame: (NSString *)name
{
NSString *aMessage;
id client;
Chapter 7: Distributed Objects 79
int reply;
aMessage = @"\nDo you want to win this game? (Y/N <RET>) ... ";
[client clientMessage: aMessage];
/*
* Implement endGame: as declared in the protocol.
* Removes a player from the game, and either sets
* a restriction on the player re-joining or removes
* the current restriction.
*/
- (BOOL) endGame: (NSString*)name
{
id client;
NSString *aMessage, *yesOrNo;
NSDate *now, *delay;
NSTimeInterval twoHours = 2 * 60 * 60; // Seconds in 2 hours.
if (name == nil)
{
NSLog(@"Attempt to end nil user");
return NO;
}
}
else // Player lost
{
/*
* Set a time delay for re-joining the game,
* and send a message to the client.
*/
[delayUntil setObject: delay forKey: name];
aMessage = @"\nYou lost, but you can re-join GameServer in 2 hours.\n";
[client clientMessage: aMessage];
}
/*
* The main function of the server program simply
* vends the root object and starts the runloop.
*/
int main(int argc, char** argv)
{
CREATE_AUTORELEASE_POOL(pool);
GameServer *server;
NSSocketPort *port;
NSConnection *connXion;
• We vend the server’s root object and start a runloop, allowing clients to connect with
Chapter 7: Distributed Objects 81
the server.
• When we receive a proxy for a client object, we communicate with that client using
methods declared in the ClientServer protocol.
• When the game is in progress, the server can alter the state of each client object to
reflect the success of each player.
I hope you managed to understand most of the code in this example. If you are reading
the on-screen version, then you can copy and paste the code to suitably named files, cre-
ate makefiles, and then make and run each program. What message is displayed if you
immediately try to re-join a game after losing? And after winning?
Exercise: Modify the server code so that the server records the number of wins for each
player, and displays this information at both the start and end of each game.
- (oneway void)noWaitForReply;
82 Objective-C GNUstep Base Programming Manual
• The in, out and inout qualifiers can be used with pointer arguments to control the
direction in which an argument is passed. The protocol declaration for the receiving
methods would look something like this:
/*
* The value that ’number’ points to will be passed in to the remote process.
* (No need to return the argument’s value from the remote process.)
*/
- setValue: (in int *)number;
/*
* The value that ’number’ points to will be passed out of the remote process.
* (No need to send the argument’s value to the remote process.)
*/
- getValue: (out int *)number;
/*
* The value that ’number’ points to is first passed in to the remote
* process, but will eventually be the value that is passed out of the
* remote process. (Send and return the argument’s value.)
*/
- changeValue: (inout int *)number;
Passing of arguments by reference is very restricted in Objective-C. it applies only to
pointers to C data types, not to objects, and except for the special case of a pointer to
a nul terminated C string (char*) the pointer is assumed to refer to a single data item
of the specified type.
/*
* A method passing an unsigned short integer by reference.
*/
- updateCounter: (inout unsigned shortn *)value;
/*
* A method passing a structure by reference.
*/
- updateState: (inout struct stateInfo *)value;
/*
* As a special case, a char (or equivalent typedef) passed by reference
* is assumed to be a nul terminated string ... there is no way to pass
* a single character by reference:
*/
- updateBuffer: (inout char *)str;
• The bycopy and byref qualifiers can be used in a protocol when the argument or return
type is an object.
Chapter 7: Distributed Objects 83
/*
* Copy of object will be received in the remote process.
*/
- sortNames: (bycopy id)listOfNames;
/*
* Copy of object will be returned by the remote process.
*/
- (bycopy id)returnNames;
By default, large objects are normally sent byref, while small objects like NSStrings
are normally sent bycopy, but you cannot rely on these defaults being adopted and
should explicitly state the qualifier in the protocol.
The bycopy qualifier can also be used in conjunction with the out qualifier, to indicate
that an object will be passed out of the remote process by copy rather than by proxy
(no need to send the object).
/*
* The object will not be received in the remote process, but the object
* will be returned bycopy.
*/
- sortAndReturn: (bycopy out id *)listOfNames;
You should be aware that some classes ignore the bycopy qualifier and the object will
be sent by reference. The bycopy qualifier will also be ignored if the remote process
does not have the class of the object in its address space, since an object’s instance
variables are accessed through the object’s methods.
When a copy of an object is sent to a remote process, only the object’s instance variables
are sent and received (an object’s methods exist in the address space of the object’s
class, not in the address space of the individual object).
the use of remote invocation and is possible due to the Objective-C message-forwarding
facility (Chapter 5 [Forwarding], page 49).
In GNUstep, there are proxies on the client and server side that handle network communica-
tions and serialization/deserialization of arguments and return values just as in CORBA and
RMI, but when it comes to responding to the client and server protocol method calls them-
selves, they are intercepted through the use of the forwardInvocation: method, where
they can be passed on to the registered client and server objects through the ordinary
Objective-C message sending mechanism.
This exception is raised if a message takes too long to arrive at the remote process, or
if a reply takes too long to return. This will happen if the remote process is busy, has
hung, or if there is a problem with the network. The best way to handle the exception
is to close the connection to the remote process.
• An exception is raised in the remote process while the remote process is executing a
method.
In most cases you can deal directly with these exceptions in the process in which they
were raised; i.e. without having to consider the network connection itself.
8 Base Library
The GNUstep Base library is an implementation of the OpenStep Foundation, a nongraph-
ical API supporting for data management, network and file interaction, date and time
handling, and more. Much of the API consists of classes with defined methods, but unlike
many “class libraries” it also includes functions and macros when these are more appropriate
to the functionality.
Note that many other APIs developed subsequently to OpenStep are also called “Foun-
dation” – the Java standard classes and the Microsoft Windows C++ class library are two
prominent examples. In OpenStep, however, the term only applies to a non-graphical li-
brary; the graphical component is referred to as the Application Kit, or “AppKit” for short.
Although the OpenStep API underwent several refactorings subsequent to its first release as
part of NeXTstep, deprecated and superseded classes and functions have not been retained.
Therefore the library still boasts a minimal footprint for its functionality.
In some cases, GNUstep has supplemented the OpenStep API, not to provide alternative
means of achieving the same goals, but to add new functionality, usually relating to tech-
nology that did not exist when the OpenStep specification was finalized, but has not, for
whatever reason, been added by Apple to the Cocoa APIs. These additions are called, ap-
propriately enough, the Base Additions library, and include classes, functions, and macros.
XML parsing facilities, for example, are provided as part of this library.
In addition, methods are sometimes added to Foundation classes. These are specially
marked in the documentation and can even be excluded at compile time (a warning will be
generated if you try to use them) if you are writing code intended to be ported to OpenStep
or Cocoa compliant systems. In addition, Cocoa has made additions to OpenStep and these
are marked as “MacOS-X”. For information on how to set compile flags, see Appendix E
[Compliance to Standards], page 113.
In deciding whether to use a given API, you need to weigh the likelihood you will need
to port the application to a platform where it will not be available, and in that case, how
much effort would be required to do without the API. If you are aiming for full portability
from the start (only a recompile needed), then you should of course avoid APIs that will
not be available. However in other cases it can be better to use whichever APIs are best
suited initially so that early development and debugging will be as efficient as possible – as
long as major redesign would not be required to later avoid these APIs.
Below, the Base and Base Additions APIs are covered in overview fashion, organized ac-
cording to functionality. For detailed documentation on individual classes and functions,
you should consult the GSDoc API references for Base and Base Additions. It may be
helpful, when reading this chapter, to keep a copy of this open in another browser window
for reference.
directly; instead it calls the -copyWithZone method, which is the sole method defined in
the NSCopying informal protocol. NSObject does not implement this protocol. If you want
objects of your class to support copying, you must implement this method yourself. If it is
not implemented, the -copy method will raise an exception if you call it.
There is a related method -(id) mutableCopy (and an NSMutableCopying informal proto-
col with a mutableCopyWithZone method) which will be explained in the following section.
GNUstep, largely via the NSObject class, provides a basic framework for comparing objects
for equality and ordering, used for sorting, indexing, and other programming tasks. These
operations are also used in several crucial places elsewhere within the base library itself.
For example, containers such as lists, sets, and hash maps are discussed in the next section
utilize these methods.
The - (BOOL) isEqual method in NSObject is useful when you want to compare objects
with one another:
if ([anObject isEqual: anotherObject])
{
// do something ...
}
The default implementation returns YES only if the two objects being compared are the
exact same object (which is the same as the result that would be returned using ’==’ to
perform the comparison). Sometimes it is useful to have two objects to be equal if their
internal state is the same, as reflected in instance variables, for example. In this case, you
can override isEqual in your class to perform such a comparison.
The -(unsigned int)hash method is useful for indexing objects, and should return the
same value for two objects of the same class that isEqual each other. The same reasoning
applies as for the isEqual method – if you want this to depend on internal state rather
than the identity of the object itself, override it. The default hash value is based on the
memory address occupied by the object.
The -(NSComparisonResult) compare: (id)object method is used in Cocoa for compar-
ing objects. It should return NSOrderedAscending if the receiver is less than the argument,
NSOrderedDescending if it is greater, otherwise NSOrderedSame. Note that this is not
meaningful for many types of objects, and is actually deprecated in GNUstep for this rea-
son.
The -(NSString *) description method in NSObject returns a short description of the
object, often used for debugging. The default implementation lists the object’s class and
memory location. If you want other information you can override it.
The methods discussed in this section are all very similar to counterparts in Java: the
equals and hashCode methods, and the Comparable interface.
-isEqual NSObject methods discussed above are used by collection instances to organize
their members. All collections retain their members (see Chapter 3 [Objects], page 23).
Unlike container APIs in some other languages, notably Java, instances of these GNUstep
classes are all immutable – once created, you cannot add or remove from them. If you
need the ability to make changes (often the case), use the mutable classes NSMutableArray,
NSMutableSet, and NSMutableDictionary. The -mutableCopy method mentioned in the
previous section will return the mutable version of a container regardless of whether the
original was mutable or not. Likewise, the -copy method returns an immutable version.
You should generally use immutable variants of objects when you don’t need to modify
them, because their implementations are more efficient. Often it is worthwhile to convert
a mutable object that has just been built into an immutable one if it is going to be heavily
accessed.
Also unlike container objects in Java, GNUstep containers possess utility methods. For
example, Arrays can sort their members, or send a message to each member individually
(like the map function in Lisp). Sets can determine whether they are equal to or subsets of
other sets. Dictionaries can save to and restore themselves from specially formatted files.
In addition to the three container types already mentioned, there is a fourth, NSCountedSet.
This is an unordered collection whose elements need not be unique, however the number of
times a given unique element has been added is tracked. This behavior is also known as bag
semantics.
All collection classes support returning an NSEnumerator object which will enumerate over
the elements of the collection. Note that if a mutable collection is modified while an enu-
merator is being used, the results are not defined.
Collections do not allow nil values or keys, but you can explicitly represent a nil object
using the special NSNull class. You simply use the singleton returned from [NSNull null].
The four container types just described handle objects, but not primitives such as float
or int. For this, you must use an NSHashTable or NSMapTable. Despite their names, these
are not classes, but data types. A set of functions is defined for dealing with them. Each
can store and retrieve arbitrary pointers keyed by other arbitrary pointers. However you
are responsible for implementing the hashing yourself. To create an NSHashTable, use the
function NSCreateHashtable. NSHashInsert and NSHashGet are the major functions, but
there are many others. There is a mostly parallel but more sophisticated set of functions
dealing with NSMapTables.
8.3.1 NSData
The NSData and NSMutableData classes manage a buffer of bytes as an object. The contents
of the buffer can be anything that can be stored in memory, a 4-dimensional array of double
for example (stored as a linear sequence). Optionally, objects of these classes can take care
88 Objective-C GNUstep Base Programming Manual
of the memory management for the buffer, growing it as needed and freeing it when they
are released.
8.3.2 NSValue
The NSValue class can wrap a single primitive value as an object so it can be used in
the containers and other places where an object reference is needed. Once initialized, an
NSValue is immutable, and there is no NSMutableValue class. You initialize it by giving it
a pointer to the primitive value, and you should be careful this does not get freed until after
the NSValue is no longer used. You can specify to the NSValue what type the primitive is
so this information can be accessed later:
int n = 10;
NSValue *theValue = [NSValue value: &n withObjCType: @encode(int)];
// ...
int *m = (int *) [theValue pointerValue];
Here, @encode is a compile-time operator that converts the data type into a string (char *)
code used at runtime to refer to the type. Object ids can also be stored within NSValues if
desired. Note that in the above case, the NSValue will be pointing to invalid data once the
local variable n goes out of scope.
If you want to wrap int or other numeric values, you should use NSNumber (a subclass
of NSValue) instead. This maintains its own copy of the data and provides convenience
methods for accessing the value as a primitive.
int n = 10;
NSNumber *theNumber = [NSNumber numberWithInt: n];
// ...
int m = [theNumber intValue];
float f = [theNumber floatValue]; // this is also valid
Notice that n ’s value is used in the initialization, not a pointer to it.
8.3.3 NSNumber
NSNumber has a subclass called NSDecimalNumber that implements a number of methods
for performing decimal arithmetic to much higher precision than supported by ordinary
long double. The behavior in terms of rounding choices and exception handling may
be customized using the NSDecimalNumberHandler class. Equivalent functionality to the
NSDecimalNumber class may be accessed through functions, mostly named NSDecimalXXX.
Both the class and the functions use a structure also called NSDecimal:
typedef struct {
signed char exponent; // Signed exponent - -128 to 127
BOOL isNegative; // Is this negative?
BOOL validNumber; // Is this a valid number?
unsigned char length; // digits in mantissa.
unsigned char cMantissa[2*NSDecimalMaxDigit];
}
Instances can be initialized using the NSDecimalFromString(NSString *) function.
Chapter 8: Base Library 89
8.5.3 Formatters
Formatters are classes providing support for converting complex values into text strings.
They also provide some support for user editing of strings to be converted back into object
equivalents. All descend from NSFormatter, which defines basic methods for obtaining
either an attributed string or a regular string for an object value. Specific classes include
NSDateFormatter for NSDate objects, NSNumberFormatter for NSNumber objects. Instances
of these classes can be customized for specific display needs.
and save files, create/list directories, and move or delete files and directories. In addition
to simply listing directories, you may obtain an NSDirectoryEnumerator instance from it,
a subclass of NSEnumerator which provides a full listing of all the files beneath a directory
and its subdirectories.
If you need to work with path names but don’t need the full NSFileManager
capabilities, NSString provides a number of path-related methods, such as -
stringByAppendingPathComponent: and -lastPathComponent. You should use these
instead of working directly with path strings to support cross-platform portability.
NSFileHandle is a general purpose I/O class which supports reading and writing to both
files and network connections, including ordinary and encrypted (SSL) socket connections,
and the standard in / standard out streams familiar from Unix systems. You can obtain
instances through methods like +fileHandleForReadingAtPath:(NSString *)path and
+fileHandleAsServerAtAddress:(NSString *)address service:(NSString *)service
protocol:(NSString *)protocol. The network-related functions of NSFileHandle
(which are a GNUstep extension not included in Cocoa) will be covered in a later section.
Note this class also supports gzip compression for reading and writing.
Finally, GNUstep also provides some miscellaneous filesystem-related utility functions, in-
cluding NSTemporaryDirectory() and NSHomeDirectoryForUser().
Serialized property lists can actually be written in one of three different formats – plain text,
XML, and binary. Interconversion amongst these is possible using the pldes and plser
command-line tools (see the tools reference).
8.7.2 Archives
Archiving utilizes a binary format that is cross-platform between GNUstep implementations,
though not between GNUstep and Mac OS X Cocoa. Archiving, like serialization in Java,
is used both for saving/restoring objects on disk and for interprocess communications with
Chapter 7 [Distributed Objects], page 65. For an object to be archivable, it must adopt the
NSCoding protocol. The coding process itself is managed by instances of the NSCoder class
and its subclasses:
NSCoder Base class, defines most of the interface used by the others.
NSArchiver, NSUnarchiver
Sequential archives that can only be saved and restored en masse.
NSKeyedArchiver, NSKeyedUnarchiver
Random access archives that can be read from and written to on an individual-
object basis and provide more robust integrity in the face of class changes.
NSPortCoder
Used for Chapter 7 [Distributed Objects], page 65.
The basic approach to accessing or creating an archive is to use one of the convenience
methods in an NSCoder subclass:
+ (BOOL) archiveRootObject: (id)object toFile: (NSString *)file
Save object and graph below it to file. ’YES’ returned on success. Both
NSArchiver and NSKeyedArchiver support.
+ (NSData *) archivedDataWithRootObject: (id)object
Save object and graph below it to a byte buffer. Both NSArchiver and
NSKeyedArchiver suport.
+ (id) unarchiveObjectWithFile: (NSString *)file
Load object graph from file. Both NSUnarchiver and NSKeyedUnarchiver
support.
+ (id) unarchiveObjectWithData: (NSData *)data
Load object graph from byte buffer. Both NSUnarchiver and
NSKeyedUnarchiver support.
To obtain more specialized behavior, instantiate one of the classes above and customize
it (through various method calls) before instigating the primary archiving or unarchiving
operation.
From the perspective of the objects being archived, the NSCoding protocol declares two
methods that must be implemented:
-(void) encodeWithCoder: (NSCoder *)encoder
This message is sent by an NSCoder subclass or instance to request the object
to archive itself. The implementation should send messages to encoder to save
its essential instance variables. If this is impossible (for whatever reason) an
exception should be raised.
92 Objective-C GNUstep Base Programming Manual
// ...
@end
...
@implementation City
if (![coder allowsKeyedCoding])
{
[coder encodeValueOfObjCType: @encode(float) at: &latitude];
[coder encodeValueOfObjCType: @encode(float) at: &longitude];
[coder encodeObject: censusData];
[coder encodeConditionalObject: state];
}
else
{
[coder encodeFloat: latitude forKey: @"City.latitude"];
[coder encodeFloat: longitude forKey: @"City.longitude"];
[coder encodeObject: censusData forKey: @"City.censusData"];
[coder encodeConditionalObject: state forKey: @"City.state"];
}
return;
}
if (![coder allowsKeyedCoding])
{
// Must decode keys in same order as encodeWithCoder:
[coder decodeValueOfObjCType: @encode(float) at: &latitude];
[coder decodeValueOfObjCType: @encode(float) at: &longitude];
censusData = [[coder decodeObject] retain];
state = [[coder decodeObject] retain];
}
else
{
// Can decode keys in any order
censusData = [[coder decodeObjectForKey: @"City.censusData"] retain];
state = [[coder decodeObjectForKey: @"City.state"] retain];
latitude = [coder decodeFloatForKey: @"City.latitude"];
longitude = [coder decodeFloatForKey: @"City.longitude"];
}
return self;
}
// ...
@end
The primary wrinkle to notice here is the check to [coder allowsKeyedCoding]. The
object encodes and decodes its instance variables using keys if this returns ’YES’. Keys
must be unique within a single inheritance hierarchy – that is, a class may not use keys the
same as its superclass or any of its ancestors or sibling classes.
Keyed archiving provides robustness against class changes and is therefore to be preferred
in most cases. For example, if instance variables are added at some point to the City class
above, this will not prevent earlier versions of the class from restoring data from a later one
(they will just ignore the new values), nor does it prevent a later version from initializing
from an earlier archive (it will not find values for the added instance variables, but can set
these to defaults).
Note that within a given archive, an object will be written only once. Subsequent requests
to write the same object are detected and a reference is written rather than the full object.
94 Objective-C GNUstep Base Programming Manual
8.8 Utility
The GNUstep Base library provides a number of utility classes that don’t fall under any
other function category.
The NSUserDefaults class provides access to a number of system- and user-dependent set-
tings that should affect tool and application behavior. You obtain an instance through
sending [NSUserDefaults standardUserDefaults]. The instance provides access to set-
tings indexed by string keys. The standard keys used are documented here. Users can
adjust settings for particular keys using the defaults command.
The NSProcessInfo class provides access to certain information about the system envi-
ronment such as the operating system and host name. It also provides support for pro-
cess logging (see Chapter 6 [Logging], page 55). You obtain an instance through sending
[NSProcessInfo processInfo].
The NSUndoManager class provides a general mechanism for supporting undo of user opera-
tions in applications. Essentially, it allows you to store sequences of messages and receivers
that need to be invoked to undo or redo an action. The various methods in this class pro-
vide for grouping of sets of actions, execution of undo or redo actions, and tuning behavior
parameters such as the size of the undo stack. Each application entity with its own editing
history (e.g., a document) should have its own undo manager instance. Obtain an instance
through a simple [[NSUndoManager alloc] init] message.
The NSProtocolChecker and NSProxy classes provide message filtering and forwarding
capabilities. If you wish to ensure at runtime that a given object will only be sent messages
in a certain protocol, you create an NSProtocolChecker instance with the protocol and the
object as arguments:
id versatileObject = [[ClassWithManyMethods alloc] init];
id narrowObject = [NSProtocolChecker protocolCheckerWithTarget: versatileObject
protocol: @protocol(SomeSpecificProtocol)];
return narrowObject;
This is often used in conjunction with distributed objects to expose only a subset of an ob-
jects methods to remote processes. The NSProxy class is another class used in conjunction
with distributed objects. It implements no methods other than basic NSObject proto-
col methods. To use it, create a subclass overriding -(void) forwardInvocation: and -
(NSMethodSignature) methodForSelector:. By appropriate implementations here, you
can make an NSProxy subclass instance act like an instance of any class of your choosing.
The NSBundle class provides support for run-time dynamic loading of libraries and ap-
plication resources, usually termed “Bundles”. A bundle consists of a top-level directory
containing subdirectories that may include images, text files, and executable binaries or
shared libraries. The “.app” directory holding a NeXTstep/OpenStep/GNUstep/Cocoa
application is actually a bundle, as are the “Frameworks” containing shared libraries to-
gether with other resources. Bundles and frameworks are covered in Appendix B [Bundles
and Frameworks], page 105.
8.9 Notifications
GNUstep provides a framework for sending messages between objects within a process called
notifications. Objects register with an NSNotificationCenter to be informed whenever
Chapter 8: Base Library 95
other objects post NSNotifications to it matching certain criteria. The notification center
processes notifications synchronously – that is, control is only returned to the notification
poster once every recipient of the notification has received it and processed it. Asynchronous
processing is possible using an NSNotificationQueue. This returns immediately when a
notification is added to it, and it will periodically post the oldest notification on its list
to the notification center. In a multithreaded process, notifications are always sent on the
thread that they are posted from.
An NSNotification consists of a string name, an object, and optionally a dictionary which
may contain arbitrary associations. Objects register with the notification center to receive
notifications matching either a particular name, a particular object, or both. When an
object registers, it specifies a message selector on itself taking an NSNotification as its
sole argument. A message will be sent using this selector whenever the notification center
receives a matching notification in a post.
Obtain a notification center instance using NSNotificationCenter +defaultCenter. An
NSDistributedNotificationCenter may be used for interprocess communication on the
same machine. Interprocess notification will be slower than within-process notification, and
makes use of the gdnc command-line tool.
Notifications are similar in some ways to events in other frameworks, although they are
not used for user interface component connections as in Java (message forwarding and the
target-action paradigm are used instead). In addition, the GNUstep GUI (AppKit) library
defines an NSEvent type for representing mouse and keyboard actions.
NSURLHandle
This class provides additional control over the URL contents loading process.
Obtain an instance through NSURL -URLHandleUsingCache:.
general, there is one run loop per thread in an application, which may always be obtained
through the +currentRunLoop method, however unless you are using the AppKit and the
[NSApplication] class, the run loop will not be started unless you explicitly send it a -run
message.
At any given point, a run loop operates in a single mode, usually NSDefaultRunLoopMode.
Other modes are used for special purposes and you usually need not worry about them.
An NSTimer provides a way to send a message at some time in the future, possibly repeating
every time a fixed interval has passed. To use a timer, you can either create one that will au-
tomatically be added to the run loop in the current thread (using the -addTimer:forMode:
method), or you can create it without adding it then add it to a run loop of your choosing
later.
Because threads share data, there is the danger that examinations of and modifications
to data performed concurrently by more than one thread will occur in the wrong order
and produce unexpected results. (Operations with immutable objects do not present this
problem unless they are actually deallocated.) GNUstep provides the NSLocking protocol
and the NSLock class and subclasses to deal with this. NSLocking provides two methods: -
lock and -unlock. When an operation needs to be performed without interference, enclose
it inside of lock-unlock:
NSArray *myArray;
NSLock *myLock = [[NSLock alloc] init];
// ...
[myLock lock];
if (myArray == nil)
{
myAray = [[NSMutableArray alloc] init];
[myArray addObject: someObject];
}
[myLock unlock];
This code protects ’myArray’ from accidentally being initialized twice if two separate threads
happen to detect it is nil around the same time. When the lock method is called, the
thread doing so is said to acquire the lock. No other thread may subsequently acquire the
lock until this one has subsequently relinquished the lock, by calling unlock.
Note that the lock object should be initialized before any thread might possibly need it.
Thus, you should either do it before any additional threads are created in the application,
or you should enclose the lock creation inside of another, existing, lock.
The -lock method in the NSLocking protocol blocks indefinitely until the lock is acquired.
If you would prefer to just check whether the lock can be acquired without committing to
this, you can use NSLock -tryLock or NSLock -lockBeforeDate:, which return YES if they
succeed in acquiring the lock.
NSRecursiveLock is an NSLock subclass that may be locked multiple times by the same
thread. (The NSLock implementation will not allow this, causing the thread to deadlock
(freeze) when it attempts to acquire the lock a second time.) Each lock message must be
balanced by a corresponding unlock before the lock is relinquished.
NSConditionLock stores an int together with its lock status. The -
lockWhenCondition:(int)value and related methods request the lock only if
the condition is equal to that passed in. The condition may be changed using the
unlockWithCondition:(int)value method. This mechanism is useful for, e.g., a
producer-consumer situation, where the producer can “tell” the consumer that data is
available by setting the condition appropriately when it releases the lock it acquired for
adding data.
Finally, the NSDistributedLock class does not adopt the NSLocking protocol but supports
locking across processes, including processes on different machines, as long as they can
access a common filesystem.
If you are writing a class library and do not know whether it will be used in a
multithreaded environment or not, and would like to avoid locking overhead if not,
use the NSThread +isMultiThreaded method. You can also register to receive
Chapter 8: Base Library 99
// code in Slave...
+ newWithCommPorts: (NSArray *)ports
{
NSConnection *comms;
/**
* New point at 0,0.
*/
+ new;
// ...
/**
* Return point’s current X position.
*/
- (float) x;
// ...
@end
102 Objective-C GNUstep Base Programming Manual
When you are finished, invoke autogsdoc giving it the names of all your header files. (It
will find the implementation files automatically, as long as they have the same names;
alternatively, give it the names of the implementation files as well.) This will produce a set
of HTML files describing your classes. If you include the ’-MakeFrames YES’ argument, the
HTML will be structured into frames for easy navigation.
(Autogsdoc, like all GNUstep command line tools, is found in the ${GNUSTEP SYSTEM ROOT}/Tools
directory.)
You can also generate documentation automatically using the GNUstep make utility. Con-
sult its primary documentation for details. The short story is:
include $(GNUSTEP_MAKEFILES)/common.make
DOCUMENT_NAME = MyProject
include $(GNUSTEP_MAKEFILES)/documentation.make
Usually this is put into a separate makefile called “DocMakeFile” in the source directory.
A.2 Cross-Referencing
GSdoc provides the ability to reference entities both within the project and in external
projects. When writing GSdoc comments in source code, references are particularly easy to
create. To refer to an argument of the method or function you are documenting, just type
it normally; it will be presented in a special type face in the final documentation to show
it is an argument. To refer to another method within the same class you are documenting,
just type its selector with the + or - sign in front. This will be converted into a hyperlink
in output forms that support that. To refer to another class, you just type the class’s
name in [Brackets]. To refer to a method in another class, put the method selector after
the name, as in [Class-methodWithArg1:andArg2:] (do not include a space). To refer to a
protocol, use [(BracketsAndParentheses)] instead of just brackets. To refer to a category,
use [Class(Category)]. For methods in these two cases, put the method name outside the
parentheses. To refer to a function, simply type its name suffixed by parentheses().
C.1 General
• C programmers may learn Objective-C in hours (though real expertise obviously takes
much longer).
• Java has global market acceptance.
• Objective-C is a compiled OO programming language.
• Java is both compiled and interpreted and therefore does not offer the same run-time
performance as Objective-C.
• Objective-C features efficient, transparent Distributed Objects.
• Java features a less efficient and less transparent Remote Machine Interface.
• Objective-C has basic CORBA compatibility through official C bindings, and full com-
patibility through unofficial Objective-C bindings.
• Java has CORBA compatibility through official Java bindings.
• Objective-C is portable across heterogeneous networks by virtue of a near omnipresent
compiler (gcc).
• Java is portable across heterogeneous networks by using client-side JVMs that are
software processors or runtime environments.
C.2 Language
• Objective-C is a superset of the C programming language, and may be used to develop
non-OO and OO programs. Objective-C provides access to scalar types, structures and
to unions, whereas Java only addresses a small number of scalar types and everything
else is an object. Objective-C provides zero-cost access to existing software libraries
written in C, Java requires interfaces to be written and incurs runtime overheads.
• Objective-C is dynamically typed but also provides static typing. Java is statically
typed, but provides type-casting mechanisms to work around some of the limitations
of static typing.
• Java tools support a convention of a universal and distributed name-space for classes,
where classes may be downloaded from remote systems to clients. Objective-C has no
such conventions or tool support in place.
• Using Java, class definitions may not be extended or divided through the addition of
logical groupings. Objective-C provides categories as a solution to this problem.
• Objective-C provides delegation (the benefits of multiple inheritance without the draw-
backs) at minimal programming cost. Java requires purpose written methods for any
delegation implemented.
• Java provides garbage collection for memory management. Objective-C provides man-
ual memory management, reference counting, and garbage collection as options.
• Java provides interfaces, Objective-C provides protocols.
108 Objective-C GNUstep Base Programming Manual
C.6 Longevity
• Objective-C has been used for over ten years, and is considered to be in a stable and
proven state, with minor enhancements from time to time.
• Java is evolving constantly.
C.7 Databases
• Apple’s EOF tools enable Objective-C developers to build object models from existing
relational database tables. Changes in the database are automatically recognised, and
there is no requirement for SQL development.
• Java uses JDBC that requires SQL development; database changes affect the Java
code. This is considered inferior to EOF. Enterprise JavaBeans with container man-
aged persistence provides a limited database capability, however this comes with much
additional baggage. Other object-relational tools and APIs are being developed for
Java (ca. 2004), but it is unclear which of these, if any, will become a standard.
C.8 Memory
• For object allocation Java has a fixed heap whose maximum size is set when the JVM
starts and cannot be resized unless the JVM is restarted. This is considered to be a
Appendix C: Differences and Similarities Between Objective-C, Java, and C++ 109
disadvantage in certain scenarios: for example, data read from databases may cause
the JVM to run out of memory and to crash.
• Objective-C’s heap is managed by the OS and the runtime system. This can typically
grow to consume all system memory (unless per-process limits have been registered
with the OS).
NO_GNUSTEP [preprocessor]
GNUstep specific extensions to the OpenStep and MacOS cocoa APIs are excluded
from the headers if this is defined to a non-zero value.
STRICT_MACOS_X [preprocessor]
Only methods and classes that are part of the MacOS cocoa API are made available
in the headers if this is defined.
STRICT_OPENSTEP [preprocessor]
Only methods and classes that are part of the OpenStep specification are made avail-
able in the headers if this is defined.
Note, these preprocessor constants are used in developer code (ie the code that users of
GNUstep write) rather than by the GNUstep software itself. They permit a developer
to ensure that he/she does not write code which depends upon API not present on other
implementations (in practice, MacOS-X or some old OPENSTEP systems). The actual
GNUstep libraries are always built with the full GNUstep API in place, so that the feature
set is as consistent as possible.
GNU-Debug [defaults]
An array of strings that lists debug levels to be used within the program. These
debug levels are merged with any which were set on the command line or added
programmatically to the set given by the [NSProcessInfo-debugSet] method.
GSLogSyslog [defaults]
Setting the user default GSLogSyslog to YES will cause log/debug output to be sent
to the syslog facility (on systems which support it), rather than to the standard error
stream. This is useful in environments where stderr has been re-used strangely for
some reason.
114 Objective-C GNUstep Base Programming Manual
GSMacOSXCompatible [defaults]
Setting the user default GSMacOSXCompatible to YES will cause MacOS compatible
behavior to be the default at runtime. This default may however be overridden to
provide more fine grained control of system behavior.
GSOldStyleGeometry [defaults]
Specifies whether the functions for producing strings describing geometric structures
(NSStringFromPoint(), NSStringFromSize(), and NSStringFromRect()) should pro-
duce strings conforming to the OpenStep specification or to MacOS-X behavior. The
functions for parsing those strings should cope with both cases anyway.
GSSOCKS [defaults]
May be used to specify a default SOCKS5 server (and optionally a port separated
from the server by a colon) to which tcp/ip connections made using the NSFileHandle
extension methods should be directed.
This default overrides the SOCKS5 SERVER and SOCKS SERVER environment
variables.
NSWriteOldStylePropertyLists [defaults]
Specifies whether text property-list output should be in the default MacOS-X format
(XML), or in the more human readable (but less powerful) original OpenStep format.
Reading of property lists is supported in either format, but only if GNUstep is built
with the libxml library (which is needed to handle XML parsing).
NB. MacOS-X generates illegal XML for some strings - those which contain characters
not legal in XML. GNUstep always generates legal XML, at the cost of a certain
degree of compatibility. GNUstep XML property lists use a backslash to escape
illegal chatracters, and consequently any string containing either a backslash or an
illegal character will be written differently to the same string on MacOS-X.
NSLanguages [defaults]
An array of strings that lists the users prefered languages, in order or preference. If
not found the default is just English.
Appendix F: Using the GNUstep Make Package 115
.SUFFIXES: .o .m
.m.o:
$(CC) -c $(CFLAGS) $<
# Macro declarations
CC = gcc
CFLAGS = -g
LIBS = -lobjc
SRC=main.m Truck.m Station.m Vehicle.m
OBJ=main.o Truck.o Station.o Vehicle.o
116 Objective-C GNUstep Base Programming Manual
# Explicit rules
hist: $(OBJ)
$(CC) $(CFLAGS) -o main $(OBJ) $(LIBS)
# Implicit rules
-include GNUmakefile.preamble
-include GNUmakefile.postamble
To compile a package that uses the Makefile Package, type make in the top-level directory
of the package. A non-GNUstep Objective-C file may be compiled by adding -lobjc on at
the command line.
Appendix F: Using the GNUstep Make Package 117
• Bundles - bundle.make
A bundle is a collection of resources and code that can be used to enhance an existing
application or tool dynamically using the NSBundle class from the GNUstep base
library.
• Command Line C Tools - ctool.make
A ctool is a project that uses only C language files. Otherwise it is similar to the ObjC
project type.
• Documentation - documentation.make
The Documentation project provides rules to use various types of documentation such
as texi and LaTeX documentation, and convert them into finished documentation like
info, PostScript, HTML, etc.
• Frameworks - framework.make
A Framework is a collection of resources and a library that provides common code that
can be linked into a Tool or Application. In many respects it is similar to a Bundle.
• Java - java.make
This project provides rules for building java programs. It also makes it easy to make
java projects that interact with the GNUstep libraries.
• Libraries - library.make
The Makefile Package provides a project type for building libraries that may be static,
shared, or dynamic link libraries (DLLs). The latter two variants are supported only
on some platforms.
Concept Index 119
Concept Index
A forwarding. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
abstract class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 frameworks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
advanced messaging . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
allocating objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 G
AppKit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
assertion facilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 game server example . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
assertion handling, compared with Java . . . . . . . . 62 garbage collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
assertions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 gdomap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
GNUstep base library . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
GNUstep Make package . . . . . . . . . . . . . . . . . . . . . . 115
B GNUstep make utility . . . . . . . . . . . . . . . . . . . . . . . . . . 7
GNUstep, what is? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
base library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
graphical programming . . . . . . . . . . . . . . . . . . . . . . . . . 7
basic OO terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
gsdoc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
GUI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
bycopy and byref type qualifiers . . . . . . . . . . . . . . . 81
H
C history of NeXTstep . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
categories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 history of Objective-C . . . . . . . . . . . . . . . . . . . . . . . . . . 5
class cluster . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 history of OpenStep . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
class, abstract . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
class, root . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 I
client/server processes . . . . . . . . . . . . . . . . . . . . . . . . . 65
in, out, and inout type qualifiers . . . . . . . . . . . . . . . 81
cluster, classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
compilation, conditional . . . . . . . . . . . . . . . . . . . . . . 113
inheriting methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
instance variables, referring to . . . . . . . . . . . . . . . . . 17
interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
D
differences and similarities, Objective-C and C++
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 J
differences and similarities, Objective-C and Java Java and Guile, programming GNUstep . . . . . . . 111
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
directory layout. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
distributed objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 L
Distributed Objects Name Server, GNUstep . . . . 71 layout, filesystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
distributed objects, client code . . . . . . . . . . . . . 66, 67 logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
distributed objects, error checking . . . . . . . . . . . . . 84 logging facilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
distributed objects, example (no error checking) logging, compared with Java . . . . . . . . . . . . . . . . . . . 62
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
distributed objects, using a protocol . . . . . . . . . . . 69
M
E Make package, GNUstep . . . . . . . . . . . . . . . . . . . . . . 115
make utility, GNUstep . . . . . . . . . . . . . . . . . . . . . . . . . . 7
error checking, distributed objects . . . . . . . . . . . . . 84 memory deallocation. . . . . . . . . . . . . . . . . . . . . . . . . . . 25
exception facilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 memory management . . . . . . . . . . . . . . . . . . . . . . . . . . 25
exception handling, compared with Java . . . . . . . 62 memory management, explicit . . . . . . . . . . . . . . . . . 25
exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 memory management, garbage collection based
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
memory management, OpenStep-style . . . . . . . . . . 26
F memory management, retain count . . . . . . . . . . . . . 26
filesystem layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 message forwarding, distributed objects . . . . . . . . 83
forward invocation, distributed objects . . . . . . . . . 83 messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
120 Objective-C GNUstep Base Programming Manual
O U
object interaction, remote objects . . . . . . . . . . . . . . 65 user defaults, API compliance . . . . . . . . . . . . . . . . 113
object-oriented programming. . . . . . . . . . . . . . . . . . . . 3
Objective-C and C++, differences and similarities
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 W
Objective-C and Java, differences and similarities what is GNUstep? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 what is Objective-C? . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Objective-C, history . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 working with objects . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Objective-C, what is? . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 writing new classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
objects, initalizing and allocating . . . . . . . . . . . . . . 23
objects, working with . . . . . . . . . . . . . . . . . . . . . . . . . . 23 Y
oneway, type qualifier . . . . . . . . . . . . . . . . . . . . . . . . . . 81
OpenStep compliance . . . . . . . . . . . . . . . . . . . . . . . . . 113 your first Objective-C program . . . . . . . . . . . . . . . . . . 8
OpenStep, history . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
OS X compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
out, type qualifier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Z
overriding methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Zones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24