File Handle

NSFileHandle objects provide an object-oriented wrapper for accessing open files or communications channels.

Overview

An NSFileHandle is an object that represents an open file or communications channel. It enables programs to read data from or write data to the represented file or channel. You can use other Cocoa methods for reading from and writing to files—NSFileManager’s contentsAtPath: and NSData’s writeToFile:options:error: are but a couple of examples. Why would you use NSFileHandle then? What are its advantages?

Instances of NSPipe, a class closely related to NSFileHandle, represent pipes: unidirectional interprocess communication channels. See the NSPipe reference for details.

Creating a File Handle Object

NSFileHandle is a class clusters, file handle objects are not actual instances of the NSFileHandle class but of one of its private subclasses. Although a file handle object’s class is private, its interface is public, as declared by the abstract superclass NSFileHandle.

Generally, you instantiate a file handle object by sending one of the fileHandle... messages to the NSFileHandle class object. These methods return a file handle object pointing to the appropriate file or communications channel. As a convenience, NSFileHandle provides class methods that create objects representing files and devices in the file system and that return objects representing the standard input, standard output, and standard error devices. You can also create file handle objects from file descriptors (such as found on BSD systems) using the initWithFileDescriptor: and initWithFileDescriptor:closeOnDealloc: methods. If you create file handle objects with these methods, you “own” the represented descriptor and are responsible for removing it from system tables, usually by sending the file handle object a closeFile message.

Background Inter-Process Communication Using Sockets

Sockets are full-duplex communication channels between processes either local to the same host machine or where one process is on a remote host. Unlike pipes, in which data goes in one direction only, sockets allow processes both to send and receive data. NSFileHandle facilitates communication over stream-type sockets by providing mechanisms run in background threads that accept socket connections and read from sockets.

NSFileHandle currently handles only communication through stream-type sockets. If you want to use datagrams or other types of sockets, you must create and manage the connection using native system routines.

The process on one end of the communication channel (the server) starts by creating and preparing a socket using system routines. These routines vary slightly between BSD and non-BSD systems, but consist of the same sequence of steps:

  1. Create a stream-type socket of a certain protocol.

  2. Bind a name to the socket.

  3. Adding itself as an observer of NSFileHandleConnectionAcceptedNotification.

  4. Sending acceptConnectionInBackgroundAndNotify to this file handle object. This method accepts the connection in the background, creates a new NSFileHandle object from the new socket descriptor, and posts an NSFileHandleConnectionAcceptedNotification.

In a method implemented to respond to this notification, the server extracts the NSFileHandle object representing the “near” socket of the connection from the notification’s userInfo dictionary; it uses the NSFileHandleNotificationFileHandleItem key to do this.

Typically the other process (the client) then locates the named socket created by the first process. Instead of accepting a connection to the socket by calling the appropriate system routine, the client creates an NSFileHandle object using the socket identifier as argument to initWithFileDescriptor:.

The client can now send data to the other process over the communications channel by sending writeData: to the NSFileHandle instance. (Note that writeData: can block.) The client can also read data directly from the NSFileHandle, but this would cause the process to block until the socket connection was closed, so it is usually better to read in the background. To do this, the process must:

  1. Add itself as an observer of NSFileHandleReadCompletionNotification or NSFileHandleReadToEndOfFileCompletionNotification.

  2. Send readInBackgroundAndNotify or readToEndOfFileInBackgroundAndNotify to this NSFileHandle object. The former method sends a notification after each transmission of data; the latter method accumulates data and sends a notification only after the sending process shuts down its end of the connection.

  3. In a method implemented to respond to either of these notifications, the process extracts the transmitted or accumulated data from the notification’s userInfo dictionary by using the NSFileHandleNotificationDataItem key.

  4. If you wish to keep getting notified you’ll need to again call readInBackgroundAndNotify in your observer method.

You close the communications channel in both directions by sending closeFile to the NSFileHandle object; either process can partially or totally close communication across the socket connection with a system-specific shutdown command.