[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8. Base Library

The GNUstep Base library is an implementation of the OpenStep Foundation, a nongraphical 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 "Foundation" - 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 library; 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 technology 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, appropriately 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 GNUstep Compliance to Standards.

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 according 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.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.1 Copying, Comparing, Hashing Objects

Often in object-oriented code you need to make a duplicate copy of an existing object. The NSObject method -(id) copy provides a standard means of acquiring a copy of the object. The depth of the copy is not defined. That is, if an object has instance variables or other references to other objects, they may either themselves be copied or just the references to them will be copied. The root class NSObject does not implement the copy method 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 protocol 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 comparing 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 reason.

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.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.2 Object Containers

GNUstep defines three major utility classes for holding collections of other objects. NSArray is an ordered collection of objects, each of which may occur in the collection multiple times. NSSet is an unordered collection of unique objects (according to isEqual and/or hash). NSDictionary is an unordered collection of key-value pairs. The keys form a set (and must be unique), however there are no restrictions on the collection of values. The -hash and -isEqual NSObject methods discussed above are used by collection instances to organize their members. All collections retain their members (see Working with Objects).

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 enumerator 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.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.3 Data and Number Containers

The data containers discussed in the previous section, with the exception of NSHashTable and NSMapTable, can store objects, but not primitive types such as ints or floats. The NS...Table structures are not always appropriate for a given task. For this case, GNUstep offers two alternatives.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

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 of the memory management for the buffer, growing it as needed and freeing it when they are released.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

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.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

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.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.3.4 NSRange, NSPoint, NSSize, NSRect

There are also a few types (not classes) for representing common composite structures. NSRange represents an integer interval. NSPoint represents a floating point 2-d cartesian location. NSSize represents a 2-d floating point extent (width and height). NSRect contains a lower-left point and an extent. A number of utility functions are defined for handling rectangles and points.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.4 Date/Time Facilities

GNUstep contains the NSDate class and the NSCalendarDate classes for representing and handling dates and times. NSDate has methods just relating to times and time differences in the abstract, but not calendar dates or time zones. These features are added in the NSCalendarDate subclass. The NSTimeZone class handles time zone information.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.5 String Manipulation and Text Processing

Basic string handling in the GNUstep Base library was covered in Strings in GNUstep. Here, we introduce a number of additional string and text processing facilities provided by GNUstep.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.5.1 NSScanner and Character Sets

The NSScanner class can be thought of as providing a combination of the capabilities of the C sscanf() function and the Java StringTokenizer class. It supports parsing of NSStrings and extraction of numeric values or substrings separated by delimiters.

NSScanner works with objects of a class NSCharacterSet and its subclasses NSMutableCharacterSet, NSBitmapCharSet, and NSMutableBitmapCharSet, which provide various means of representing sets of unicode characters.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.5.2 Attributed Strings

Attributed strings are strings that support the association of attributes with ranges of characters within the string. Attributes are name-value pairs represented by an NSDictionary and may include standard attributes (used by GNUstep GUI classes for font and other characteristics during rendering) as well as programmer-defined application specific attributes. The classes NSAttributedString and NSMutableAttributedString represent attributed strings. They are not subclasses of NSString, though they bundle an instance of one.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

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.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.6 File Handling

A number of convenience facilities are provided for platform-independent access to the file system. The most generally useful is the NSFileManager class, which allows you to read 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, including NSTemporaryDirectory() and NSHomeDirectoryForUser().


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.7 Persistence and Serialization

GNUstep provides robust facilities for persisting objects to disk or sending them over a network connection (to implement Distributed Objects). One class of facilities is referred to as property list serialization, and is only usually used for NSDictionary and NSArray container objects, and NSNumber, NSData, NSString, and NSDate member objects. It utilizes primarily text-based formats.

Saving to and loading back from a serialized property list representation will preserve values but not necessarily the classes of the objects. This makes property list representations robust across platforms and library changes, but also makes it unsuitable for certain applications. Archiving, the second class of GNUstep persistence facilities, provides for the persistence of a graph of arbitrary objects, with references to one another, taking care to only persist each individual object one time no matter how often it is referred to. Object class identities are preserved, so that the behavior of a reloaded object graph is guaranteed to be the same as the saved one. On the other hand, the classes for these objects must be available at load time.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.7.1 Property List Serialization

Serialized property list representations (sometimes referred to as "plists") are typically saved and restored using methods in collection classes. For example the NSDictionary class has -writeToFile:atomically: to save, +dictionaryWithContentsOfFile to restore, and NSArray has similar methods. Alternatively, if you wish to save/restore individual NSData or other objects, you can use the NSPropertyListSerialization class. (There are also NSSerializer and NSDeserializer classes, but these are deprecated in Mac OS X and are not really needed in GNUstep either, so should not be used.)

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).


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

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 Distributed Objects. 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 Distributed Objects.

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.

-(id) initWithCoder: (NSCoder *)decoder

This message is sent by an NSCoder subclass or instance to request the object to restore itself from an archive. The implementation should send messages to decoder to load its essential instance variables. An exception should be raised if there is a problem restoring state.

Here is an example NSCoding implementation:

 
@interface City : PoliticalUnit
{
private
float      latitude;
float      longitude;
CensusData *censusData;
State      *state;
}

  // ...
@end

 ...

@implementation City

- (void) encodeWithCoder: (NSCoder *)coder
{
  [super encodeWithCoder:coder];  // always call super first

  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;
}

- (id) initWithCoder: (NSCoder *)coder
{
  self = [super initWithCoder:coder];  // always assign 'self' to super init..

  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).

Finally, notice the use of encodeConditionalObject above for state, in contrast to encodeObject for census data. The reason the two different methods are used is that the City object owns its census data, which is an integral part of its structure, whereas the state is an auxiliary reference neither owned nor retained by City. It should be possible to store the cities without storing the states. Thus, the encodeConditionalObject method is called, which only stores the State if it is already being stored unconditionally elsewhere during the current encoding operation.

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.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

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 settings that should affect tool and application behavior. You obtain an instance through sending [NSUserDefaults standardUserDefaults]. The instance provides access to settings 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 environment such as the operating system and host name. It also provides support for process logging (see Logging). You obtain an instance through sending [NSProcessInfo processInfo].

The NSUndoManager class provides a general mechanism for supporting undo of user operations 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 provide 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 objects methods to remote processes. The NSProxy class is another class used in conjunction with distributed objects. It implements no methods other than basic NSObject protocol 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 application 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 together with other resources. Bundles and frameworks are covered in Application Resources: Bundles and Frameworks.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

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 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.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.10 Networking and RPC

GNUstep provides some general network-related functionality, as well as classes supporting distributed objects and related forms of inter-process communication.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.10.1 Basic Networking

GNUstep provides the following classes handling basic network communications:

NSHost

Holds and manages information on host names and IP addresses. Use the +currentHost, +hostWithName:, or +hostWithAddress: class methods to obtain an instance.

NSFileHandle

On Unix, network connections are treated analogously to files. This abstraction has proven very useful, so GNUstep supports it in the NSFileHandle class, even on non-Unix platforms. You may use the class methods +fileHandleAsServerAtAddress:(NSString *)address service:(NSString *)service protocol:(NSString *)protocol and corresponding client methods to access one side of a connection to a port on a networked machine. (To use pipes, see the next section.)

NSURL

Provides methods for working with URLs and the data accessible through them. Once an NSURL is constructed, data may be loaded asynchronously through NSURL -loadResourceDataNotifyingClient:usingCache: or synchronously through NSURL -resourceDataUsingCache:. It can also be obtained through NSString +stringWithContentsOfURL: or NSData +dataWithContentsOfURL:.

NSURLHandle

This class provides additional control over the URL contents loading process. Obtain an instance through NSURL -URLHandleUsingCache:.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.10.2 Remote Process Communications

GNUstep provides a number of classes supporting distributed objects and related forms of inter-process communication. In most cases, you only need to know about the NSConnection class, but if you require additional control over distributed objects, or if you wish to use alternative forms of communications such as simple messaging, you may use the classes listed here.

NSConnection

This is the primary class used for registering and acquiring references to distributed objects.

NSDistantObject

When a client acquires a remote object reference through NSConnection +rootProxyForConnectionWithRegisteredName:, the returned object is an instance of this class, which is a subclass of NSProxy. Since usually you will just cast your reference to this to a particular protocol, you do not need to refer to the NSDistantObject class directly.

NSPort, NSPortMessage

Behind the scenes in distributed objects, NSPort objects handle both network communications and serialization/deserialization for sending messages to remote objects and receiving the results. The actual data sent over the network is encapsulated by NSPortMessage objects, which consist of two ports (sender and receiver) and a body consisting of one or more NSData or NSPort objects. (Data in the NSData must be in network byte order.)

NSSocketPort, NSMessagePort

If you want to send custom messages between processes yourself, you can use these classes. NSSocketPort can communicate to processes on the same or remote machines. NSMessagePort is optimized for local communications only.

NSPortNameServer, NSSocketPortNameServer, NSMessagePortNameServer

The NSPortNameServer class and subclasses are used behind the scenes by the distributed objects system to register and look up remotely-accessible objects.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.11 Threads and Run Control

A GNUstep program may initiate independent processing in two ways - it can start up a separate process, referred to as a task, much like a fork in Unix, or it may spawn multiple threads within a single process. Threads share data, tasks do not. Before discussing tasks and threads, we first describe the run loop in GNUstep programs.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.11.1 Run Loops and Timers

NSRunLoop instances handle various utility tasks that must be performed repetitively in an application, such as processing input events, listening for distributed objects communications, firing NSTimers, and sending notifications and other messages asynchronously. In 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 automatically 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.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.11.2 Tasks and Pipes

You can run another program as a subprocess using an NSTask instance, and communicate with it using NSPipe instances. The following code illustrates.

 
NSTask *task = [[NSTask alloc] init];
NSPipe *pipe = [NSPipe pipe];
NSFileHandle *readHandle = [pipe fileHandleForReading];
NSData *inData = nil;

[task setStandardOutput: pipe]; 
[task setLaunchPath: [NSHomeDirectory()
                         stringByAppendingPathComponent:@"bin/someBinary"]];
[task launch];

while ((inData = [readHandle availableData]) && [inData length])
  {
    [self processData:inData];
  }
[task release];

Here, we just assume the task has exited when it has finished sending output. If this might not be the case, you can register an observer for Notifications named NSTaskDidTerminateNotification.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.11.3 Threads and Locks

Threads provide a way for applications to execute multiple tasks in parallel. Unlike separate processes, all threads of a program share the same memory space, and therefore may access the same objects and variables.

GNUstep supports multithreaded applications in a convenient manner through the NSThread and NSLock classes and subclasses. NSThread +detachNewThreadSelector:toTarget:withObject: allows you to initiate a new thread and cause a message to be sent to an object on that thread. The thread can either run in a "one-shot" manner or it can sit in loop mode (starting up its own instance of the NSRunLoop class) and communicate with other threads using part of the distributed objects framework. Each thread has a dictionary (accessed through -threadDictionary that allows for storage of thread-local variables.

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 NSWillBecomeMultiThreadedNotifications. You can also use the GSLazyLock and GSLazyRecursiveLock classes (see next section) which handle this automatically.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.11.4 Using NSConnection to Communicate Between Threads

You can use the distributed objects framework to communicate between threads. This can help avoid creating threads repeatedly for the same operation, for example. While you can go through the full process of registering a server object by name as described in distributed objects, a lighter weight approach is to create NSConnections manually:

 
  // code in Master...
- startSlave
{
  NSPort *master;
  NSPort *slave;
  NSArray *ports;
  NSConnection *comms;

  master = [NSPort port];
  slave  = [NSPort port];

  comms = [[NSConnection alloc] initWithReceivePort: master sendPort: slave];
  [comms setRootObject: self];

  portArray = [NSArray arrayWithObjects: slave, master, nil];

  [NSThread detachNewThreadSelector: @selector(newWithCommPorts:)
                           toTarget: [Slave class]
                         withObject: ports];
}

  // code in Slave...
+ newWithCommPorts: (NSArray *)ports
{
  NSConnection *comms;

  NSPort *slave  = [ports objectAtIndex: 0];
  NSPort *master = [ports objectAtIndex: 1];

  comms = [NSConnection connectionWithReceivePort: slave sendPort: master];

  // create instance and assign to 'self'
  self = [[self alloc] init];

  [(id)[comms rootProxy] setServer: self];
  [self release];

  [[NSRunLoop currentRunLoop] run];
}


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.12 GNUstep Additions

The Base Additions library consists of a number of classes, all beginning with 'GC' or 'GS', that are not specified in OpenStep or Cocoa but have been deemed to be of general utility by the GNUstep developers. The library is designed so that it can be built and installed on a system, such as OS X, where GNUstep is not available but an alternate Foundation implementation is.

It contains the following five categories of classes:

GCxxx

These are classes that are substituted (behind the scenes) for certain Foundation classes if the Base library is compiled with garbage collection enabled. (See Memory Management.)

GSXMLxxx

Classes for parsing XML using DOM- or SAX-like APIs, for processing XPath expressions, and for performing XSLT transforms. These are implemented over the libxml2 C library, and using them should for the most part protect you from the frequent API changes in that library.

GSHtmlXxx

Classes for parsing HTML documents (not necessarily XHTML).

GSMimeXxx

Classes for handling MIME messages or HTML POST documents.

GSLazyXxx

Classes implementing "lazy" locking that do not actually attempt any locking unless running in a multithreaded application. See Threads and Run Control.

All of these classes have excellent API reference documentation and you should look there for further information.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Richard Frith-Macdonald on July, 26 2013 using texi2html 1.76.