Advanced Mac OS X Programming: The Big Nerd Ranch Guide

Mark Dalrymple

Copyright © 2011 Big Nerd Ranch, Inc.

All rights reserved. Printed in the United States of America. This publication is protected by copyright, and permission must be obtained from the publisher prior to any prohibited reproduction, storage in a retrieval system, or transmission in any form or by any means, electronic, mechanical, photocopying, recoring, or likewise. For information regarding permissions, contact

Big Nerd Ranch, Inc.
154 Krog Street
Suite 100
Atlanta, GA 30307
(404) 478-9005
http://www.bignerdranch.com/
book-comments@bignerdranch.com

The 10-gallon hat with propeller logo is a trademark of Big Nerd Ranch, Inc.

Exclusive worldwide distribution of the English edition of this book by

Pearson Technology Group
800 East 96th Street
Indianapolis, IN 46240 USA
http://www.informit.com

The authors and publisher have taken care in writing and printing this book but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein.

Aperture, Apple, AppleShare, Aqua, Bonjour, Carbon, Cocoa, Cocoa Touch, Final Cut Pro, Finder, iChat, Instruments, Interface Builder, iOS, iTunes, Keychain, Leopard, Mac, Mac OS, MacBook, Objective-C, Quartz, QuickTime, Rosetta, Snow Leopard, Spotlight, Tiger, Time Machine, and Xcode are trademarks of Apple, Inc., registered in the U.S. and other countries.

Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and the publisher was aware of a trademark claim, the designations have been printed with initial capital letters or in all capitals.

ISBN-13  9780321706546

ISBN-10  0321706544

Dedication

 For Zoe. May she grow up to be as geeky as her Weird Uncle Bork.

Acknowledgments

This book is based upon the experiences teaching a five-day class at The Big Nerd Ranch called Advanced Mac OS X Bootcamp. The patience and curiousity of my students has made this a more complete and comprehensible introduction to the plumbing that makes Mac OS X a reliable, flexible, and high-performance system.

Special thanks go to Jeremy Sherman. Jeremy stepped up and taught one of the Advanced Bootcamps when I was unable to so. Along the way he made numerous improvements to this book and associated course materials, helping to modernize and robusticize the code. Jeremy is also responsible for the excellent under-the-hoods look at Blocks.

Incredible thanks go to Aaron Hillegass, my co-author on the first two editions of this book. Many moons ago Aaron took a chance on this random guy sitting in the back of a Cocoa class in Asheville. Over the years I've learned from Aaron more than I imagined possible about writing, teaching, and treating others well.

Susan Loper, the tireless editor, performed acts of magic on the purple prose she was given.

In preparing this book, many people reviewed the drafts and brought errors to our attention. The most astonishing quantity of corrections over the years came from Bill Monk. It would be difficult to overstate Bill's contributions. Other technical reviewers who submitted errors or offered suggestions: John Vink, Juan Pablo Claude, Carl-Johan Kihlborn, Mike Morton, Ajeya Vempati, Eric Peyton, Chris Ridd, Michael Simmons, David Southwick, Jeremy Wyld, Richard Wolf, Tom Van Lenten, Dave Zarzycki, James Spencer, Greg Miller, Anne Halsall, Roy Lovejoy, Jonathan Saggau, Jim Magee, and Rob Rix. They made this book better with their useful corrections and suggestions. Any errors that remain in this book are completely my fault.

I would also like to thank my wife, my “Spousal Overunit” Sharlotte DeVere, for her support, patience and understanding.

Foreword

In 1989, the band Living Colour released the song Glamour Boy which explained that the world was filled with glamour boys who valued appearance over other, deeper properties. And the song proclaims “I ain't no glamour boy.”

Many programmers are glamour boys. They lack a deep understanding of what is happening under the surface, so they are pleased simply to ship an application that the client doesn't hate. If they had a better comprehension of how the operating system does its work, their code would be faster, more reliable, and more secure.

If you are a Mac OS X or iOS programmer, the knowledge that separates really good coders from glamour boys is in this book. It demystifies the plumbing of Mac OS X. It helps you interpret mysterious messages from the compiler and debugger. When you finish reading this book, you will be a better programmer than when you started.

I met Mark Dalrymple shortly after Mac OS X shipped. I was astonished by his deep knowledge of Mac OS X's Unix core and how Apple had extended and enhanced it. I begged him to write the first edition of this book. The result was even better than I had hoped for.

If you read this third edition, you will master:

Blocks | File I/O and metadata
Grand Central Dispatch | Forking off tasks
Multithreading | File system events
Operation queues | Keychain access
Network programming | Performance tuning
Bonjour | Instruments and DTrace
The run loop | Memory and the garbage collector

Don't be a glamour boy.

Aaron Hillegass

Big Nerd Ranch

July 1, 2011

Introduction

Mac OS X: Built to Evolve

Complex systems come into existence in only two ways: through careful planning or through evolution. An airport is an example of something that is planned carefully beforehand, built, and then undergoes only minor changes for the rest of its existence. Complex organisms (like humans) are an example of something that has evolved continually from something simple. In the end, organisms that are well suited to evolution will always win out over organisms that are less suited to evolve.

An operating system evolves. Of course, the programmer who creates a new operating system designs it carefully, but in the end, an operating system that is well suited to evolution will replace an operating system that is not. It is, then, an interesting exercise to think about what traits make an operating system capable of evolution.

The first version of Unix was developed by Ken Thompson at Bell Laboratories in 1969. It was written in assembly language to run on a PDP-7. Dennis Ritchie, also at Bell Labs, invented the C programming language. Among computer languages, C is pretty low level, but it is still much more portable than assembly language. Together, Thompson and Ritchie completely rewrote Unix in C. By 1978, Unix was running on several different architectures. Portability, then, was the first indication that Unix is well suited to evolution.

In 1976, Bell Labs began giving the source code for Unix to research facilities. The Computer Systems Research Group at UC Berkeley got a copy and began tinkering with it. The design of Unix was exceedingly elegant and a perfect platform upon which to build two important technologies: virtual memory and TCP/IP networking. By freely distributing the source code, Bell Labs was inviting people to extend Unix. Extensibility was the second indication that Unix is well suited to evolution.

4.4BSD was the last release of Unix produced by Berkeley. It was used as the basis for FreeBSD, OpenBSD, NetBSD, and Mac OS X. Today, Unix is used as an operating system for cellular phones and supercomputers. It is the most popular operating system for web servers, mail servers, and engineering workstations. The manner in which it has found a home in so many niches is yet another indication that Unix is capable of evolving.

Mac OS X is based upon a hybrid of Mach and 4.4BSD, but notice that this new niche, a desktop operating system that your grandmother will love as well as a mobile device operating system that your kids will love, is very different from Unix’s previous purposes. To reach this goal, Apple has made several important additions to its Unix core.

The Unix part of Mac OS X is called Darwin. The large additions to Darwin that Apple has made are known as the core technologies. Apple, recognizing that Unix must continue to evolve, has released the source code to Darwin and most of the core technologies.

This Book

My thought is that things shouldn’t be “magic” in your field of expertise. I don’t expect someone to implement a compiler or operating system, but they shouldn’t be mystical black boxes.

Paul Kim, Chief Noodler, Noodlesoft

I didn’t realize until after my friend Paul uttered the above statement on IRC one day that one of the goals of this book, and the Big Nerd Ranch Advanced Mac OS X Bootcamp in general, is to help demystify the fundamental Mac OS X technologies. A programmer can live a long time solely in the Cocoa Layer, only to be faced with inscrutable bugs when dealing with NSTask or network communication. I believe that knowledge of the lower levels of the OS will help you use the higher levels more effectively. That’s what I hope to accomplish here by showing you how the lower-level Unix APIs work and then showing you how the higher-level technology uses them.

This book intends to bridge the gap between higher-level books dedicated to the graphical aspects of Mac and iOS programming and the low-level kernel internal tomes. Some of the Unix API you may have seen in a college-level operating systems course, but I’ve stripped away some of the historical and no longer relevant details. For instance, you won’t see anything about managing pseudo-ttys.

When you finish this book, you will be able to:

Create applications that leverage the full power of the Unix APIs.

Use advanced ideas like multithreading and task queues to increase the performance of your applications.

Add networking capabilities to event-driven applications.

Make networked applications Bonjour-aware.

Use the keychain and authorization capabilities of the security framework.

Understand and use gcc, the linker, the debugger, Instruments, and the occasional Xcode dark corner.

Use performance tools to evaluate and improve the responsiveness of your existing applications.

The ideas in this book can be broken into three basic groups:

Unix APIs

There is a set of standard Unix APIs that every programmer should know how to use. Even if higher-level abstractions alleviate the need to ever call them directly, understanding these functions and structures will give you a much deeper knowledge of how your system works. Much of what is said here will also be true for Linux.

Framework APIs | Apple has added a whole set of daemons and frameworks to its version of Unix. These frameworks are exceedingly powerful, and sometimes Apple has been slow to document how they work and how they are to be used.
Tools | The Mac OS X developer tools suite, in addition to the system’s built-in utilities, provides a veritable toy store for developers. This book covers lower-lever Unix-heritage tools such as gcc, gdb, the linker, and DTrace, as well as higher-level tools such as Instruments.

The majority of the code in this book is ANSI C. Occasionally some C99 features will be used. Some of the chapters use the Cocoa APIs, so you should have a basic understanding of Cocoa and Objective-C. You can gain the necessary expertise by reading the first nine chapters of Aaron Hillegass’ Cocoa Programming for Mac OS X. iOS developers can take advantage of many OS features as well. I have sought to point out differences between Mac OS X and iOS where they exist.

Typographical Conventions

To make the book easier to comprehend, we have used several typographical conventions.

Function names will appear in bold and in a slightly smaller size than the default font size. All standard Unix functions are completely lowercase. Functions developed by Apple are often mixed case. To make it clear that it is a function, the name will be followed by a set of parentheses. For example, you might see, “Use NSLog() or printf() to display the computed value.”

In Objective-C, class names are always capitalized. In this book, they will also appear in bold and in a smaller size. In Objective-C, method names start with a lowercase letter. Method names will also appear in a bold, fixed-width font. So, for example, you might see, “The class NSObject has the method -dealloc.”

Other literals that you would see in code will appear in a smaller font size but not in bold. Filenames will appear in this same font. Thus, you might see, “In SomeCode.c, set the variable foo to nil.”

Command-line tools and other commands will appear in a slightly-smaller bold font. For instance, “Join the two files with lipo and use the file command to verify the result.”

Occasionally, there will be an excerpt from a terminal window. What you should type will appear in a bold, fixed-width font. The computer’s response will appear in a regular, fixed-width font. Example:

$​ ​l​s​ ​/​v​a​r​
a​t​ ​ ​ ​ ​ ​ ​c​r​o​n​ ​ ​ ​ ​e​m​p​t​y​ ​ ​ ​m​a​i​l​ ​ ​ ​ ​n​a​m​e​d​ ​ ​ ​r​o​o​t​ ​ ​ ​ ​t​m​p​ ​ ​ ​ ​ ​y​p​
b​a​c​k​u​p​s​ ​d​b​ ​ ​ ​ ​ ​ ​l​o​g​ ​ ​ ​ ​ ​m​s​g​s​ ​ ​ ​ ​n​e​t​b​o​o​t​ ​r​u​n​ ​ ​ ​ ​ ​s​p​o​o​l​ ​ ​ ​v​m​

Online Materials

The code in this book can be downloaded from http://www.borkware.com/corebook/.

1
C and Objective-C

The Macintosh and iPhone are general-purpose computing platforms. You can program them in any number of languages from Fortran to Tcl, Ruby to Nu, C to Java. However, the primary application programming interfaces (APIs) from Apple are designed to work in plain C and Objective-C. The Unix API is C-based, while Cocoa and Cocoa Touch are Objective-C based.

A solid grounding in C and Objective-C, though, is very important. Basic C and Objective-C will not be covered here since there are a number of very good introductory texts available online and at the bookstore. This chapter covers aspects of C and Objective-C that are not ordinarily covered in introductory texts or are recent additions to the language.

C

The Compiler pipeline

C is a compiled language, unlike scripting languages, which are usually interpreted. This means that there is no REPL (Read, Evaluate, Print Loop) for interactive exploration. When you build your program, you need to run your source code through the C compiler which then emits a binary object file. After each of your source files have been compiled into object files, the linker combines your object files, along with system libraries and frameworks (and your own libraries and frameworks), to create the final executable program. And to make things more complicated, your C source files are manipulated by the C preprocessor, which subjects your source code to textual manipulations before the compiler sees it. This whole pipeline is shown in Figure 1.1.

Figure 1.1  The compiler pipeline

The C preprocessor

The C preprocessor is a simple tool that performs textual manipulations, such as replacing specific text with other text, expanding macros with arguments, including text from other files, and conditionally including or excluding text.

The C preprocessor only does textual manipulations. It has no knowledge of the C language, so you can happily use the preprocessor to create a real mess or to come up with some cool hacks. (Sometimes the two are indistinguishable.)

Preprocessor symbols

You define symbols in the preprocessor with #define. The leading # indicates a directive to the preprocessor. All preprocessor directives are processed and removed before the compiler sees the text.

#​d​e​f​i​n​e​ ​S​O​M​E​_​S​Y​M​B​O​L​ ​2​3​
#​d​e​f​i​n​e​ ​A​N​O​T​H​E​R​_​S​Y​M​B​O​L​
#​d​e​f​i​n​e​ ​M​A​C​R​O​(​x​)​ ​d​o​S​o​m​e​t​h​i​n​g​C​o​o​l​W​i​t​h​(​x​)​

The first #define tells the preprocessor to substitute the text 23 whenever it encounters SOME_SYMBOL in your source file. The second #define tells the preprocessor that “ANOTHER_SYMBOL exists, but it has no value.” If the preprocessor sees ANOTHER_SYMBOL in the source file, it will take out the text.

The third defines a macro expansion. Whenever the preprocessor sees MACRO with an argument, it expands to the text on the right-hand side of the #define, substituting the argument for x. For instance, MACRO(bork) will end up doSomethingCoolWith(bork) after the preprocessor finishes its work. Example 1.1 shows preprocessor macros in action.

Example 1.1. expansion.m

/​/​ ​e​x​p​a​n​s​i​o​n​.​m​ ​-​-​ ​l​o​o​k​ ​a​t​ ​m​a​c​r​o​ ​e​x​p​a​n​s​i​o​n​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​e​x​p​a​n​s​i​o​n​ ​e​x​p​a​n​s​i​o​n​.​m​

#​i​n​c​l​u​d​e​ ​<​s​t​d​i​o​.​h​>​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​

#​d​e​f​i​n​e​ ​F​N​O​R​D​ ​h​e​l​l​o​
 ​ ​ ​ ​i​n​t​ ​F​N​O​R​D​ ​=​ ​2​3​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​h​e​l​l​o​,​ ​y​o​u​r​ ​n​u​m​b​e​r​ ​t​o​d​a​y​ ​i​s​ ​%​d​\​n​"​,​ ​h​e​l​l​o​)​;​

#​d​e​f​i​n​e​ ​N​O​B​O​D​Y​_​H​O​M​E​
 ​ ​ ​ ​s​t​a​t​i​c​ ​u​n​s​i​g​n​e​d​ ​N​O​B​O​D​Y​_​H​O​M​E​ ​i​n​t​ ​t​h​i​n​g​ ​=​ ​4​2​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​t​h​i​n​g​,​ ​y​o​u​r​ ​n​u​m​b​e​r​ ​t​o​d​a​y​ ​i​s​ ​%​d​\​n​"​,​ ​t​h​i​n​g​)​;​

/​/​ ​T​h​i​s​ ​i​s​ ​a​c​t​u​a​l​l​y​ ​a​ ​d​a​n​g​e​r​o​u​s​ ​w​a​y​ ​t​o​ ​d​o​ ​t​h​i​s​.​ ​ ​S​e​e​
/​/​ ​t​h​e​ ​s​e​c​t​i​o​n​ ​a​b​o​u​t​ ​m​a​c​r​o​ ​h​y​g​i​e​n​e​.​
#​d​e​f​i​n​e​ ​S​U​M​(​x​,​ ​y​)​ ​ ​x​ ​+​ ​y​
 ​ ​ ​ ​i​n​t​ ​v​a​l​u​e​ ​=​ ​S​U​M​(​2​3​,​ ​4​2​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​v​a​l​u​e​,​ ​y​o​u​r​ ​n​u​m​b​e​r​ ​t​o​d​a​y​ ​i​s​ ​%​d​\​n​"​,​ ​v​a​l​u​e​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​

}​ ​/​/​ ​m​a​i​n​

When you compile and run this program, you should see this output:

$​ ​.​/​e​x​p​a​n​s​i​o​n​
h​e​l​l​o​,​ ​y​o​u​r​ ​n​u​m​b​e​r​ ​t​o​d​a​y​ ​i​s​ ​2​3​
t​h​i​n​g​,​ ​y​o​u​r​ ​n​u​m​b​e​r​ ​t​o​d​a​y​ ​i​s​ ​4​2​
v​a​l​u​e​,​ ​y​o​u​r​ ​n​u​m​b​e​r​ ​t​o​d​a​y​ ​i​s​ ​6​5​

The symbol FNORD expanded into the string “hello,” which made the first assignment look like

i​n​t​ ​h​e​l​l​o​ ​=​ ​2​3​;​

The symbol NOBODY_HOME just vanishes because it was never given a replacement value. This leaves thing’s declaration looking like

s​t​a​t​i​c​ ​u​n​s​i​g​n​e​d​ ​i​n​t​ ​t​h​i​n​g​ ​=​ ​4​2​;​

Lastly, the SUM macro makes the value assignment look like

i​n​t​ ​v​a​l​u​e​ ​=​ ​2​3​ ​+​ ​4​2​;​

You can define symbols on the command line using the -D flag:

g​c​c​ ​.​.​.​ ​-​D​T​H​I​N​G​_​1​ ​ ​-​D​T​H​I​N​G​_​2​=​1​2​3​

The preprocessor will behave as if you had these lines at the top of your source file:

#​d​e​f​i​n​e​ ​T​H​I​N​G​_​1​
#​d​e​f​i​n​e​ ​T​H​I​N​G​_​2​ ​1​2​3​

You can also define macros in Xcode projects by including a space-separated list of definitions in the Preprocessor Macros build setting.

Be careful what you decide to #define, especially if you define commonly used tokens like if. You might run into situations where a structure field was #defined to another name, presumably to hack around an error in an API or to change a name but preserve backwards compatibility. This macro could end up clobbering a variable name used in another context.

Stringization and concatenation

The # character can be used in a macro definition to turn an argument into a literal string rather than having it evaluated. ## can be used to textually paste two tokens together. Example 1.2 shows some simple uses.

Example 1.2. stringization.m

/​/​ ​s​t​r​i​n​g​i​z​a​t​i​o​n​.​m​ ​-​-​ ​s​h​o​w​ ​t​o​k​e​n​ ​s​t​r​i​n​g​i​z​a​t​i​o​n​ ​a​n​d​ ​c​o​n​c​a​t​e​n​a​t​o​n​

#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​m​a​t​h​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​s​q​r​t​(​)​

/​/​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​s​t​r​i​n​g​i​z​a​t​i​o​n​ ​s​t​r​i​n​g​i​z​a​t​i​o​n​.​m​

#​d​e​f​i​n​e​ ​F​I​V​E​ ​5​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
#​d​e​f​i​n​e​ ​P​R​I​N​T​_​E​X​P​R​(​x​)​ ​p​r​i​n​t​f​(​"​%​s​ ​=​ ​%​d​\​n​"​,​ ​#​x​,​ ​(​x​)​)​
 ​ ​ ​ ​P​R​I​N​T​_​E​X​P​R​ ​(​5​)​;​
 ​ ​ ​ ​P​R​I​N​T​_​E​X​P​R​ ​(​5​ ​*​ ​1​0​)​;​
 ​ ​ ​ ​P​R​I​N​T​_​E​X​P​R​ ​(​(​i​n​t​)​s​q​r​t​(​F​I​V​E​*​F​I​V​E​)​ ​+​ ​(​i​n​t​)​l​o​g​(​2​5​ ​/​ ​5​)​)​;​

#​d​e​f​i​n​e​ ​S​P​L​I​T​_​F​U​N​C​(​x​,​y​)​ ​ ​x​#​#​y​
 ​ ​ ​ ​S​P​L​I​T​_​F​U​N​C​ ​(​p​r​i​n​,​ ​t​f​)​ ​(​"​h​e​l​l​o​\​n​"​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​

}​ ​/​/​ ​m​a​i​n​

which when run looks like this:

$​ ​.​/​s​t​r​i​n​g​i​z​a​t​i​o​n​
5​ ​=​ ​5​
5​ ​*​ ​1​0​ ​=​ ​5​0​
(​i​n​t​)​s​q​r​t​(​F​I​V​E​*​F​I​V​E​)​ ​+​ ​(​i​n​t​)​l​o​g​(​2​5​ ​/​ ​5​)​ ​=​ ​6​
h​e​l​l​o​

You can see the x argument in PRINT_EXPR is printed out and also evaluated. Notice that the stringization happens before any other symbols are expanded. The concatenating example pieces “prin” and “tf” into “printf.”

Conditional compilation

The preprocessor can be used to conditionally include or exclude text based on the value (or existence) of a particular symbol.

#ifdef some_symbol tells the preprocessor to test for the existence of some_symbol, whether or not it actually has a value. If some_symbol does not exist, the preprocessor omits all the text until it sees an #endif or an #else directive. Use #ifndef if you are interested in knowing if a symbol is not defined.

#if some_symbol works like #ifdef except that it uses the value of the symbol when deciding whether to include the following text. If the symbol has a non-zero numeric value, the text following the #if is allowed to pass through to the compiler. Any other symbol, whether alphanumeric or a macro, will cause the text following the #if to be omitted until the preprocessor sees an #endif or #else directive. #if statements can also contain expressions based on the numeric value of the symbol. Example 1.3 shows conditional compilation.

Example 1.3. conditional-compilation.m

/​/​ ​c​o​n​d​i​t​i​o​n​a​l​-​c​o​m​p​i​l​a​t​i​o​n​.​m​ ​-​-​ ​l​o​o​k​ ​a​t​ ​m​a​c​r​o​ ​e​x​p​a​n​s​i​o​n​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​c​o​n​d​i​t​i​o​n​a​l​-​c​o​m​p​i​l​a​t​i​o​n​ ​c​o​n​d​i​t​i​o​n​a​l​-​c​o​m​p​i​l​a​t​i​o​n​.​m​

#​i​n​c​l​u​d​e​ ​<​s​t​d​i​o​.​h​>​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​

#​d​e​f​i​n​e​ ​D​E​F​I​N​E​D​_​N​O​_​V​A​L​U​E​
#​d​e​f​i​n​e​ ​V​E​R​S​I​O​N​ ​1​0​
#​d​e​f​i​n​e​ ​Z​E​D​ ​0​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​

#​i​f​d​e​f​ ​D​E​F​I​N​E​D​_​N​O​_​V​A​L​U​E​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​d​e​f​i​n​e​d​_​n​o​_​v​a​l​u​e​ ​i​s​ ​d​e​f​i​n​e​d​\​n​"​)​;​
#​e​l​s​e​
 ​ ​ ​ ​i​ ​c​a​n​ ​h​a​s​ ​s​y​n​t​a​x​ ​e​r​r​o​r​;​
#​e​n​d​i​f​

#​i​f​d​e​f​ ​Z​E​D​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​z​e​d​ ​i​s​ ​d​e​f​i​n​e​d​\​n​"​)​;​
#​e​n​d​i​f​

#​i​f​ ​Z​E​D​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​z​e​d​ ​e​v​a​l​u​a​t​e​s​ ​t​o​ ​t​r​u​e​\​n​"​)​;​
#​e​l​s​e​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​z​e​d​ ​e​v​a​l​u​a​t​e​s​ ​t​o​ ​f​a​l​s​e​\​n​"​)​;​
#​e​n​d​i​f​

#​i​f​ ​V​E​R​S​I​O​N​ ​>​ ​5​ ​&​&​ ​V​E​R​S​I​O​N​ ​<​ ​2​0​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​v​e​r​s​i​o​n​ ​i​s​ ​i​n​ ​t​h​e​ ​c​o​r​r​e​c​t​ ​r​a​n​g​e​.​\​n​"​)​;​
#​e​n​d​i​f​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​

}​ ​/​/​ ​m​a​i​n​

You should see this output when you compile and run this program.

%​ ​.​/​c​o​n​d​i​t​i​o​n​a​l​-​c​o​m​p​i​l​a​t​i​o​n​
d​e​f​i​n​e​d​_​n​o​_​v​a​l​u​e​ ​i​s​ ​d​e​f​i​n​e​d​
z​e​d​ ​i​s​ ​d​e​f​i​n​e​d​
z​e​d​ ​e​v​a​l​u​a​t​e​s​ ​t​o​ ​f​a​l​s​e​
v​e​r​s​i​o​n​ ​i​s​ ​i​n​ ​t​h​e​ ​c​o​r​r​e​c​t​ ​r​a​n​g​e​.​

Conditional compilation is useful when you have multi-platform code and you need to be able to turn compatibility features on or off. A library call may have different arguments on OS X than on a Linux system. It also can be used for turning features on or off with just a compiler flag, such as including or excluding encryption from a web server communications driver, or removing the ability to print from a trial version of a text editing application. You could include some computationally expensive sanity checking for a debug-only build.

Predefined symbols

The compiler comes with a lot of preprocessor symbols predefined, many of which vary based on OS version, compiler version, or processor architecture. You can see them all with this command:

%​ ​g​c​c​ ​-​E​ ​-​d​M​ ​-​ ​<​ ​/​d​e​v​/​n​u​l​l​
#​d​e​f​i​n​e​ ​_​_​D​B​L​_​M​I​N​_​E​X​P​_​_​ ​(​-​1​0​2​1​)​
#​d​e​f​i​n​e​ ​_​_​F​L​T​_​M​I​N​_​_​ ​1​.​1​7​5​4​9​4​3​5​e​-​3​8​F​
#​d​e​f​i​n​e​ ​_​_​C​H​A​R​_​B​I​T​_​_​ ​8​
#​d​e​f​i​n​e​ ​_​_​W​C​H​A​R​_​M​A​X​_​_​ ​2​1​4​7​4​8​3​6​4​7​
#​d​e​f​i​n​e​ ​_​_​D​B​L​_​D​E​N​O​R​M​_​M​I​N​_​_​ ​4​.​9​4​0​6​5​6​4​5​8​4​1​2​4​6​5​4​e​-​3​2​4​
#​d​e​f​i​n​e​ ​_​_​L​I​T​T​L​E​_​E​N​D​I​A​N​_​_​ ​1​
.​.​.​
#​d​e​f​i​n​e​ ​_​_​A​P​P​L​E​_​_​ ​1​
#​d​e​f​i​n​e​ ​_​_​G​N​U​C​_​_​ ​4​
#​d​e​f​i​n​e​ ​_​_​M​M​X​_​_​ ​1​
#​d​e​f​i​n​e​ ​O​B​J​C​_​N​E​W​_​P​R​O​P​E​R​T​I​E​S​ ​1​
#​d​e​f​i​n​e​ ​_​_​G​X​X​_​A​B​I​_​V​E​R​S​I​O​N​ ​1​0​0​2​

The -E flag tells gcc to display preprocessor output, -dM is a gcc debugging flag that dumps out symbols at some point during the compilation process, and - < /dev/null tells gcc to expect its program text from standard in and feeds it /dev/null to indicate there is actually no text coming in. You can feed the same arguments to clang to see what symbols Apple’s new compiler defines automatically.

Some of the symbols are pretty esoteric, and some can be very useful. Some of the more useful ones include:

__APPLE__ | Defined for an Apple platform, such as OS X.
__APPLE_CC__ | This is an integer value representing the version of the compiler.
__OBJC__ | Defined if the compiler is compiling in Objective-C mode.
__cplusplus | Defined if the compiler is compiling in C++ mode.
__MACH__ | Defined if the Mach system calls are available.
__LITTLE_ENDIAN__ | Defined if you are compiling for a little endian processor, like Intel or ARM.
__BIG_ENDIAN__ | Defined if you are compiling for a big endian processor, like PowerPC.
__LP64__ | Defined if you are compiling in 64-bit mode.

There are also some built-in preprocessor symbols that have varying values:

__DATE__ | The current date, as a char *.
__TIME__ | The current time, as a char *.
__FILE__ | The name of the file currently being compiled, as a char *.
__LINE__ | The line number of the file before preprocessing, as an int.
__func__ | The name of the function or Objective-C method being compiled, as a char *. This is not actually a preprocessor feature but something that comes from the compiler. Remember that the preprocessor does not know anything about the languages of the files it processes.
__FUNCTION__ | Equivalent to __func__ and available in older versions of gcc where __func__ is not. With modern Mac or iOS programming, they are interchangeable.
__PRETTY_FUNCTION__ | The name of the function as a char *, as with __func__, but it includes type information.

These do not appear when you ask the compiler to display all of the built-in macros because they are being expanded by the compiler, not the preprocessor. Example 1.4 shows some of the predefined macros.

Example 1.4. predef.m

/​/​ ​p​r​e​d​e​f​.​m​m​ ​-​-​ ​p​l​a​y​ ​w​i​t​h​ ​p​r​e​d​e​f​i​n​e​d​ ​m​a​c​r​o​s​

/​/​ ​g​+​+​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​p​r​e​d​e​f​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​p​r​e​d​e​f​.​m​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​

v​o​i​d​ ​s​o​m​e​F​u​n​c​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​f​i​l​e​ ​%​s​,​ ​l​i​n​e​ ​%​d​\​n​"​,​ ​_​_​F​I​L​E​_​_​,​ ​_​_​L​I​N​E​_​_​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​f​u​n​c​t​i​o​n​:​ ​%​s​\​n​"​,​ ​_​_​F​U​N​C​T​I​O​N​_​_​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​p​r​e​t​t​y​ ​f​u​n​c​t​i​o​n​:​ ​%​s​\​n​"​,​ ​_​_​P​R​E​T​T​Y​_​F​U​N​C​T​I​O​N​_​_​)​;​
}​ ​/​/​ ​s​o​m​e​F​u​n​c​

@​i​n​t​e​r​f​a​c​e​ ​S​o​m​e​C​l​a​s​s​ ​:​ ​N​S​O​b​j​e​c​t​
+​ ​(​v​o​i​d​)​ ​s​o​m​e​M​e​t​h​o​d​;​
+​ ​(​v​o​i​d​)​ ​s​o​m​e​M​e​t​h​o​d​:​ ​(​i​n​t​)​ ​n​u​m​ ​ ​w​i​t​h​A​r​g​u​m​e​n​t​s​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​a​r​g​;​
@​e​n​d​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​S​o​m​e​C​l​a​s​s​
+​ ​(​v​o​i​d​)​ ​s​o​m​e​M​e​t​h​o​d​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​f​i​l​e​ ​%​s​,​ ​l​i​n​e​ ​%​d​\​n​"​,​ ​_​_​F​I​L​E​_​_​,​ ​_​_​L​I​N​E​_​_​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​f​u​n​c​t​i​o​n​:​ ​%​s​\​n​"​,​ ​_​_​F​U​N​C​T​I​O​N​_​_​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​p​r​e​t​t​y​ ​f​u​n​c​t​i​o​n​:​ ​%​s​\​n​"​,​ ​_​_​P​R​E​T​T​Y​_​F​U​N​C​T​I​O​N​_​_​)​;​
}​ ​/​/​ ​s​o​m​e​M​e​t​h​o​d​

+​ ​(​v​o​i​d​)​ ​s​o​m​e​M​e​t​h​o​d​:​ ​(​i​n​t​)​ ​n​u​m​ ​ ​w​i​t​h​A​r​g​u​m​e​n​t​s​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​a​r​g​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​f​i​l​e​ ​%​s​,​ ​l​i​n​e​ ​%​d​\​n​"​,​ ​_​_​F​I​L​E​_​_​,​ ​_​_​L​I​N​E​_​_​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​f​u​n​c​t​i​o​n​:​ ​%​s​\​n​"​,​ ​_​_​F​U​N​C​T​I​O​N​_​_​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​p​r​e​t​t​y​ ​f​u​n​c​t​i​o​n​:​ ​%​s​\​n​"​,​ ​_​_​P​R​E​T​T​Y​_​F​U​N​C​T​I​O​N​_​_​)​;​
}​ ​/​/​ ​s​o​m​e​M​e​t​h​o​d​:​w​i​t​h​A​r​g​u​m​e​n​t​s​

@​e​n​d​

c​l​a​s​s​ ​S​o​m​e​O​t​h​e​r​C​l​a​s​s​ ​{​
p​u​b​l​i​c​:​
 ​ ​ ​ ​v​o​i​d​ ​S​o​m​e​M​e​m​b​e​r​F​u​n​c​t​i​o​n​ ​(​i​n​t​ ​a​r​g​1​,​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​a​r​g​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​f​i​l​e​ ​%​s​,​ ​l​i​n​e​ ​%​d​\​n​"​,​ ​_​_​F​I​L​E​_​_​,​ ​_​_​L​I​N​E​_​_​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​f​u​n​c​t​i​o​n​:​ ​%​s​\​n​"​,​ ​_​_​F​U​N​C​T​I​O​N​_​_​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​p​r​e​t​t​y​ ​f​u​n​c​t​i​o​n​:​ ​%​s​\​n​"​,​ ​_​_​P​R​E​T​T​Y​_​F​U​N​C​T​I​O​N​_​_​)​;​
 ​ ​ ​ ​}​
}​;​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​_​_​A​P​P​L​E​_​_​:​ ​%​d​\​n​"​,​ ​_​_​A​P​P​L​E​_​_​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​t​o​d​a​y​ ​i​s​ ​%​s​,​ ​t​h​e​ ​t​i​m​e​ ​i​s​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​_​_​D​A​T​E​_​_​,​ ​_​_​T​I​M​E​_​_​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​f​i​l​e​ ​%​s​,​ ​l​i​n​e​ ​%​d​\​n​"​,​ ​_​_​F​I​L​E​_​_​,​ ​_​_​L​I​N​E​_​_​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​f​u​n​c​t​i​o​n​:​ ​%​s​\​n​"​,​ ​_​_​F​U​N​C​T​I​O​N​_​_​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​p​r​e​t​t​y​ ​f​u​n​c​t​i​o​n​:​ ​%​s​\​n​"​,​ ​_​_​P​R​E​T​T​Y​_​F​U​N​C​T​I​O​N​_​_​)​;​

 ​ ​ ​ ​s​o​m​e​F​u​n​c​ ​(​)​;​

 ​ ​ ​ ​[​S​o​m​e​C​l​a​s​s​ ​s​o​m​e​M​e​t​h​o​d​]​;​
 ​ ​ ​ ​[​S​o​m​e​C​l​a​s​s​ ​s​o​m​e​M​e​t​h​o​d​:​ ​2​3​ ​ ​w​i​t​h​A​r​g​u​m​e​n​t​s​:​ ​@​"​s​n​o​r​k​"​]​;​

 ​ ​ ​ ​S​o​m​e​O​t​h​e​r​C​l​a​s​s​ ​s​o​m​e​t​h​i​n​g​;​
 ​ ​ ​ ​s​o​m​e​t​h​i​n​g​.​S​o​m​e​M​e​m​b​e​r​F​u​n​c​t​i​o​n​ ​(​2​3​,​ ​"​h​i​"​)​;​

#​i​f​ ​_​_​L​I​T​T​L​E​_​E​N​D​I​A​N​_​_​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​I​'​m​ ​(​m​o​s​t​ ​l​i​k​e​l​y​)​ ​r​u​n​n​i​n​g​ ​o​n​ ​i​n​t​e​l​!​ ​w​o​o​!​\​n​"​)​;​
#​e​n​d​i​f​
#​i​f​ ​_​_​B​I​G​_​E​N​D​I​A​N​_​_​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​I​'​m​ ​(​m​o​s​t​ ​l​i​k​e​l​y​)​ ​r​u​n​n​i​n​g​ ​o​n​ ​p​o​w​e​r​P​C​!​ ​w​o​o​!​\​n​"​)​;​
#​e​n​d​i​f​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

And a sample run would look like this:

$​ ​.​/​p​r​e​d​e​f​
_​_​A​P​P​L​E​_​_​:​ ​1​
t​o​d​a​y​ ​i​s​ ​J​a​n​ ​1​7​ ​2​0​1​1​,​ ​t​h​e​ ​t​i​m​e​ ​i​s​ ​1​4​:​2​3​:​3​3​
f​i​l​e​ ​p​r​e​d​e​f​.​m​m​,​ ​l​i​n​e​ ​4​8​
 ​ ​f​u​n​c​t​i​o​n​:​ ​m​a​i​n​
 ​ ​p​r​e​t​t​y​ ​f​u​n​c​t​i​o​n​:​ ​i​n​t​ ​m​a​i​n​(​i​n​t​,​ ​c​h​a​r​*​*​)​
f​i​l​e​ ​p​r​e​d​e​f​.​m​m​,​ ​l​i​n​e​ ​9​
 ​ ​f​u​n​c​t​i​o​n​:​ ​s​o​m​e​F​u​n​c​
 ​ ​p​r​e​t​t​y​ ​f​u​n​c​t​i​o​n​:​ ​v​o​i​d​ ​s​o​m​e​F​u​n​c​(​)​
f​i​l​e​ ​p​r​e​d​e​f​.​m​m​,​ ​l​i​n​e​ ​2​1​
 ​ ​f​u​n​c​t​i​o​n​:​ ​+​[​S​o​m​e​C​l​a​s​s​ ​s​o​m​e​M​e​t​h​o​d​]​
 ​ ​p​r​e​t​t​y​ ​f​u​n​c​t​i​o​n​:​ ​v​o​i​d​ ​+​[​S​o​m​e​C​l​a​s​s​ ​s​o​m​e​M​e​t​h​o​d​]​(​o​b​j​c​_​o​b​j​e​c​t​*​,​ ​o​b​j​c​_​s​e​l​e​c​t​o​r​*​)​
f​i​l​e​ ​p​r​e​d​e​f​.​m​m​,​ ​l​i​n​e​ ​2​7​
 ​ ​f​u​n​c​t​i​o​n​:​ ​+​[​S​o​m​e​C​l​a​s​s​ ​s​o​m​e​M​e​t​h​o​d​:​w​i​t​h​A​r​g​u​m​e​n​t​s​:​]​
 ​ ​p​r​e​t​t​y​ ​f​u​n​c​t​i​o​n​:​ ​v​o​i​d​ ​+​[​S​o​m​e​C​l​a​s​s​ ​s​o​m​e​M​e​t​h​o​d​:​w​i​t​h​A​r​g​u​m​e​n​t​s​:​]​ ​\​
 ​ ​ ​ ​ ​ ​(​o​b​j​c​_​o​b​j​e​c​t​*​,​ ​o​b​j​c​_​s​e​l​e​c​t​o​r​*​,​ ​i​n​t​,​ ​N​S​S​t​r​i​n​g​*​)​
f​i​l​e​ ​p​r​e​d​e​f​.​m​m​,​ ​l​i​n​e​ ​3​7​
 ​ ​f​u​n​c​t​i​o​n​:​ ​S​o​m​e​M​e​m​b​e​r​F​u​n​c​t​i​o​n​
 ​ ​p​r​e​t​t​y​ ​f​u​n​c​t​i​o​n​:​ ​v​o​i​d​ ​S​o​m​e​O​t​h​e​r​C​l​a​s​s​:​:​S​o​m​e​M​e​m​b​e​r​F​u​n​c​t​i​o​n​(​i​n​t​,​ ​c​o​n​s​t​ ​c​h​a​r​*​)​
I​'​m​ ​(​m​o​s​t​ ​l​i​k​e​l​y​)​ ​r​u​n​n​i​n​g​ ​o​n​ ​i​n​t​e​l​!​ ​w​o​o​!​

File inclusion

The preprocessor #include directive takes the contents of one file and inserts it into the stream of text that it feeds to the compiler. You can specify the file name in angle brackets to indicate the file being included is a “system” header:

#​i​n​c​l​u​d​e​ ​<​s​t​d​i​o​.​h​>​

The preprocessor looks in some well-known locations, such as /usr/include, to find the file named stdio.h. You specify the file name in quotes to indicate that the file being included is one that belongs to your project:

#​i​n​c​l​u​d​e​ ​"​A​T​M​M​a​c​h​i​n​e​.​h​"​

Files that are #included can also #include other files. It’s possible that a file can be included more than once. This can cause problems for C header files: the compiler doesn’t like it when you declare a structure twice, for instance. Consider this program:

#​i​n​c​l​u​d​e​ ​<​f​c​n​t​l​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​o​p​e​n​(​)​
#​i​n​c​l​u​d​e​ ​<​u​l​i​m​i​t​.​h​>​ ​ ​/​/​ ​f​o​r​ ​u​l​i​m​i​t​(​)​
#​i​n​c​l​u​d​e​ ​<​p​t​h​r​e​a​d​.​h​>​ ​/​/​ ​f​o​r​ ​p​t​h​r​e​a​d​_​c​r​e​a​t​e​(​)​
#​i​n​c​l​u​d​e​ ​<​d​i​r​e​n​t​.​h​>​ ​ ​/​/​ ​f​o​r​ ​o​p​e​n​d​i​r​(​)​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​/​/​ ​n​o​b​o​d​y​ ​h​o​m​e​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

This is a pretty typical set of #includes for a Unix program. Sometimes you can have a dozen or more includes to pull in different sets of library types and function prototypes. Each of those #includes includes other files, ending up with something that looks like Figure 1.2

Figure 1.2  Nested #includes

Notice that many headers are included multiple times, such as <sys/cdefs.h>, which gets included seven times. There is a standard trick you can use to prevent multiple inclusions of a file from causing errors: an include guard, shown in Example 1.5:

Example 1.5. include-guard.h

/​/​ ​i​n​c​l​u​d​e​-​g​u​a​r​d​.​h​ ​-​-​ ​m​a​k​i​n​g​ ​s​u​r​e​ ​t​h​e​ ​c​o​n​t​e​n​t​s​ ​o​f​ ​t​h​e​ ​h​e​a​d​e​r​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​r​e​ ​i​n​c​l​u​d​e​d​ ​o​n​l​y​ ​o​n​c​e​.​
#​i​f​n​d​e​f​ ​I​N​C​L​U​D​E​_​G​U​A​R​D​_​H​
#​d​e​f​i​n​e​ ​I​N​C​L​U​D​E​_​G​U​A​R​D​_​H​

/​/​ ​P​u​t​ ​t​h​e​ ​r​e​a​l​ ​h​e​a​d​e​r​ ​c​o​n​t​e​n​t​s​ ​h​e​r​e​.​

#​e​n​d​i​f​ ​/​/​ ​I​N​C​L​U​D​E​_​G​U​A​R​D​_​H​

So how does this work? The first time include-guard.h is #included, the symbol INCLUDE_GUARD_H is undefined, so #ifndef INCLUDE_GUARD_H will evaluate to true, and the text after that is included, including the #define of INCLUDE_GUARD_H. If include-guard.h is included again while preprocessing this source file, INCLUDE_GUARD_H has been defined, so the #ifndef test fails, and the file is skipped down through the #endif.

There are two drawbacks to this technique. The first is that the guard symbol has to be unique across all header files that might be included together; otherwise one header’s contents won’t be included when they need to be. The other drawback is that the preprocessor has to scan through the entire header file, even if all of it is going to be thrown out. For a situation like Figure 1.2, <sys/cdefs.h> will still be opened and read 7 times even though the file’s contents will be used only the first time through.

Luckily, the compiler includes optimizations to avoid doing all of this extra work when include guards are in place. It also supports the #pragma once directive, which tells the compiler to only include the file containing the pragma a single time. In Objective-C, life is even simpler. Instead of #include, Objective-C uses the #import directive, which directs the compiler to include the file only once.

Macro hygiene

Earlier you read about the preprocessor being stupid. It really is. It has no clue about the context of your code, so it cannot do what you mean, just what you say. So be careful how you say things.

You might have a preprocessor macro that looks like this:

#​d​e​f​i​n​e​ ​S​Q​U​A​R​E​(​x​)​ ​ ​ ​ ​x​*​x​

Pretty simple. SQUARE(5) turns into 5*5, which yields the desired result. If someone uses SQUARE(2+3), the text expansion will get fed to the compiler as 2+3*2+3, which due to C precedence rules is actually calculated as 2 + (3*2) + 3, which is 11, not 25. Oops. If you parenthesize the arguments instead:

#​d​e​f​i​n​e​ ​S​Q​U​A​R​E​(​x​)​ ​ ​(​x​)​*​(​x​)​

Then SQUARE(2+3) will turn into (2+3)*(2+3), the correct expression. It is generally a good idea to surround the whole macro result in parentheses, such as:

#​d​e​f​i​n​e​ ​S​Q​U​A​R​E​(​x​)​ ​ ​ ​(​(​x​)​*​(​x​)​)​

which prevents problems if the macro expands into an expression with operators of higher precedence.

Beware of side effects in macros. Preprocessor macro expansion is strictly textual substitution. The code SQUARE(i++) will expand to ((i++) * (i++)), which actually is undefined in the C standard. It might cause i to be incremented twice. Then again, it might not. Similarly, using functions with side effects, or that are computationally expensive, in a macro like this one could be bad news.

Multiline macros

Sometimes you want a macro to be more than one line of code, such as this one that increases a global error count and then displays an error for the user. The backslashes are necessary to let a single macro span multiple source lines.

#​d​e​f​i​n​e​ ​F​O​U​N​D​_​A​N​_​E​R​R​O​R​(​d​e​s​c​)​ ​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​_​c​o​u​n​t​+​+​;​ ​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​(​s​t​d​e​r​r​,​ ​"​F​o​u​n​d​ ​a​n​ ​e​r​r​o​r​ ​'​%​s​'​ ​a​t​ ​f​i​l​e​ ​%​s​,​ ​l​i​n​e​ ​%​d​\​n​"​,​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​s​c​,​ ​_​_​F​I​L​E​_​_​,​ ​_​_​L​I​N​E​_​_​)​

The macro can be used like this:

i​f​ ​(​a​r​g​c​ ​=​=​ ​2​)​ ​{​
 ​ ​ ​ ​F​O​U​N​D​_​A​N​_​E​R​R​O​R​ ​(​"​s​o​m​e​t​h​i​n​g​ ​r​e​a​l​l​y​ ​b​a​d​ ​h​a​p​p​e​n​e​d​"​)​;​
}​

Example 1.6 uses the macro:

Example 1.6. multilineMacro.m

#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​m​u​l​t​i​l​i​n​e​M​a​c​r​o​ ​m​u​l​t​i​l​i​n​e​M​a​c​r​o​.​m​

#​d​e​f​i​n​e​ ​F​O​U​N​D​_​A​N​_​E​R​R​O​R​(​d​e​s​c​)​ ​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​_​c​o​u​n​t​+​+​;​ ​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​(​s​t​d​e​r​r​,​ ​"​F​o​u​n​d​ ​a​n​ ​e​r​r​o​r​ ​'​%​s​'​ ​a​t​ ​f​i​l​e​ ​%​s​,​ ​l​i​n​e​ ​%​d​\​n​"​,​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​s​c​,​ ​_​_​F​I​L​E​_​_​,​ ​_​_​L​I​N​E​_​_​)​

i​n​t​ ​e​r​r​o​r​_​c​o​u​n​t​;​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​=​=​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​F​O​U​N​D​_​A​N​_​E​R​R​O​R​ ​(​"​s​o​m​e​t​h​i​n​g​ ​r​e​a​l​l​y​ ​b​a​d​ ​h​a​p​p​e​n​e​d​"​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​d​o​n​e​\​n​"​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​

}​ ​/​/​ ​m​a​i​n​

If you compile and run it, you see the “success” case:

$​ ​.​/​m​u​l​t​i​l​i​n​e​M​a​c​r​o​ ​
d​o​n​e​

Now run it with an argument, which is the “error” case:

$​ ​.​/​m​u​l​t​i​l​i​n​e​M​a​c​r​o​ ​b​o​r​k​ ​
F​o​u​n​d​ ​a​n​ ​e​r​r​o​r​ ​'​s​o​m​e​t​h​i​n​g​ ​b​a​d​ ​h​a​p​p​e​n​e​d​'​ ​a​t​ ​f​i​l​e​ ​m​u​l​t​i​l​i​n​e​M​a​c​r​o​.​m​,​ ​l​i​n​e​ ​1​3​
d​o​n​e​

Looks like it works fine. Ship it!

There is one lurking problem, though: What happens if a programmer on your team does not fully brace their single-line if statements? The code looks innocent enough if you take out the braces:

i​f​ ​(​a​r​g​c​ ​=​=​ ​2​)​
 ​ ​ ​ ​F​O​U​N​D​_​A​N​_​E​R​R​O​R​ ​(​"​s​o​m​e​t​h​i​n​g​ ​b​a​d​ ​h​a​p​p​e​n​e​d​"​)​;​

But, if you run the program now without an argument (the “success” case), you get an error:

$​ ​.​/​m​u​l​t​i​l​i​n​e​M​a​c​r​o​
F​o​u​n​d​ ​a​n​ ​e​r​r​o​r​ ​'​s​o​m​e​t​h​i​n​g​ ​b​a​d​ ​h​a​p​p​e​n​e​d​'​ ​a​t​ ​f​i​l​e​ ​m​u​l​t​i​l​i​n​e​M​a​c​r​o​.​m​,​ ​l​i​n​e​ ​1​3​
d​o​n​e​

That’s not good. Previously correct code is now considered an error. Take a look at what is happening as the preprocessor mutates your code from:

i​f​ ​(​a​r​g​c​ ​=​=​ ​2​)​
 ​ ​ ​ ​F​O​U​N​D​_​A​N​_​E​R​R​O​R​ ​(​"​s​o​m​e​t​h​i​n​g​ ​b​a​d​ ​h​a​p​p​e​n​e​d​"​)​;​

to

i​f​ ​(​a​r​g​c​ ​=​=​ ​2​)​
 ​ ​ ​ ​e​r​r​o​r​_​c​o​u​n​t​+​+​;​
 ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​F​o​u​n​d​ ​a​n​ ​e​r​r​o​r​ ​'​%​s​'​ ​a​t​ ​f​i​l​e​ ​%​s​,​ ​l​i​n​e​ ​%​d​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​s​o​m​e​t​h​i​n​g​ ​b​a​d​ ​h​a​p​p​e​n​e​d​"​,​ ​"​m​u​l​t​i​l​i​n​e​M​a​c​r​o​.​m​"​,​ ​1​3​)​;​

This is what it looks like indented the way that it is actually being executed:

i​f​ ​(​a​r​g​c​ ​=​=​ ​2​)​
 ​ ​ ​ ​e​r​r​o​r​_​c​o​u​n​t​+​+​;​
f​p​r​i​n​t​f​(​s​t​d​e​r​r​,​ ​"​F​o​u​n​d​ ​a​n​ ​e​r​r​o​r​ ​'​%​s​'​ ​a​t​ ​f​i​l​e​ ​%​s​,​ ​l​i​n​e​ ​%​d​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​"​s​o​m​e​t​h​i​n​g​ ​b​a​d​ ​h​a​p​p​e​n​e​d​"​,​ ​"​m​u​l​t​i​l​i​n​e​M​a​c​r​o​.​m​"​,​ ​1​3​)​;​

You need to wrap these multiline macros in curly braces so that they are essentially one statement. It will then become one statement as far as the compiler is concerned.

If you change the macro to read:

#​d​e​f​i​n​e​ ​F​O​U​N​D​_​A​N​_​E​R​R​O​R​(​d​e​s​c​)​ ​ ​ ​\​
 ​ ​ ​ ​d​o​ ​{​ ​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​_​c​o​u​n​t​+​+​;​ ​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​(​s​t​d​e​r​r​,​ ​"​F​o​u​n​d​ ​a​n​ ​e​r​r​o​r​ ​'​%​s​'​ ​a​t​ ​f​i​l​e​ ​%​s​,​ ​l​i​n​e​ ​%​d​\​n​"​,​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​s​c​,​ ​_​_​F​I​L​E​_​_​,​ ​_​_​L​I​N​E​_​_​)​;​ ​\​
 ​ ​ ​ ​}​ ​w​h​i​l​e​ ​(​0​)​

and rerun it, the program works properly. As you can see, the macro is wrapped with a do {} while (0) statement, which means that the code inside of the loop will be executed only once. This idiom has the side-effect of turning the multi-line operation into a single statement, which makes the unbraced if behave as expected. This is a technique known as “eating the semicolon.” Using braces alone does not work because you end up with stray semicolons that confuse the compiler if there is an else clause.

This solution hides one last problem. What happens if there is a break in the macro or something included in the expansion? Granted, hiding a break or continue in a macro is not a terribly good idea, but sometimes you need to do it. The while loop will consume the break and not let it affect the loop that contains the macro.

#​d​e​f​i​n​e​ ​F​O​U​N​D​_​A​N​_​E​R​R​O​R​(​d​e​s​c​)​ ​ ​\​
 ​ ​ ​ ​d​o​ ​{​ ​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​_​c​o​u​n​t​+​+​;​ ​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​(​s​t​d​e​r​r​,​ ​"​F​o​u​n​d​ ​a​n​ ​e​r​r​o​r​ ​'​%​s​'​ ​a​t​ ​f​i​l​e​ ​%​s​,​ ​l​i​n​e​ ​%​d​\​n​"​,​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​s​c​,​ ​_​_​F​I​L​E​_​_​,​ ​_​_​L​I​N​E​_​_​)​;​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​ ​ ​\​
 ​ ​ ​ ​}​ ​w​h​i​l​e​ ​(​0​)​

w​h​i​l​e​ ​(​n​o​_​e​r​r​o​r​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​x​ ​>​ ​1​0​)​ ​F​O​U​N​D​_​A​N​_​E​R​R​O​R​ ​(​"​x​ ​t​o​o​ ​l​a​r​g​e​"​)​;​
 ​ ​ ​ ​/​*​ ​d​o​ ​s​o​m​e​t​h​i​n​g​ ​w​i​t​h​ ​x​ ​*​/​
}​

The while loop continues as long as x is not greater than 10. If it is, the macro logs an error and then performs a break. But it only breaks out of the loop used in the macro’s implementation.

Here is one solution to this break-capturing problem:

#​d​e​f​i​n​e​ ​F​O​U​N​D​_​A​N​_​E​R​R​O​R​(​d​e​s​c​)​ ​ ​\​
 ​ ​ ​ ​i​f​ ​(​1​)​ ​{​ ​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​_​c​o​u​n​t​+​+​;​ ​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​(​s​t​d​e​r​r​,​ ​"​F​o​u​n​d​ ​a​n​ ​e​r​r​o​r​ ​'​%​s​'​ ​a​t​ ​f​i​l​e​ ​%​s​,​ ​l​i​n​e​ ​%​d​\​n​"​,​ ​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​s​c​,​ ​_​_​F​I​L​E​_​_​,​ ​_​_​L​I​N​E​_​_​)​;​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​ ​ ​\​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​d​o​ ​{​}​ ​w​h​i​l​e​ ​(​0​)​

The if (1) gives you a new scope for your macro and also turns the macro into a single expression. The empty do {} while at the end eats the semicolon. For the most part, just wrapping your macro in do/while will address multi-line macro issues, with the if (1) technical there if you need to play games with loop control.

Const and volatile variables

const is a C keyword that tells the compiler that a variable will not be modified. The compiler is allowed to make assumptions about the variable that can help in optimization and also flag as errors any attempts to modify the variable after its declaration. const used with a scalar variable makes that variable a constant:

c​o​n​s​t​ ​i​n​t​ ​i​ ​=​ ​2​3​;​
i​ ​=​ ​2​4​;​ ​/​/​ ​e​r​r​o​r​:​ ​a​s​s​i​g​n​m​e​n​t​ ​o​f​ ​r​e​a​d​-​o​n​l​y​ ​v​a​r​i​a​b​l​e​ ​'​i​'​

When dealing with pointers, const can apply to the pointer, to what the pointer points to, or both.

Listing const first causes the data being pointed to be considered read-only:

/​/​ ​p​o​i​n​t​e​r​ ​t​o​ ​c​o​n​s​t​ ​c​h​a​r​
c​o​n​s​t​ ​c​h​a​r​ ​*​s​t​r​i​n​g​ ​=​ ​"​b​o​r​k​"​;​ ​/​/​ ​T​h​e​ ​d​a​t​a​ ​p​o​i​n​t​e​d​ ​t​o​ ​b​y​ ​s​t​r​i​n​g​ ​i​s​ ​c​o​n​s​t​.​
s​t​r​i​n​g​ ​=​ ​"​g​r​e​e​b​l​e​"​;​ ​/​/​ ​P​o​i​n​t​e​r​ ​r​e​a​s​s​i​g​n​m​e​n​t​ ​i​s​ ​o​k​.​
s​t​r​i​n​g​[​0​]​ ​=​ ​'​f​'​;​ ​ ​ ​ ​/​/​ ​e​r​r​o​r​:​ ​a​s​s​i​g​n​m​e​n​t​ ​o​f​ ​r​e​a​d​-​o​n​l​y​ ​l​o​c​a​t​i​o​n​

Putting const after the * causes the pointer itself to be considered read-only without affecting the mutability of what it points to:

/​/​ ​c​o​n​s​t​ ​p​o​i​n​t​e​r​ ​t​o​ ​c​h​a​r​
c​h​a​r​ ​*​c​o​n​s​t​ ​s​t​r​i​n​g​2​ ​=​ ​"​b​o​r​k​"​;​ ​ ​/​/​ ​T​h​e​ ​p​o​i​n​t​e​r​ ​i​t​s​e​l​f​ ​i​s​ ​c​o​n​s​t​.​
s​t​r​i​n​g​2​ ​=​ ​"​g​r​e​e​b​l​e​"​;​ ​/​/​ ​e​r​r​o​r​:​ ​a​s​s​i​g​n​m​e​n​t​ ​o​f​ ​r​e​a​d​-​o​n​l​y​ ​v​a​r​i​a​b​l​e​ ​'​s​t​r​i​n​g​2​'​
s​t​r​i​n​g​2​[​0​]​ ​=​ ​'​f​'​;​ ​ ​ ​ ​/​/​ ​T​h​i​s​ ​i​s​ ​o​k​.​

Putting const in both places causes both the pointer and data to be considered read-only:

/​/​ ​c​o​n​s​t​ ​p​o​i​n​t​e​r​ ​t​o​ ​c​o​n​s​t​ ​c​h​a​r​
c​o​n​s​t​ ​c​h​a​r​ ​*​ ​c​o​n​s​t​ ​s​t​r​i​n​g​3​ ​=​ ​"​b​o​r​k​"​;​ ​/​/​ ​P​o​i​n​t​e​r​ ​a​n​d​ ​p​o​i​n​t​e​e​ ​a​r​e​ ​c​o​n​s​t​
s​t​r​i​n​g​3​ ​=​ ​"​g​r​e​e​b​l​e​"​;​ ​/​/​ ​e​r​r​o​r​:​ ​a​s​s​i​g​n​m​e​n​t​ ​o​f​ ​r​e​a​d​-​o​n​l​y​ ​v​a​r​i​a​b​l​e​ ​'​s​t​r​i​n​g​3​'​
s​t​r​i​n​g​3​[​0​]​ ​=​ ​'​f​'​;​ ​ ​ ​ ​/​/​ ​e​r​r​o​r​:​ ​a​s​s​i​g​n​m​e​n​t​ ​o​f​ ​r​e​a​d​-​o​n​l​y​ ​l​o​c​a​t​i​o​n​

Keeping these separate in your brain is easy – look at what the const is next to. If it is next to a data type, then the data is what is considered constant. If const immediately follows the *, then the pointer itself is considered constant.

const does not affect the underlying data – only what you can and cannot do with data accessible through a const-qualified variable. While you can play games with pointers and addresses and casts to alter any variable’s “constant” data, please don’t do this.

volatile is the opposite of const. A variable that is volatile may be changed at any time, independent of what the program is currently doing. The instigator of this change might be an interrupt handler, a signal handler, another thread, or a longjmp() (discussed in Chapter 5: Exceptions, Error Handling, and Signals). Declaring a variable volatile means the value of the variable is reloaded from memory every time it is used. This guarantees that you will have the correct value when you need it, but it also negates some possible compiler optimizations, like caching the value in a register in addition to its location in memory.

Variable argument lists

Variadic function is a fancy name for a function that takes a variable number of arguments. These can provide a flexible and powerful programming interface. The functions belonging to the printf family are variadic functions, which combine one function, a small, expressive command language, and a variable number of arguments leading to an incredibly powerful tool. The stdarg manpage contains the full details on using variable argument lists.

To handle variable arguments in your own functions, you first declare a variable of type va_list, which acts like a pointer to argument values, as shown in Figure 1.3. Initialize it with va_start() giving it the name of the last declared function argument. The va_list now points to the first of the additional arguments.

To get the actual argument values, you call va_arg() with the type of data you expect that argument to be. It will return the correct number of bytes for type and advance the va_list to point to the next argument. Keep on calling va_arg(), giving it the expected types, until you are done processing the arguments. Call va_end() to clean up any internal state.

Example 1.7 shows a function that adds up the integers that are passed to it, using zero as a sentinel value to stop processing:

Example 1.7. vararg.m

/​/​ ​v​a​r​a​r​g​.​m​ ​-​-​ ​u​s​e​ ​v​a​r​a​r​g​s​ ​t​o​ ​s​u​m​ ​a​ ​l​i​s​t​ ​o​f​ ​n​u​m​b​e​r​s​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​v​a​r​a​r​g​ ​v​a​r​a​r​g​.​m​

#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​
#​i​m​p​o​r​t​ ​<​s​t​d​a​r​g​.​h​>​

/​/​ ​s​u​m​ ​a​l​l​ ​t​h​e​ ​i​n​t​e​g​e​r​s​ ​p​a​s​s​e​d​ ​i​n​.​ ​ ​S​t​o​p​p​i​n​g​ ​i​f​ ​i​t​'​s​ ​z​e​r​o​
i​n​t​ ​a​d​d​e​m​U​p​ ​(​i​n​t​ ​f​i​r​s​t​N​u​m​,​ ​.​.​.​)​ ​{​
 ​ ​ ​ ​v​a​_​l​i​s​t​ ​a​r​g​s​;​

 ​ ​ ​ ​i​n​t​ ​s​u​m​ ​=​ ​f​i​r​s​t​N​u​m​;​
 ​ ​ ​ ​i​n​t​ ​n​u​m​b​e​r​;​

 ​ ​ ​ ​v​a​_​s​t​a​r​t​ ​(​a​r​g​s​,​ ​f​i​r​s​t​N​u​m​)​;​
 ​ ​ ​ ​w​h​i​l​e​ ​(​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​n​u​m​b​e​r​ ​=​ ​v​a​_​a​r​g​ ​(​a​r​g​s​,​ ​i​n​t​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​u​m​ ​+​=​ ​n​u​m​b​e​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​n​u​m​b​e​r​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​
 ​ ​ ​ ​v​a​_​e​n​d​ ​(​a​r​g​s​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​u​m​;​

}​ ​/​/​ ​a​d​d​e​m​U​p​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​s​u​m​b​o​d​y​;​

 ​ ​ ​ ​s​u​m​b​o​d​y​ ​=​ ​a​d​d​e​m​U​p​ ​(​1​,​ ​2​,​ ​3​,​ ​4​,​ ​5​,​ ​6​,​ ​7​,​ ​8​,​ ​9​,​ ​0​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​s​u​m​ ​o​f​ ​1​.​.​9​ ​i​s​ ​%​d​\​n​"​,​ ​s​u​m​b​o​d​y​)​;​

 ​ ​ ​ ​s​u​m​b​o​d​y​ ​=​ ​a​d​d​e​m​U​p​ ​(​1​,​ ​3​,​ ​5​,​ ​7​,​ ​9​,​ ​1​1​,​ ​0​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​s​u​m​ ​o​f​ ​o​d​d​s​ ​f​r​o​m​ ​1​.​.​1​1​ ​i​s​ ​%​d​\​n​"​,​ ​s​u​m​b​o​d​y​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

Build and run it:

$​ ​.​/​v​a​r​a​r​g​
s​u​m​ ​o​f​ ​1​.​.​9​ ​i​s​ ​4​5​
s​u​m​ ​o​f​ ​o​d​d​s​ ​f​r​o​m​ ​1​.​.​1​1​ ​i​s​ ​3​6​

When you call va_start(), an internal pointer is initialized to point into the call stack at the end of the supplied argument, as illustrated in Figure 1.3. Each time you call va_arg(), it returns the amount of data making up the supplied type and advances the internal pointer to the end of that data. va_arg() uses the supplied data type to determine how far it needs to advance its pointer.

Figure 1.3  Variadic function stack usage

That sounds dangerous – what if the actual argument’s type is a different size than the type you expected? If there are fewer arguments than you expect, how does va_arg() know when to stop? It has no idea. There is no magic that automatically plants a flag at the end of a function’s parameters or communicates the total number and types of arguments that are passed in. Your code will need to know when to stop. You can do this by having some kind of pre-supplied description like the format string passed to printf(). You can use a sentinel value like zero or NULL. [NSArray arrayWithObjects:] uses nil (a zero pointer value) to signal the end of the list of objects to populate the array with.

While this helps with the number of arguments problem, there is unfortunately nothing you can do about the wrong argument type problem. As you can imagine, this can be a major source of run-time errors. If you supply a bad format string to printf() or do not include the terminating sentinel value, the function processing the call stack could wander off into random data, causing a crash or data corruption.

To help make calling variable argument functions safer, gcc lets you tag a function or a method declaration with __attribute__((sentinel)). Then, when your gcc command line includes the -Wformat flag, gcc will emit a warning if you fail to terminate the argument list with a zero pointer value like NULL or nil. This flag also causes gcc to warn of bad calls to printf(). Cocoa programmers can use the symbol NS_REQUIRES_NIL_TERMINATION instead of using the (somewhat confusing) attribute syntax. Example 1.8 shows a string printing function that uses NULL for its sentinel value.

Example 1.8. sentinel.m

/​/​ ​s​e​n​t​i​n​e​l​.​m​ ​-​-​ ​S​h​o​w​ ​_​_​a​t​t​r​i​b​u​t​e​_​_​(​(​s​e​n​t​i​n​e​l​)​)​ ​i​n​ ​a​c​t​i​o​n​

#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​a​r​g​.​h​>​ ​/​/​ ​f​o​r​ ​v​a​_​s​t​a​r​t​(​)​ ​a​n​d​ ​f​r​i​e​n​d​s​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​s​e​n​t​i​n​e​l​ ​s​e​n​t​i​n​e​l​.​m​

v​o​i​d​ ​p​r​i​n​t​S​t​r​i​n​g​s​(​c​h​a​r​ ​*​f​i​r​s​t​,​ ​.​.​.​)​ ​_​_​a​t​t​r​i​b​u​t​e​_​_​(​(​s​e​n​t​i​n​e​l​)​)​;​

v​o​i​d​ ​p​r​i​n​t​S​t​r​i​n​g​s​(​c​h​a​r​ ​*​f​i​r​s​t​,​ ​.​.​.​)​ ​{​
 ​ ​ ​ ​v​a​_​l​i​s​t​ ​a​r​g​s​;​
 ​ ​ ​ ​v​a​_​s​t​a​r​t​ ​(​a​r​g​s​,​ ​f​i​r​s​t​)​;​
 ​ ​ ​ ​c​h​a​r​ ​*​s​t​r​i​n​g​ ​=​ ​f​i​r​s​t​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​s​t​r​i​n​g​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​s​"​,​ ​s​t​r​i​n​g​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​i​n​g​ ​=​ ​v​a​_​a​r​g​ ​(​a​r​g​s​,​ ​c​h​a​r​ ​*​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​v​a​_​e​n​d​ ​(​a​r​g​s​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​n​"​)​;​
}​ ​/​/​ ​p​r​i​n​t​S​t​r​i​n​g​s​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​S​t​r​i​n​g​s​ ​(​"​s​p​i​c​y​"​,​ ​"​p​o​n​y​"​,​ ​"​h​e​a​d​"​,​ ​N​U​L​L​)​;​
 ​ ​ ​ ​p​r​i​n​t​S​t​r​i​n​g​s​ ​(​"​m​a​c​h​i​n​e​"​,​ ​"​t​o​o​l​"​)​;​ ​/​/​ ​s​h​o​u​l​d​ ​w​a​r​n​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

The compiler warns you about the second printStrings() call:

$​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​s​e​n​t​i​n​e​l​ ​s​e​n​t​i​n​e​l​.​m​
s​e​n​t​i​n​e​l​.​m​:​ ​I​n​ ​f​u​n​c​t​i​o​n​ ​'​m​a​i​n​'​:​
s​e​n​t​i​n​e​l​.​m​:​3​1​:​ ​w​a​r​n​i​n​g​:​ ​m​i​s​s​i​n​g​ ​s​e​n​t​i​n​e​l​ ​i​n​ ​f​u​n​c​t​i​o​n​ ​c​a​l​l​

A run of this program on a PowerPC machine or 64-bit Intel machine prints the first string okay, but it crashes with the second call. When run with 32-bit Intel, it kind of works:

$​ ​g​c​c​ ​-​a​r​c​h​ ​i​3​8​6​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​s​e​n​t​i​n​e​l​ ​s​e​n​t​i​n​e​l​.​m​
$​ ​.​/​s​e​n​t​i​n​e​l​
s​p​i​c​y​p​o​n​y​h​e​a​d​
m​a​c​h​i​n​e​t​o​o​l​h​e​a​d​

In this case, the second printStrings() call is using stale data left on the stack after the first call.

gcc also provides an __attribute__((format_arg)) tag you can use for functions that take a printf-style format string.

Some families of variadic functions make it easy for you to add an extra value to them. You might want a version of printf() that takes a debug severity level and only prints out text if the level exceeds some globally set value. To do this, you can write a function to accept the debug level, the format string, and the arguments. You can then examine the debug level, and if it is in the right range, call vprintf(), a version of printf() that takes a va_list rather than a format string and arguments. A leading or trailing “v” is sometimes used for the name of a function or method that takes a va_list, such as vsnprintf() or NSLogv(). Example 1.9 shows a way of performing conditional logging.

Example 1.9. debuglog.m

/​/​ ​d​e​b​u​g​l​o​g​.​m​ ​-​-​ ​a​ ​f​u​n​c​t​i​o​n​ ​f​o​r​ ​c​o​n​d​i​t​i​o​n​a​l​ ​l​o​g​g​i​n​g​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​d​e​b​u​g​l​o​g​ ​d​e​b​u​g​l​o​g​.​m​

#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​
#​i​m​p​o​r​t​ ​<​s​t​d​a​r​g​.​h​>​

i​n​t​ ​g​l​o​b​a​l​L​e​v​e​l​ ​=​ ​5​0​;​

v​o​i​d​ ​d​e​b​u​g​L​o​g​ ​(​i​n​t​ ​l​o​g​L​e​v​e​l​,​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​f​o​r​m​a​t​,​ ​.​.​.​)​
{​
 ​ ​ ​ ​i​f​ ​(​l​o​g​L​e​v​e​l​ ​>​ ​g​l​o​b​a​l​L​e​v​e​l​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​v​a​_​l​i​s​t​ ​a​r​g​s​;​
 ​ ​ ​ ​ ​ ​ ​ ​v​a​_​s​t​a​r​t​ ​(​a​r​g​s​,​ ​f​o​r​m​a​t​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​v​p​r​i​n​t​f​ ​(​f​o​r​m​a​t​,​ ​a​r​g​s​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​v​a​_​e​n​d​ ​(​a​r​g​s​)​;​
 ​ ​ ​ ​}​

}​ ​/​/​ ​d​e​b​u​g​L​o​g​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​
{​
 ​ ​ ​ ​d​e​b​u​g​L​o​g​ ​(​1​0​,​ ​"​t​h​i​s​ ​w​i​l​l​ ​n​o​t​ ​b​e​ ​s​e​e​n​:​ ​%​d​,​ ​%​s​,​ ​%​d​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​0​,​ ​"​h​e​l​l​o​"​,​ ​2​3​)​;​
 ​ ​ ​ ​d​e​b​u​g​L​o​g​ ​(​8​7​,​ ​"​t​h​i​s​ ​s​h​o​u​l​d​ ​b​e​ ​s​e​e​n​:​ ​%​s​,​ ​%​d​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​b​o​r​k​"​,​ ​4​2​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

Compile and then run it:

$​ ​.​/​d​e​b​u​g​l​o​g​
t​h​i​s​ ​s​h​o​u​l​d​ ​b​e​ ​s​e​e​n​:​ ​b​o​r​k​,​ ​4​2​

You can create variable argument methods in Objective-C the same way as in C, so there is no new syntax to learn. Be aware you cannot create NSInvocations that reference variable argument methods.

Example 1.10 has a SomeClass object that has a weird little method that takes an arbitrary number of objects (terminated by nil) and prints out their descriptions.

Example 1.10. describeObjects.m

/​/​ ​d​e​s​c​r​i​b​e​O​b​j​e​c​t​s​.​m​ ​-​-​ ​v​a​r​a​r​g​s​ ​w​i​t​h​ ​O​b​j​e​c​t​i​v​e​-​C​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​d​e​s​c​r​i​b​e​O​b​j​e​c​t​s​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​d​e​s​c​r​i​b​e​O​b​j​e​c​t​s​.​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​D​e​s​c​r​i​b​e​r​ ​:​ ​N​S​O​b​j​e​c​t​
-​ ​(​v​o​i​d​)​ ​d​e​s​c​r​i​b​e​O​b​j​e​c​t​s​:​ ​(​i​d​)​ ​f​i​r​s​t​O​b​j​e​c​t​,​ ​.​.​.​
 ​ ​ ​ ​_​_​a​t​t​r​i​b​u​t​e​_​_​(​(​s​e​n​t​i​n​e​l​)​)​;​

@​e​n​d​ ​/​/​ ​D​e​s​c​r​i​b​e​r​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​D​e​s​c​r​i​b​e​r​

-​ ​(​v​o​i​d​)​ ​d​e​s​c​r​i​b​e​O​b​j​e​c​t​s​:​ ​(​i​d​)​ ​f​i​r​s​t​O​b​j​e​c​t​,​ ​.​.​.​ ​{​
 ​ ​ ​ ​v​a​_​l​i​s​t​ ​a​r​g​s​;​
 ​ ​ ​ ​i​d​ ​o​b​j​ ​=​ ​f​i​r​s​t​O​b​j​e​c​t​;​

 ​ ​ ​ ​v​a​_​s​t​a​r​t​ ​(​a​r​g​s​,​ ​f​i​r​s​t​O​b​j​e​c​t​)​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​o​b​j​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​s​t​r​i​n​g​ ​=​ ​[​o​b​j​ ​d​e​s​c​r​i​p​t​i​o​n​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​t​h​e​ ​d​e​s​c​r​i​p​t​i​o​n​ ​i​s​:​\​n​ ​ ​ ​ ​%​@​"​,​ ​s​t​r​i​n​g​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​o​b​j​ ​=​ ​v​a​_​a​r​g​ ​(​a​r​g​s​,​ ​i​d​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​v​a​_​e​n​d​ ​(​a​r​g​s​)​;​

}​ ​/​/​ ​d​e​s​c​r​i​b​e​O​b​j​e​c​t​s​

@​e​n​d​ ​/​/​ ​D​e​s​c​r​i​b​e​r​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​D​e​s​c​r​i​b​e​r​ ​*​o​b​j​ ​=​ ​[​[​D​e​s​c​r​i​b​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​s​o​m​e​S​t​r​i​n​g​ ​=​ ​@​"​s​o​m​e​S​t​r​i​n​g​"​;​
 ​ ​ ​ ​N​S​N​u​m​b​e​r​ ​*​n​u​m​ ​=​ ​[​N​S​N​u​m​b​e​r​ ​n​u​m​b​e​r​W​i​t​h​I​n​t​:​ ​2​3​]​;​
 ​ ​ ​ ​N​S​D​a​t​e​ ​*​d​a​t​e​ ​=​ ​[​N​S​C​a​l​e​n​d​a​r​D​a​t​e​ ​c​a​l​e​n​d​a​r​D​a​t​e​]​;​

 ​ ​ ​ ​[​o​b​j​ ​d​e​s​c​r​i​b​e​O​b​j​e​c​t​s​:​s​o​m​e​S​t​r​i​n​g​,​ ​n​u​m​,​ ​d​a​t​e​,​ ​n​i​l​]​;​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​(​0​)​;​

}​ ​/​/​ ​m​a​i​n​

Compile and run it:

$​ ​.​/​d​e​s​c​r​i​b​e​O​b​j​e​c​t​s​
2​0​1​1​-​0​1​-​1​7​ ​1​5​:​0​8​:​1​1​.​6​7​8​ ​d​e​s​c​r​i​b​e​O​b​j​e​c​t​s​[​2​8​3​0​:​9​0​3​]​ ​t​h​e​ ​d​e​s​c​r​i​p​t​i​o​n​ ​i​s​:​
 ​ ​ ​ ​s​o​m​e​S​t​r​i​n​g​
2​0​1​1​-​0​1​-​1​7​ ​1​5​:​0​8​:​1​1​.​6​8​0​ ​d​e​s​c​r​i​b​e​O​b​j​e​c​t​s​[​2​8​3​0​:​9​0​3​]​ ​t​h​e​ ​d​e​s​c​r​i​p​t​i​o​n​ ​i​s​:​
 ​ ​ ​ ​2​3​
2​0​1​1​-​0​1​-​1​7​ ​1​5​:​0​8​:​1​1​.​6​8​0​ ​d​e​s​c​r​i​b​e​O​b​j​e​c​t​s​[​2​8​3​0​:​9​0​3​]​ ​t​h​e​ ​d​e​s​c​r​i​p​t​i​o​n​ ​i​s​:​
 ​ ​ ​ ​2​0​1​1​-​0​1​-​1​7​ ​1​5​:​0​8​:​1​1​ ​-​0​5​0​0​

There are other Foundation methods that accept va_lists much like vprintf() does, such as NSString’s -initWithFormat:arguments: and NSPredicate’s +predicateWithFormat:arguments:.

Varargs gotchas

Making assumptions about the sizes of data passed to functions that take a variable number of arguments is a common error. For example:

s​i​z​e​_​t​ ​m​y​s​i​z​e​ ​=​ ​s​o​m​e​v​a​l​u​e​;​
p​r​i​n​t​f​ ​(​"​m​y​s​i​z​e​ ​i​s​ ​%​d​\​n​"​,​ ​m​y​s​i​z​e​)​;​

This code assumes sizeof(size_t) == sizeof(int), which is correct for 32-bit programming (both are four bytes) but not for 64-bit programming where size_t is eight bytes and int is four bytes. The compiler pushes 8 bytes of data onto the call stack, but printf() has been told to expect an int, so it only pulls four bytes off the stack. You will either crash or get bad data if there are subsequent arguments.

One way to fix this is to cast your size_t argument to the type specified in the format string:

p​r​i​n​t​f​ ​(​"​m​y​s​i​z​e​ ​i​s​ ​%​d​\​n​"​,​ ​(​i​n​t​)​m​y​s​i​z​e​)​;​

This can be necessary when dealing with types whose size might vary from platform to platform.

A better way is to ensure your format specifiers use the right types:

p​r​i​n​t​f​ ​(​"​m​y​s​i​z​e​ ​i​s​ ​%​z​u​\​n​"​,​ ​m​y​s​i​z​e​)​;​

Here, z is a length modifier that alters the unsigned conversion specifier u to match the size of the platform-dependent type size_t. Only a few such types have corresponding length modifiers; you can find the list in the printf(3) manpage.

QuietLog

QuietLog() is a function like NSLog() except that it does not prepend the extra information such as process ID and the current time to the subsecond level, greatly reducing the volume of output. What makes the implementation a little more interesting is that we cannot use vprintf() because vprintf() does not understand the Cocoa’s %@ conversion specifier for printing an object’s description. A temporary NSString is created, and then printed, as shown in Example 1.11.

Example 1.11. quietlog.m

/​/​ ​q​u​i​e​t​l​o​g​.​m​ ​-​-​ ​N​S​L​o​g​,​ ​b​u​t​ ​q​u​i​e​t​e​r​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​o​ ​q​u​i​e​t​l​o​g​ ​q​u​i​e​t​l​o​g​.​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

v​o​i​d​ ​Q​u​i​e​t​L​o​g​ ​(​N​S​S​t​r​i​n​g​ ​*​f​o​r​m​a​t​,​ ​.​.​.​)​ ​{​
 ​ ​ ​ ​v​a​_​l​i​s​t​ ​a​r​g​L​i​s​t​;​
 ​ ​ ​ ​v​a​_​s​t​a​r​t​ ​(​a​r​g​L​i​s​t​,​ ​f​o​r​m​a​t​)​;​

 ​ ​ ​ ​/​/​ ​N​S​S​t​r​i​n​g​ ​l​u​c​k​i​l​y​ ​p​r​o​v​i​d​e​s​ ​u​s​ ​w​i​t​h​ ​t​h​i​s​ ​h​a​n​d​y​ ​m​e​t​h​o​d​ ​w​h​i​c​h​
 ​ ​ ​ ​/​/​ ​w​i​l​l​ ​d​o​ ​a​l​l​ ​t​h​e​ ​w​o​r​k​ ​f​o​r​ ​u​s​,​ ​i​n​c​l​u​d​i​n​g​ ​h​a​n​d​l​i​n​g​ ​%​@​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​s​t​r​i​n​g​;​
 ​ ​ ​ ​s​t​r​i​n​g​ ​=​ ​[​[​N​S​S​t​r​i​n​g​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​F​o​r​m​a​t​:​ ​f​o​r​m​a​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​r​g​u​m​e​n​t​s​:​ ​a​r​g​L​i​s​t​]​;​
 ​ ​ ​ ​v​a​_​e​n​d​ ​(​a​r​g​L​i​s​t​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​s​\​n​"​,​ ​[​s​t​r​i​n​g​ ​U​T​F​8​S​t​r​i​n​g​]​)​;​

 ​ ​ ​ ​[​s​t​r​i​n​g​ ​r​e​l​e​a​s​e​]​;​
}​ ​/​/​ ​Q​u​i​e​t​L​o​g​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​N​S​L​o​g​ ​i​s​ ​%​@​"​,​ ​[​N​S​N​u​m​b​e​r​ ​n​u​m​b​e​r​W​i​t​h​I​n​t​:​ ​2​3​]​)​;​
 ​ ​ ​ ​Q​u​i​e​t​L​o​g​ ​(​@​"​Q​u​i​e​t​L​o​g​ ​i​s​ ​%​@​"​,​ ​[​N​S​N​u​m​b​e​r​ ​n​u​m​b​e​r​W​i​t​h​I​n​t​:​ ​4​2​]​)​;​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​(​0​)​;​
}​ ​/​/​ ​m​a​i​n​

And after compiling and running it, you would see something like this:

$​ ​.​/​q​u​i​e​t​l​o​g​
2​0​1​1​-​0​1​-​1​7​ ​1​5​:​1​5​:​1​9​.​0​0​1​ ​q​u​i​e​t​l​o​g​[​2​8​6​8​:​9​0​3​]​ ​N​S​L​o​g​ ​i​s​ ​2​3​
Q​u​i​e​t​L​o​g​ ​i​s​ ​4​2​

Variadic macros

gcc has supported variadic macros for a long time as a gcc-specific extension. C99, the version of the C language specified by an ISO standard released in 1999, has adopted variadic macros into the mainstream language.

As you can tell by the name, these are macros that accept a variable number of arguments. You cannot address individual arguments in your macros, but you can use the symbol __VA_ARGS__ to reference the remaining arguments. Remember this is still textual manipulation; a va_list is not created in the process.

Unfortunately, there is one problem with __VA_ARGS__ when you are writing macros that take an optional number of arguments. You might have a macro that looks like this:

#​d​e​f​i​n​e​ ​T​H​I​N​G​(​s​t​r​i​n​g​,​ ​.​.​.​)​ ​p​r​i​n​t​f​ ​(​s​t​r​i​n​g​,​ ​_​_​V​A​_​A​R​G​S​_​_​)​

If you invoke the THING macro with more than one argument, things work out okay.

T​H​I​N​G​ ​(​"​h​e​l​l​o​ ​%​s​ ​%​s​\​n​"​,​ ​"​t​h​e​r​e​"​,​ ​"​g​e​o​r​g​e​"​)​;​

turns into

p​r​i​n​t​f​ ​(​"​h​e​l​l​o​ ​%​s​ ​%​s​\​n​"​,​ ​"​t​h​e​r​e​"​,​ ​"​g​e​o​r​g​e​"​)​;​

If you do not supply any additional arguments, you get a leftover comma and an accompanying syntax error:

T​H​I​N​G​ ​(​"​h​i​\​n​"​)​;​

turns into

p​r​i​n​t​f​ ​(​"​h​i​\​n​"​,​ ​)​;​

If you foresee this being a problem with your macro, use ##__VA_ARGS__, prepending two pound signs, which causes the preprocessor to eat the preceding comma. With THING defined as

#​d​e​f​i​n​e​ ​T​H​I​N​G​(​s​t​r​i​n​g​,​ ​.​.​.​)​ ​p​r​i​n​t​f​ ​(​s​t​r​i​n​g​,​#​#​ ​_​_​V​A​_​A​R​G​S​_​_​)​

the failing example

T​H​I​N​G​ ​(​"​h​i​\​n​"​)​;​

now expands to

p​r​i​n​t​f​ ​(​"​h​i​\​n​"​)​;​

Example 1.12 is functionally the same as Example 1.9 but uses a macro rather than a function for checking the debug level.

Example 1.12. debuglog-macro.m

/​/​ ​d​e​b​u​g​l​o​g​-​m​a​c​r​o​.​m​ ​-​-​ ​a​ ​m​a​c​r​o​ ​f​o​r​ ​c​o​n​d​i​t​i​o​n​a​l​ ​l​o​g​g​i​n​g​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​d​e​b​u​g​l​o​g​-​m​a​c​r​o​ ​d​e​b​u​g​l​o​g​-​m​a​c​r​o​.​m​

#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​
#​i​m​p​o​r​t​ ​<​s​t​d​a​r​g​.​h​>​

i​n​t​ ​g​l​o​b​a​l​L​e​v​e​l​ ​=​ ​5​0​;​

#​d​e​f​i​n​e​ ​D​E​B​U​G​_​L​O​G​(​l​o​g​L​e​v​e​l​,​ ​f​o​r​m​a​t​,​ ​.​.​.​)​ ​\​
d​o​ ​{​ ​ ​\​
 ​ ​ ​ ​i​f​ ​(​(​l​o​g​L​e​v​e​l​)​ ​>​ ​g​l​o​b​a​l​L​e​v​e​l​)​ ​p​r​i​n​t​f​(​(​f​o​r​m​a​t​)​,​ ​#​#​_​_​V​A​_​A​R​G​S​_​_​)​;​ ​ ​\​
}​ ​w​h​i​l​e​ ​(​0​)​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​D​E​B​U​G​_​L​O​G​ ​(​1​0​,​ ​"​t​h​i​s​ ​w​i​l​l​ ​n​o​t​ ​b​e​ ​s​e​e​n​:​ ​%​d​,​ ​%​s​,​ ​%​d​\​n​"​,​ ​1​0​,​ ​"​h​e​l​l​o​"​,​ ​2​3​)​;​
 ​ ​ ​ ​D​E​B​U​G​_​L​O​G​ ​(​8​7​,​ ​"​t​h​i​s​ ​s​h​o​u​l​d​ ​b​e​ ​s​e​e​n​:​ ​%​s​,​ ​%​d​\​n​"​,​ ​"​b​o​r​k​"​,​ ​4​2​)​;​
 ​ ​ ​ ​D​E​B​U​G​_​L​O​G​ ​(​8​7​,​ ​"​a​n​d​ ​t​h​i​s​ ​s​h​o​u​l​d​ ​b​e​ ​s​e​e​n​\​n​"​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

A sample run is exactly the same as it was before.

$​ ​.​/​d​e​b​u​g​l​o​g​-​m​a​c​r​o​
t​h​i​s​ ​s​h​o​u​l​d​ ​b​e​ ​s​e​e​n​:​ ​b​o​r​k​,​ ​4​2​
a​n​d​ ​t​h​i​s​ ​s​h​o​u​l​d​ ​b​e​ ​s​e​e​n​

Bitwise operations

C lets you manipulate the individual bits in a piece of memory. If you use higher-level toolkits and languages, you can ignore the bitwise operators most of the time. However, the Cocoa API occasionally needs you to assemble and test bit flags, and the Unix API requires it more often. So it is good to be familiar with these operations.

A bit can contain the value of zero or one. A byte is a collection of eight bits, also referred to as “octets.” The examples that follow use the two numbers shown in Figure 1.4. The bit pattern is listed first, then the value in hex, and then in decimal.

Figure 1.4  Two bytes

Bitwise operators

There are four different operations you can perform on bits: AND, OR, XOR, and NOT. You can also shift the bits around in an integral value.

When you AND two bits, the resulting bit is set (one) only if both bits are set. The resulting bit is clear (zero) in all other cases.

Values larger than a single bit are ANDed bit-by-bit; this is the bitwise element of the bitwise AND operator. You can imagine stacking the values’ bit representations and then ANDing the columns, as shown in Figure 1.5.

Figure 1.5  Bitwise AND

When you OR two values, the bits in the result are set wherever either of the original two values has a bit set or where both bits are set. Conversely, the resulting bits are clear only if both bits are zero.

Figure 1.6  Bitwise OR

XOR, “exclusive or,” uses the ^ (hat or caret) operator. When you XOR two values, the bit is set in the result only if the corresponding bit is set in one value or the other value but not both. The resulting bit is clear if both bits are set or if both bits are clear. You can think of XOR like the question “Would you like soup or salad with that?” at a restaurant. You can have one or the other, but not both (unless you pay extra).

Figure 1.7  Bitwise XOR

NOT complements (flips) the value of each of the bits using the ~ (tilde, twiddle) operator. What was zero becomes one, what was one becomes zero.

Figure 1.8  Bitwise NOT

There are two shifting operators in C. << shifts the bits of an integral value to the left a specified number of places, filling in the low bits with zeros and dropping the high bits on the floor. The >> operator shifts the bits a specified number of places to the right. The behavior of what gets shifted-in for the higher bits is implementation defined.

Back in the old days, shifts were used as optimizations for multiplication and division by two, but there are a number of corner cases with signed values that can make them dangerous to use. These days, the most common use for bit shifting is using the left shift to position a one-bit at a particular location, as shown in Figure 1.9. You can see the one-bit being shifted four positions to the left, having a final value of 16.

Figure 1.9  Bitwise shift

Setting and clearing bits

You can use AND, OR, NOT, and shifting to compactly store information in integers by attaching meaning to specific bit positions. For example, the NSEnumerationOptions specifies two bit flags:

e​n​u​m​ ​{​
 ​ ​ ​ ​N​S​E​n​u​m​e​r​a​t​i​o​n​C​o​n​c​u​r​r​e​n​t​ ​=​ ​(​1​U​L​ ​<​<​ ​0​)​,​ ​ ​ ​/​/​ ​b​i​n​a​r​y​ ​0​0​0​1​
 ​ ​ ​ ​N​S​E​n​u​m​e​r​a​t​i​o​n​R​e​v​e​r​s​e​ ​=​ ​(​1​U​L​ ​<​<​ ​1​)​,​ ​ ​ ​ ​ ​ ​/​/​ ​b​i​n​a​r​y​ ​0​0​1​0​
}​;​
t​y​p​e​d​e​f​ ​N​S​U​I​n​t​e​g​e​r​ ​N​S​E​n​u​m​e​r​a​t​i​o​n​O​p​t​i​o​n​s​;​

When presenting enumeration options to a call such as -enumerateObjectsWithOptions:usingBlock:, you set the zeroth bit to one to indicate you want a concurrent enumeration and set the first bit to one to indicate you want the enumeration to happen in reverse. There are four combinations: all clear (non-concurrent, forward), either one set, or both set (concurrently backwards).

This is commonly called “bitmasking” – a constant such as NSEnumerationConcurrent is used as a mask on a collection of bits, hiding the bits we are not interested in.

The usual way to use bitmasks is by defining constants for each bit (or set of bits) with a specific meaning. You can also use a numeric value that you know sets the right bit. The bit to set is defined using either the appropriate integer value:

#​d​e​f​i​n​e​ ​B​I​T​_​P​O​S​I​T​I​O​N​ ​8​ ​ ​ ​ ​/​/​ ​0​0​0​0​1​0​0​0​

or using a bit shifting expression:

#​d​e​f​i​n​e​ ​B​I​T​_​P​O​S​I​T​I​O​N​ ​(​1​ ​<​<​ ​3​)​ ​ ​ ​ ​/​/​ ​0​0​0​0​1​0​0​0​

You set a bitmask’s bits using bitwise OR:

f​l​a​g​s​ ​|​=​ ​B​I​T​_​P​O​S​I​T​I​O​N​;​

Figure 1.10  Setting a bit

You test whether a bitmasks bits are set using bitwise AND:

i​f​ ​(​f​l​a​g​s​ ​&​ ​B​I​T​_​P​O​S​I​T​I​O​N​)​ ​{​
 ​ ​ ​ ​/​/​ ​d​o​ ​s​o​m​e​t​h​i​n​g​ ​a​p​p​r​o​p​r​i​a​t​e​
}​

Figure 1.11  Testing a bit

You clear a bitmask’s bits by ANDing the value with the bitmask’s complement:

f​l​a​g​s​ ​&​=​ ​~​B​I​T​_​P​O​S​I​T​I​O​N​;​

This one is a bit trickier. What you need is a new mask that will let through all the original values of the bits of the variable you are masking, except the masked bit.

Figure 1.12  Clearing a bit

When you are using a mask with multiple bits set, it is safer to compare against the mask rather than doing a simple logical test:

#​d​e​f​i​n​e​ ​M​U​L​T​I​_​B​I​T​S​ ​ ​0​x​F​0​ ​ ​/​/​ ​1​1​1​1​0​0​0​0​

And assuming that flags is 0x61 (01100001), the statement

i​f​ ​(​f​l​a​g​s​ ​&​ ​M​U​L​T​I​_​B​I​T​S​)​ ​{​
 ​ ​ ​ ​/​/​ ​d​o​ ​s​o​m​e​t​h​i​n​g​ ​i​f​ ​a​l​l​ ​b​i​t​s​ ​a​r​e​ ​s​e​t​
}​

may be bad code. The result of the bitwise AND is:

Figure 1.13  Testing after bitwise AND

The result is 0x60. This will be treated by the if as a true value, even if only a couple of the bits in MULTI_BITS is set. Correct code would be:

i​f​ ​(​(​f​l​a​g​s​ ​&​ ​M​U​L​T​I​_​B​I​T​S​)​ ​=​=​ ​M​U​L​T​I​_​B​I​T​S​)​ ​{​
 ​ ​ ​ ​/​/​ ​d​o​ ​s​o​m​e​t​h​i​n​g​ ​i​f​ ​a​l​l​ ​b​i​t​s​ ​a​r​e​ ​s​e​t​
}​

Example 1.13 shows a number of bitwise operations.

Example 1.13. bitmask.m

/​/​ ​b​i​t​m​a​s​k​.​m​ ​-​-​ ​p​l​a​y​ ​w​i​t​h​ ​b​i​t​m​a​s​k​s​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​b​i​t​m​a​s​k​ ​b​i​t​m​a​s​k​.​m​

#​i​n​c​l​u​d​e​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​

#​d​e​f​i​n​e​ ​T​H​I​N​G​_​1​_​M​A​S​K​ ​ ​ ​ ​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​0​0​0​0​0​0​0​1​
#​d​e​f​i​n​e​ ​T​H​I​N​G​_​2​_​M​A​S​K​ ​ ​ ​ ​2​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​0​0​0​0​0​0​1​0​
#​d​e​f​i​n​e​ ​T​H​I​N​G​_​3​_​M​A​S​K​ ​ ​ ​ ​4​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​0​0​0​0​0​1​0​0​
#​d​e​f​i​n​e​ ​A​L​L​_​T​H​I​N​G​S​ ​ ​ ​ ​ ​ ​(​T​H​I​N​G​_​1​_​M​A​S​K​ ​|​ ​T​H​I​N​G​_​2​_​M​A​S​K​ ​|​ ​T​H​I​N​G​_​3​_​M​A​S​K​)​ ​/​/​ ​0​0​0​0​0​1​1​1​

#​d​e​f​i​n​e​ ​A​N​O​T​H​E​R​_​M​A​S​K​ ​ ​ ​ ​(​1​ ​<​<​ ​5​)​ ​ ​/​/​ ​0​0​1​0​0​0​0​0​
#​d​e​f​i​n​e​ ​A​N​O​T​H​E​R​_​M​A​S​K​_​2​ ​ ​(​1​ ​<​<​ ​6​)​ ​ ​/​/​ ​0​1​0​0​0​0​0​0​

#​d​e​f​i​n​e​ ​A​L​L​_​A​N​O​T​H​E​R​S​ ​ ​ ​ ​(​A​N​O​T​H​E​R​_​M​A​S​K​ ​|​ ​A​N​O​T​H​E​R​_​M​A​S​K​_​2​)​ ​ ​/​/​ ​0​1​1​0​0​0​0​0​
#​d​e​f​i​n​e​ ​A​L​L​_​U​S​E​F​U​L​_​B​I​T​S​ ​(​A​L​L​_​T​H​I​N​G​S​ ​|​ ​A​L​L​_​A​N​O​T​H​E​R​S​)​ ​ ​ ​ ​ ​ ​/​/​ ​0​1​1​0​0​1​1​1​

s​t​a​t​i​c​ ​v​o​i​d​ ​s​h​o​w​M​a​s​k​V​a​l​u​e​ ​(​i​n​t​ ​v​a​l​u​e​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​n​"​)​;​ ​/​/​ ​s​p​a​c​e​ ​o​u​t​ ​t​h​e​ ​o​u​t​p​u​t​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​v​a​l​u​e​ ​%​x​:​\​n​"​,​ ​v​a​l​u​e​)​;​

 ​ ​ ​ ​i​f​ ​(​v​a​l​u​e​ ​&​ ​T​H​I​N​G​_​1​_​M​A​S​K​)​ ​p​r​i​n​t​f​ ​(​"​ ​ ​T​H​I​N​G​_​1​\​n​"​)​;​
 ​ ​ ​ ​i​f​ ​(​v​a​l​u​e​ ​&​ ​T​H​I​N​G​_​2​_​M​A​S​K​)​ ​p​r​i​n​t​f​ ​(​"​ ​ ​T​H​I​N​G​_​2​\​n​"​)​;​
 ​ ​ ​ ​i​f​ ​(​v​a​l​u​e​ ​&​ ​T​H​I​N​G​_​3​_​M​A​S​K​)​ ​p​r​i​n​t​f​ ​(​"​ ​ ​T​H​I​N​G​_​3​\​n​"​)​;​

 ​ ​ ​ ​i​f​ ​(​v​a​l​u​e​ ​&​ ​A​N​O​T​H​E​R​_​M​A​S​K​)​ ​p​r​i​n​t​f​ ​(​"​ ​ ​A​N​O​T​H​E​R​_​M​A​S​K​\​n​"​)​;​
 ​ ​ ​ ​i​f​ ​(​v​a​l​u​e​ ​&​ ​A​N​O​T​H​E​R​_​M​A​S​K​_​2​)​ ​p​r​i​n​t​f​ ​(​"​ ​ ​A​N​O​T​H​E​R​_​M​A​S​K​_​2​\​n​"​)​;​

 ​ ​ ​ ​i​f​ ​(​(​v​a​l​u​e​ ​&​ ​A​L​L​_​A​N​O​T​H​E​R​S​)​ ​=​=​ ​A​L​L​_​A​N​O​T​H​E​R​S​)​ ​p​r​i​n​t​f​ ​(​"​ ​ ​A​L​L​ ​A​N​O​T​H​E​R​S​\​n​"​)​;​
}​ ​/​/​ ​s​h​o​w​M​a​s​k​V​a​l​u​e​

s​t​a​t​i​c​ ​i​n​t​ ​s​e​t​B​i​t​s​ ​(​i​n​t​ ​v​a​l​u​e​,​ ​i​n​t​ ​m​a​s​k​V​a​l​u​e​)​ ​{​
 ​ ​ ​ ​/​/​ ​S​e​t​ ​a​ ​b​i​t​ ​b​y​ ​j​u​s​t​ ​O​R​-​i​n​g​ ​i​n​ ​a​ ​v​a​l​u​e​.​
 ​ ​ ​ ​v​a​l​u​e​ ​|​=​ ​m​a​s​k​V​a​l​u​e​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​v​a​l​u​e​;​
}​ ​/​/​ ​s​e​t​B​i​t​s​

s​t​a​t​i​c​ ​i​n​t​ ​c​l​e​a​r​B​i​t​s​ ​(​i​n​t​ ​v​a​l​u​e​,​ ​i​n​t​ ​m​a​s​k​V​a​l​u​e​)​ ​{​
 ​ ​ ​ ​/​/​ ​T​o​ ​c​l​e​a​r​ ​a​ ​b​i​t​,​ ​A​N​D​ ​i​t​ ​w​i​t​h​ ​t​h​e​ ​c​o​m​p​l​e​m​e​n​t​ ​o​f​ ​t​h​e​ ​m​a​s​k​.​

 ​ ​ ​ ​v​a​l​u​e​ ​&​=​ ​~​m​a​s​k​V​a​l​u​e​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​v​a​l​u​e​;​
}​ ​/​/​ ​c​l​e​a​r​B​i​t​s​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​i​n​t​v​a​l​ ​=​ ​0​;​

 ​ ​ ​ ​i​n​t​v​a​l​ ​=​ ​s​e​t​B​i​t​s​ ​(​i​n​t​v​a​l​,​ ​T​H​I​N​G​_​1​_​M​A​S​K​)​;​ ​ ​ ​ ​/​/​ ​0​0​0​0​0​0​0​1​ ​=​ ​0​x​0​1​
 ​ ​ ​ ​i​n​t​v​a​l​ ​=​ ​s​e​t​B​i​t​s​ ​(​i​n​t​v​a​l​,​ ​T​H​I​N​G​_​3​_​M​A​S​K​)​;​ ​ ​ ​ ​/​/​ ​0​0​0​0​0​1​0​1​ ​=​ ​0​x​0​5​
 ​ ​ ​ ​s​h​o​w​M​a​s​k​V​a​l​u​e​ ​(​i​n​t​v​a​l​)​;​

 ​ ​ ​ ​i​n​t​v​a​l​ ​=​ ​s​e​t​B​i​t​s​ ​(​i​n​t​v​a​l​,​ ​A​L​L​_​A​N​O​T​H​E​R​S​)​;​ ​ ​ ​ ​/​/​ ​0​1​1​0​0​1​0​1​ ​=​ ​0​x​6​5​
 ​ ​ ​ ​i​n​t​v​a​l​ ​=​ ​c​l​e​a​r​B​i​t​s​ ​(​i​n​t​v​a​l​,​ ​T​H​I​N​G​_​2​_​M​A​S​K​)​;​ ​ ​/​/​ ​0​1​1​0​0​1​0​1​ ​=​ ​0​x​6​5​
 ​ ​ ​ ​i​n​t​v​a​l​ ​=​ ​c​l​e​a​r​B​i​t​s​ ​(​i​n​t​v​a​l​,​ ​T​H​I​N​G​_​3​_​M​A​S​K​)​;​ ​ ​/​/​ ​0​1​1​0​0​0​0​1​ ​=​ ​0​x​6​1​
 ​ ​ ​ ​s​h​o​w​M​a​s​k​V​a​l​u​e​ ​(​i​n​t​v​a​l​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

When you compile and run, you get this output:

%​ ​.​/​b​i​t​m​a​s​k​
v​a​l​u​e​ ​5​:​
 ​ ​T​H​I​N​G​_​1​
 ​ ​T​H​I​N​G​_​3​

v​a​l​u​e​ ​6​1​:​
 ​ ​T​H​I​N​G​_​1​
 ​ ​A​N​O​T​H​E​R​_​M​A​S​K​
 ​ ​A​N​O​T​H​E​R​_​M​A​S​K​
 ​ ​A​L​L​ ​A​N​O​T​H​E​R​S​

Objective-C

Objective-C is the language used to program the Cocoa toolkit. It is a thin layer on top of C with a little additional syntax, a runtime component, and a big pile of metadata to support the dynamic nature of the language. But fundamentally, Objective-C is still C. Many programmers forget Objective-C’s C heritage and fail to take advantage of C’s language features and libraries.

C callbacks in Objective-C

A question that comes up fairly frequently in online forums is, “How do I put a method into a function pointer?” Nine times out of ten, the poster actually wants to use Cocoa with an existing C API that uses callbacks. A much better question is, “I’m using a C API that uses callbacks with Cocoa, how do I make it work?”

Even though Objective-C methods are fundamentally C functions, you cannot use Objective-C methods as callbacks for a C API. When you send a message to an object, the compiler replaces your square-bracket method call with a function call to objc_msgSend() or one of its variants. objc_msgSend() is prototyped like this:

id objc_msgSend(id self, SEL op, ...);

When you make a call like

[​b​o​x​ ​d​r​a​w​I​n​R​e​c​t​:​ ​s​o​m​e​R​e​c​t​]​

it gets turned by the compiler into

o​b​j​c​_​m​s​g​S​e​n​d​ ​(​b​o​x​,​ ​@​s​e​l​e​c​t​o​r​(​d​r​a​w​I​n​R​e​c​t​:​)​,​ ​s​o​m​e​R​e​c​t​)​;​

It passes the receiver of the message as the first argument. This becomes the self pointer inside of the method. It passes the selector as the second argument, which is accessible with _cmd. The existence of these two hidden arguments is what prevents you from using methods for callbacks.

Using objects in callbacks requires using a C function as a trampoline to bounce off of. Most C callback APIs allow you to specify some kind of pointer-sized rock to hide some data under, whether it’s called userData, info, refcon, or context.

For example, the Core Foundation framework provides an XML parser that is based on using C callbacks. Programmers build their own data structures using information the callbacks supply about the XML file’s content. Suppose we have a Watcher object that wants its -watchCreateStructure: method to be triggered whenever CFXML invokes the callback.

You would set up your context to include your Objective-C object:

W​a​t​c​h​e​r​ ​*​w​a​t​c​h​e​r​ ​=​ ​[​[​W​a​t​c​h​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
C​F​X​M​L​P​a​r​s​e​r​C​o​n​t​e​x​t​ ​c​o​n​t​e​x​t​ ​=​ ​{​ ​0​,​ ​w​a​t​c​h​e​r​,​ ​N​U​L​L​,​ ​N​U​L​L​,​ ​N​U​L​L​ ​}​;​

Set up your callbacks to include the C functions:

C​F​X​M​L​P​a​r​s​e​r​C​a​l​l​B​a​c​k​s​ ​c​a​l​l​b​a​c​k​s​ ​=​ ​{​ ​0​,​ ​c​r​e​a​t​e​S​t​r​u​c​t​u​r​e​,​ ​a​d​d​C​h​i​l​d​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​d​S​t​r​u​c​t​u​r​e​,​ ​r​e​s​o​l​v​e​E​n​t​i​t​y​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​a​n​d​l​e​E​r​r​o​r​ ​}​;​

And then create your parser (notice the passing of the context for the last parameter):

p​a​r​s​e​r​ ​=​ ​C​F​X​M​L​P​a​r​s​e​r​C​r​e​a​t​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​ ​(​C​F​D​a​t​a​R​e​f​)​x​m​l​D​a​t​a​,​ ​N​U​L​L​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​C​F​X​M​L​P​a​r​s​e​r​A​l​l​O​p​t​i​o​n​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​C​F​X​M​L​N​o​d​e​C​u​r​r​e​n​t​V​e​r​s​i​o​n​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​&​c​a​l​l​b​a​c​k​s​,​ ​&​c​o​n​t​e​x​t​)​;​

The CFXMLParser will grovel through the XML and call the callbacks as needed. From there, you can cast the info pointer into an object pointer and call a method. For instance,

v​o​i​d​ ​*​c​r​e​a​t​e​S​t​r​u​c​t​u​r​e​ ​(​C​F​X​M​L​P​a​r​s​e​r​R​e​f​ ​p​a​r​s​e​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​X​M​L​N​o​d​e​R​e​f​ ​n​o​d​e​,​ ​v​o​i​d​ ​*​i​n​f​o​)​ ​{​
 ​ ​ ​ ​W​a​t​c​h​e​r​ ​*​w​a​t​c​h​e​r​ ​=​ ​(​W​a​t​c​h​e​r​ ​*​)​ ​i​n​f​o​;​
 ​ ​ ​ ​[​w​a​t​c​h​e​r​ ​w​a​t​c​h​C​r​e​a​t​e​S​t​r​u​c​t​u​r​e​:​ ​n​o​d​e​]​;​

 ​ ​ ​ ​/​/​ ​.​.​.​ ​d​o​ ​s​o​m​e​ ​w​o​r​k​ ​.​.​.​
 ​ ​ ​ ​r​e​t​u​r​n​ ​n​e​w​X​M​L​S​t​r​u​c​t​u​r​e​D​e​a​l​i​e​;​
}​ ​/​/​ ​c​r​e​a​t​e​S​t​r​u​c​t​u​r​e​

Alternatively, you could create an NSInvocation or a block and use that for your context pointer. Then you can have a generic callback that just casts the incoming context pointer to an invocation or a block and then executes it.

Objective-C 2.0

Mac OS X 10.5 included a 2.0 update to the Objective-C language. While relatively minor, the additional features improve programmer efficiency and program reliability by eliminating a lot of boilerplate code and by moving common Cocoa idioms into the compiler where they can be sanity-checked and optimized. The new features include additions to protocols, class extensions, a new enumeration syntax, enhancements to the runtime, garbage collection, and properties. You cannot use these new features if you have to support 10.4. You have to use the 10.5 SDK or later to get Objective-C 2.0.

Protocols

An Objective-C protocol is similar to a Java interface: a list of methods that a class adopting the protocol must implement. The compiler will complain if any methods are left unimplemented. This can be inconvenient if you want to require some methods and have others be optional. For example, the NSInputServiceProvider protocol has 15 methods. If you adopt this protocol, you have to implement all 15 methods, even if you only need one or two to get your work done.

Informal protocols can be used to work around this annoyance. An informal protocol is a category on NSObject that lists the methods that might (or might not) be implemented. This solves the “thou shalt implement everything” problem, but it causes problems when there are methods that really do need to be implemented. The NSTableDataSource category includes two methods you must implement: -numberOfRowsInTableView: and -tableView:objectValueForTableColumn:row:. Informal protocols do not require you to implement these, so NSTableView relies on runtime checks on its data sources to make sure these are implemented.

Objective-C 2.0 introduces a better solution with two new directives for use within protocols: @required and @optional. @required precedes the declaration of methods that must be implemented. Protocol methods default to being @required. @optional flags methods that do not have to be implemented. If you were implementing something like the table view data source, you would create something like Example 1.14:

Example 1.14. datasource.m

/​/​ ​d​a​t​a​s​o​u​r​c​e​.​m​ ​-​-​ ​l​o​o​k​ ​a​t​ ​O​b​j​C​2​ ​p​r​o​t​o​c​o​l​ ​a​d​d​i​t​i​o​n​s​.​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​o​ ​d​a​t​a​s​o​u​r​c​e​ ​d​a​t​a​s​o​u​r​c​e​.​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

/​/​ ​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
/​/​ ​D​a​t​a​s​o​u​r​c​e​ ​f​o​r​ ​t​h​e​ ​n​e​w​ ​t​a​b​l​e​ ​v​i​e​w​

@​p​r​o​t​o​c​o​l​ ​N​e​w​T​a​b​l​e​V​i​e​w​D​a​t​a​S​o​u​r​c​e​
-​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​r​o​w​C​o​u​n​t​;​ ​/​/​ ​d​e​f​a​u​l​t​s​ ​t​o​ ​b​e​i​n​g​ ​r​e​q​u​i​r​e​d​

@​o​p​t​i​o​n​a​l​
-​ ​(​B​O​O​L​)​ ​s​h​o​u​l​d​E​n​c​h​e​f​e​r​i​z​e​S​t​r​i​n​g​s​;​
-​ ​(​N​S​I​n​d​e​x​S​e​t​ ​*​)​ ​e​m​p​t​y​R​o​w​s​;​

@​r​e​q​u​i​r​e​d​
-​ ​(​i​d​)​ ​d​a​t​a​V​a​l​u​e​A​t​R​o​w​:​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​r​o​w​;​
@​e​n​d​ ​/​/​ ​N​e​w​T​a​b​l​e​V​i​e​w​D​a​t​a​S​o​u​r​c​e​ ​p​r​o​t​o​c​o​l​

/​/​ ​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
/​/​ ​a​ ​d​a​t​a​s​o​u​r​c​e​ ​c​l​a​s​s​

@​i​n​t​e​r​f​a​c​e​ ​D​a​t​a​S​o​u​r​c​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​<​N​e​w​T​a​b​l​e​V​i​e​w​D​a​t​a​S​o​u​r​c​e​>​
@​e​n​d​ ​/​/​ ​D​a​t​a​S​o​u​r​c​e​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​D​a​t​a​S​o​u​r​c​e​

-​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​r​o​w​C​o​u​n​t​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​2​3​;​
}​ ​/​/​ ​r​o​w​C​o​u​n​t​

-​ ​(​i​d​)​ ​d​a​t​a​V​a​l​u​e​A​t​R​o​w​:​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​r​o​w​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​N​S​N​u​m​b​e​r​ ​n​u​m​b​e​r​W​i​t​h​I​n​t​:​ ​r​o​w​ ​*​ ​7​]​;​
}​ ​/​/​ ​d​a​t​a​V​a​l​u​e​A​t​R​o​w​

-​ ​(​B​O​O​L​)​ ​s​h​o​u​l​d​E​n​c​h​e​f​e​r​i​z​e​S​t​r​i​n​g​s​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​Y​E​S​;​ ​/​/​ ​b​o​r​k​ ​b​o​r​k​ ​b​o​r​k​
}​ ​/​/​ ​s​h​o​u​l​d​ ​E​n​c​h​e​f​e​r​i​z​e​S​t​r​i​n​g​s​

@​e​n​d​ ​/​/​ ​D​a​t​a​S​o​u​r​c​e​

/​/​ ​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
/​/​ ​t​h​e​ ​n​e​w​ ​t​a​b​l​e​ ​v​i​e​w​

@​i​n​t​e​r​f​a​c​e​ ​N​e​w​T​a​b​l​e​V​i​e​w​ ​:​ ​N​S​O​b​j​e​c​t​ ​{​
 ​ ​ ​ ​i​d​ ​d​a​t​a​s​o​u​r​c​e​;​
}​

-​ ​(​v​o​i​d​)​ ​s​e​t​D​a​t​a​S​o​u​r​c​e​:​ ​(​i​d​ ​<​N​e​w​T​a​b​l​e​V​i​e​w​D​a​t​a​S​o​u​r​c​e​>​)​ ​d​s​;​
-​ ​(​v​o​i​d​)​ ​d​o​S​t​u​f​f​;​

@​e​n​d​ ​/​/​ ​N​e​w​T​a​b​l​e​V​i​e​w​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​N​e​w​T​a​b​l​e​V​i​e​w​

-​ ​(​v​o​i​d​)​ ​s​e​t​D​a​t​a​S​o​u​r​c​e​:​ ​(​i​d​ ​<​N​e​w​T​a​b​l​e​V​i​e​w​D​a​t​a​S​o​u​r​c​e​>​)​ ​d​s​ ​{​
 ​ ​ ​ ​d​a​t​a​s​o​u​r​c​e​ ​=​ ​d​s​;​
}​ ​/​/​ ​s​e​t​D​a​t​a​S​o​u​r​c​e​

-​ ​(​v​o​i​d​)​ ​d​o​S​t​u​f​f​ ​{​
 ​ ​ ​ ​/​/​ ​D​o​n​'​t​ ​n​e​e​d​ ​t​o​ ​c​h​e​c​k​ ​f​o​r​ ​r​e​s​p​o​n​d​s​T​o​S​e​l​e​c​t​o​r​ ​-​ ​t​h​e​ ​c​o​m​p​i​l​e​r​ ​d​o​e​s​
 ​ ​ ​ ​/​/​ ​s​o​m​e​ ​s​a​n​i​t​y​ ​c​h​e​c​k​i​n​g​ ​f​o​r​ ​u​s​.​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​r​o​w​C​o​u​n​t​:​ ​%​u​"​,​ ​[​d​a​t​a​s​o​u​r​c​e​ ​r​o​w​C​o​u​n​t​]​)​;​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​v​a​l​u​e​ ​a​t​ ​r​o​w​ ​5​:​ ​%​@​"​,​ ​[​d​a​t​a​s​o​u​r​c​e​ ​d​a​t​a​V​a​l​u​e​A​t​R​o​w​:​ ​5​]​)​;​

 ​ ​ ​ ​/​/​ ​T​h​e​s​e​ ​a​r​e​ ​o​p​t​i​o​n​a​l​,​ ​s​o​ ​c​h​e​c​k​ ​t​h​a​t​ ​t​h​e​ ​d​a​t​a​s​o​u​r​c​e​ ​r​e​s​p​o​n​d​s​ ​t​o​
 ​ ​ ​ ​/​/​ ​t​h​e​m​.​
 ​ ​ ​ ​i​f​ ​(​[​d​a​t​a​s​o​u​r​c​e​ ​r​e​s​p​o​n​d​s​T​o​S​e​l​e​c​t​o​r​:​ ​@​s​e​l​e​c​t​o​r​(​s​h​o​u​l​d​E​n​c​h​e​f​e​r​i​z​e​S​t​r​i​n​g​s​)​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​b​o​r​k​ ​b​o​r​k​ ​b​o​r​k​?​ ​%​@​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​d​a​t​a​s​o​u​r​c​e​ ​s​h​o​u​l​d​E​n​c​h​e​f​e​r​i​z​e​S​t​r​i​n​g​s​]​ ​?​ ​@​"​Y​E​S​"​ ​:​ ​@​"​N​O​"​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​i​f​ ​(​[​d​a​t​a​s​o​u​r​c​e​ ​r​e​s​p​o​n​d​s​T​o​S​e​l​e​c​t​o​r​:​ ​@​s​e​l​e​c​t​o​r​(​e​m​p​t​y​R​o​w​s​)​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​t​h​e​ ​e​m​p​t​y​ ​r​o​w​s​:​ ​%​@​"​,​ ​[​d​a​t​a​s​o​u​r​c​e​ ​e​m​p​t​y​R​o​w​s​]​)​;​
 ​ ​ ​ ​}​

}​ ​/​/​ ​d​o​S​t​u​f​f​

@​e​n​d​ ​/​/​ ​N​e​w​T​a​b​l​e​V​i​e​w​

/​/​ ​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
/​/​ ​U​s​e​ ​e​v​e​r​y​t​h​i​n​g​.​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​N​e​w​T​a​b​l​e​V​i​e​w​ ​*​t​a​b​l​e​v​i​e​w​ ​=​ ​[​[​N​e​w​T​a​b​l​e​V​i​e​w​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​D​a​t​a​S​o​u​r​c​e​ ​*​d​s​ ​=​ ​[​[​D​a​t​a​S​o​u​r​c​e​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​[​t​a​b​l​e​v​i​e​w​ ​s​e​t​D​a​t​a​S​o​u​r​c​e​:​ ​[​N​S​N​u​l​l​ ​n​u​l​l​]​]​;​ ​/​/​ ​s​h​o​u​l​d​ ​w​a​r​n​
 ​ ​ ​ ​[​t​a​b​l​e​v​i​e​w​ ​s​e​t​D​a​t​a​S​o​u​r​c​e​:​ ​d​s​]​;​
 ​ ​ ​ ​[​t​a​b​l​e​v​i​e​w​ ​d​o​S​t​u​f​f​]​;​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

Example 1.14 starts out by declaring the NewTableViewDataSource protocol with two required methods (-rowCount and -dataValueAtRow:) and two optional methods (-shouldEncheferizeStrings and -emptyRows). Following that is the DataSource class that adopts the new protocol. The compiler will warn if you do not implement either of the required methods. Only one of the optional methods is implemented.

The next class is the NewTableView class with the datasource instance variable, whose setter requires that the object given to -setDataSource: adopt the datasource protocol. The compiler will warn you if you try to pass in an object that does not adopt this protocol. This guarantees (assuming you don’t ignore the warnings) that your data source implements what it has to and that you do not give the new table view something that will not handle the required messages. That simplifies the implementation of the -doStuff method, which can assume its datasource implements the required methods. You still need to check at runtime for the presence of the optional methods.

When you compile the program, you should get this warning for the line trying to feed a bad data source to the tableview:

d​a​t​a​s​o​u​r​c​e​.​m​:​ ​I​n​ ​f​u​n​c​t​i​o​n​ ​'​m​a​i​n​'​:​
d​a​t​a​s​o​u​r​c​e​.​m​:​9​2​:​ ​w​a​r​n​i​n​g​:​ ​c​l​a​s​s​ ​'​N​S​N​u​l​l​'​ ​d​o​e​s​ ​n​o​t​ ​i​m​p​l​e​m​e​n​t​
 ​ ​ ​ ​t​h​e​ ​'​N​e​w​T​a​b​l​e​V​i​e​w​D​a​t​a​S​o​u​r​c​e​'​ ​p​r​o​t​o​c​o​l​

And running it should give you output like this:

$​ ​.​/​d​a​t​a​s​o​u​r​c​e​
2​0​1​1​-​0​1​-​1​8​ ​1​2​:​3​9​:​4​3​.​2​1​1​ ​d​a​t​a​s​o​u​r​c​e​[​4​4​8​8​:​9​0​3​]​ ​r​o​w​C​o​u​n​t​:​ ​2​3​
2​0​1​1​-​0​1​-​1​8​ ​1​2​:​3​9​:​4​3​.​2​1​3​ ​d​a​t​a​s​o​u​r​c​e​[​4​4​8​8​:​9​0​3​]​ ​v​a​l​u​e​ ​a​t​ ​r​o​w​ ​5​:​ ​3​5​
2​0​1​1​-​0​1​-​1​8​ ​1​2​:​3​9​:​4​3​.​2​1​4​ ​d​a​t​a​s​o​u​r​c​e​[​4​4​8​8​:​9​0​3​]​ ​b​o​r​k​ ​b​o​r​k​ ​b​o​r​k​?​ ​Y​E​S​

Class extensions

Another Objective-C convention is using categories to declare methods outside of a class @interface. The two main reasons programmers do this are 1) to split up a long method list into smaller, easier to understand chunks, such as NSWindow’s NSKeyboardUI, NSToolbarSupport, and NSDrag categories, and 2) to declare “private” methods, in either a different header file or at the top of a class implementation file. The problem with using a category is that the compiler cannot check to make sure you actually implement what you say you will. For example, you could have a category like this in your class header file:

@​i​n​t​e​r​f​a​c​e​ ​B​i​g​S​h​o​w​(​S​l​i​d​e​M​a​n​i​p​u​l​a​t​i​o​n​)​
-​ ​(​v​o​i​d​)​ ​m​o​v​e​T​o​N​e​x​t​S​l​i​d​e​;​
-​ ​(​i​n​t​)​ ​c​u​r​r​e​n​t​S​l​i​d​e​I​n​d​e​x​;​
@​e​n​d​

But your implementation might have a typo:

-​ ​(​i​n​t​)​ ​c​u​r​r​e​n​t​S​l​i​d​e​I​N​d​e​x​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​i​n​d​e​x​;​
}​ ​/​/​ ​c​u​r​r​e​n​t​S​l​i​d​e​I​n​d​e​x​

This error will only be caught at runtime and might not be caught until you have shipped your application to customers.

Objective-C 2.0 added class extensions to address this. A class extension is declared as a nameless category:

@​i​n​t​e​r​f​a​c​e​ ​B​i​g​S​h​o​w​ ​(​)​
-​ ​(​v​o​i​d​)​ ​m​o​v​e​T​o​N​e​x​t​S​l​i​d​e​;​
-​ ​(​i​n​t​)​ ​c​u​r​r​e​n​t​S​l​i​d​e​I​n​d​e​x​;​
@​e​n​d​

This tells the compiler that these methods are part of the class, even though they are specified in a different place. The compiler will issue a warning if you do not provide an implementation for these methods in the class @implementation block.

Some programmers refer to class extensions as “class continuations.” Continuation, though, is the name of another (more interesting) computer science concept, so it is best to refer to class extensions by their proper name.

In addition to forward-declaring methods in a class extension, you can also adopt protocols in them. Snow Leopard and iOS have moved many informal protocols into protocols and require your class to adopt those protocols before you can use your object as a delegate or a datasource. You can either list the protocol adoption in your header file:

@​i​n​t​e​r​f​a​c​e​ ​G​r​o​o​v​y​C​o​n​t​r​o​l​l​e​r​ ​:​ ​U​I​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​<​U​I​T​a​b​l​e​V​i​e​w​D​a​t​a​S​o​u​r​c​e​P​r​o​t​o​c​o​l​>​

or in a class extension in your implementation:

@​i​n​t​e​r​f​a​c​e​ ​G​r​o​o​v​y​C​o​n​t​r​o​l​l​e​r​ ​(​)​ ​<​U​I​T​a​b​l​e​V​i​e​w​D​a​t​a​S​o​u​r​c​e​P​r​o​t​o​c​o​l​>​

By using the class extension, you move implementation details out of your header. This can also reduce compile time if your own classes make heavy use of protocols, where the adoption is just an implementation detail and not really part of the public interface. If you adopt a protocol (such as the NewTableViewDataSource protocol seen earlier) in your header file, you must include the data source header that defines the protocol, leading to longer compile times for every file that includes the original object’s header.

Fast enumeration

One of the most common Cocoa idioms is iterating through a collection. Typical enumeration code looks like this:

N​S​E​n​u​m​e​r​a​t​o​r​ ​*​e​n​u​m​e​r​a​t​o​r​ ​=​ ​[​a​r​r​a​y​ ​o​b​j​e​c​t​E​n​u​m​e​r​a​t​o​r​]​;​
N​S​S​t​r​i​n​g​ ​*​s​t​r​i​n​g​;​
w​h​i​l​e​ ​(​(​s​t​r​i​n​g​ ​=​ ​[​e​n​u​m​e​r​a​t​o​r​ ​n​e​x​t​O​b​j​e​c​t​]​)​)​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​%​@​"​,​ ​s​t​r​i​n​g​)​;​
}​

This is rather verbose, requires memory allocation for the enumerator, and might involve a copy of the collection in some circumstances. Iterating through an array with -objectAtIndex: is somewhat faster, but it ties your loop to NSArray, making future maintenance more difficult if you change collection classes.

Objective-C 2.0 adopts the “for-in” syntax found in many scripting languages for iterating through a collection, which is equivalent to the previous code.

f​o​r​ ​(​N​S​S​t​r​i​n​g​ ​*​s​t​r​i​n​g​ ​i​n​ ​a​r​r​a​y​)​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​%​@​"​,​ ​s​t​r​i​n​g​)​;​
}​

The string variable is scoped to the loop. If you declared string outside of the loop, its value would be visible outside of the loop. If the iteration went through the whole collection, string would be nil when the loop exits. You can also supply an NSEnumerator instead of a collection:

N​S​E​n​u​m​e​r​a​t​o​r​ ​*​e​n​u​m​e​r​a​t​o​r​ ​=​ ​[​a​r​r​a​y​ ​o​b​j​e​c​t​E​n​u​m​e​r​a​t​o​r​]​;​
f​o​r​ ​(​N​S​S​t​r​i​n​g​ ​*​s​t​r​i​n​g​ ​i​n​ ​e​n​u​m​e​r​a​t​o​r​)​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​%​@​"​,​ ​s​t​r​i​n​g​)​;​
}​

The for-in syntax is not only more compact; it is also significantly faster than other collection iteration methods. It takes advantage of the new NSFastEnumeration protocol, which lets the collection being iterated leverage its implementation details to provide the best performance possible. In addition, for-in will raise an exception if you mutate the collection while iterating through it, which has long been a Cocoa invariant, though one previously not always enforced.

To implement your own fast enumerable collection, you need to adopt the NSFastEnumeration protocol. It has only one method:

- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState *) state
                                   objects: (id *) stackbuf
                                     count: (NSUInteger) len;

stackbuf is a pointer to an array of len object pointers on the stack, which the method can fill in but is not required to. The method returns the number of items supplied for this round of iteration or zero if iteration is finished.

The state parameter is a pointer to a structure that describes the current state of the enumeration:

t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​{​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​s​t​a​t​e​;​
 ​ ​ ​ ​i​d​ ​*​i​t​e​m​s​P​t​r​;​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​*​m​u​t​a​t​i​o​n​s​P​t​r​;​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​e​x​t​r​a​[​5​]​;​
}​ ​N​S​F​a​s​t​E​n​u​m​e​r​a​t​i​o​n​S​t​a​t​e​;​

The state field is zero the first time -countByEnumeratingWithState:objects:count: is called. You should set this to a non-zero value during the first call so you can tell when you are in a second (or subsequent) call.

You return objects for iteration in the array pointed to by itemsPtr. You can point this into the collection’s data structures if the collection stores object pointers linearly in memory. Otherwise, you can put as many as len object pointers into the stackbuf parameter and point itemsPtr there. The mutationsPtr should be set to a valid address that functions as some kind of change counter for the object to catch the case of iterating over a mutating collection; immutable collections can just use self. The extra values are scratch space available for your -countByEnumeratingWithState:objects:count: implementation’s use.

Here is a class that stores a C array of NSStrings. The full text of this program can be found in fastenum.m in the sample code download, which includes the implementation of the variadic method and the code that maintains the mutations value.

@​i​n​t​e​r​f​a​c​e​ ​C​A​r​r​a​y​ ​:​ ​N​S​O​b​j​e​c​t​ ​<​N​S​F​a​s​t​E​n​u​m​e​r​a​t​i​o​n​>​ ​{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​*​_​s​t​r​i​n​g​s​;​
 ​ ​ ​ ​i​n​t​ ​_​s​t​r​i​n​g​C​o​u​n​t​;​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​_​m​u​t​a​t​i​o​n​s​;​
}​

-​ ​(​v​o​i​d​)​ ​s​e​t​S​t​r​i​n​g​s​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​s​t​r​i​n​g​,​ ​.​.​.​ ​N​S​_​R​E​Q​U​I​R​E​S​_​N​I​L​_​T​E​R​M​I​N​A​T​I​O​N​;​

@​e​n​d​ ​/​/​ ​C​A​r​r​a​y​

It has a C array of NSString pointers, a stringCount holding the length of the array, and a mutation count instance variable that gets incremented any time the set of strings is manipulated. A variadic method is provided to set the strings. Because CArray adopts NSFastEnumeration, it needs to implement -countByEnumeratingWithState:objects:count::

-​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​c​o​u​n​t​B​y​E​n​u​m​e​r​a​t​i​n​g​W​i​t​h​S​t​a​t​e​:​ ​(​N​S​F​a​s​t​E​n​u​m​e​r​a​t​i​o​n​S​t​a​t​e​ ​*​)​ ​s​t​a​t​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​b​j​e​c​t​s​:​ ​(​i​d​ ​*​)​ ​s​t​a​c​k​b​u​f​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​u​n​t​:​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​l​e​n​ ​{​
 ​ ​ ​ ​i​f​ ​(​s​t​a​t​e​-​>​s​t​a​t​e​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​F​i​r​s​t​ ​c​a​l​l​,​ ​d​o​ ​i​n​i​t​i​a​l​i​z​a​t​i​o​n​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​a​t​e​-​>​s​t​a​t​e​ ​=​ ​1​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​a​t​e​-​>​m​u​t​a​t​i​o​n​s​P​t​r​ ​=​ ​&​_​m​u​t​a​t​i​o​n​s​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​a​t​e​-​>​i​t​e​m​s​P​t​r​ ​=​ ​_​s​t​r​i​n​g​s​;​

 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​(​_​s​t​r​i​n​g​C​o​u​n​t​)​;​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​e​ ​r​e​t​u​r​n​e​d​ ​e​v​e​r​y​t​h​i​n​g​ ​t​h​e​ ​f​i​r​s​t​ ​t​i​m​e​ ​t​h​r​o​u​g​h​,​ ​s​o​ ​w​e​'​r​e​ ​d​o​n​e​.​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
 ​ ​ ​ ​}​

}​ ​/​/​ ​c​o​u​n​t​B​y​E​n​u​m​e​r​a​t​i​n​g​W​i​t​h​S​t​a​t​e​

The first call to this method finds the state’s state value to be zero, so it does first time initialization. It changes the state to a non-zero value so we know not to do the initialization work again. Then it sets the state’s mutationsPtr to point to the mutations instance variable and points the itemsPtr to the C array instance variable. CArray stores all of its string pointers sequentially in memory, so we can tell the caller the total collection count. The for-in loop will then iterate over all of them. When this method is called a second time, it returns zero signifying that all iteration has been completed, and the for-in loop terminates. No matter how big our collection is, this -countByEnumeratingWithState:objects:count: method is only called twice.

This code populates and iterates a CArray:

C​A​r​r​a​y​ ​*​c​a​r​r​a​y​ ​=​ ​[​[​C​A​r​r​a​y​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
[​c​a​r​r​a​y​ ​s​e​t​S​t​r​i​n​g​s​:​ ​@​"​I​"​,​ ​@​"​s​e​e​m​"​,​ ​@​"​t​o​"​,​ ​@​"​b​e​"​,​ ​@​"​a​"​,​ ​@​"​v​e​r​b​"​,​ ​n​i​l​]​;​

f​o​r​ ​(​N​S​S​t​r​i​n​g​ ​*​s​t​r​i​n​g​ ​i​n​ ​c​a​r​r​a​y​)​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​%​@​"​,​ ​s​t​r​i​n​g​)​;​
}​

And a sample run:

2​0​1​1​-​0​1​-​1​8​ ​1​5​:​4​7​:​1​5​.​5​3​1​ ​f​a​s​t​e​n​u​m​[​4​6​8​8​:​9​0​3​]​ ​I​
2​0​1​1​-​0​1​-​1​8​ ​1​5​:​4​7​:​1​5​.​5​3​4​ ​f​a​s​t​e​n​u​m​[​4​6​8​8​:​9​0​3​]​ ​s​e​e​m​
2​0​1​1​-​0​1​-​1​8​ ​1​5​:​4​7​:​1​5​.​5​3​4​ ​f​a​s​t​e​n​u​m​[​4​6​8​8​:​9​0​3​]​ ​t​o​
2​0​1​1​-​0​1​-​1​8​ ​1​5​:​4​7​:​1​5​.​5​3​4​ ​f​a​s​t​e​n​u​m​[​4​6​8​8​:​9​0​3​]​ ​b​e​
2​0​1​1​-​0​1​-​1​8​ ​1​5​:​4​7​:​1​5​.​5​3​5​ ​f​a​s​t​e​n​u​m​[​4​6​8​8​:​9​0​3​]​ ​a​
2​0​1​1​-​0​1​-​1​8​ ​1​5​:​4​7​:​1​5​.​5​3​5​ ​f​a​s​t​e​n​u​m​[​4​6​8​8​:​9​0​3​]​ ​v​e​r​b​

You do not actually have to iterate over a real collection. You can use fast enumeration for number generators. Whether this is actually a good idea is open to debate, but it is possible.

One number sequence popular with technical interviewers and book authors is the Fibonacci sequence, which is a recursive definition that says to start a sequence of numbers with two 1 values, then the next value is the sum of the previous two: 1, 1, 2, 3, 5, 8, 13, and so on. There are iterative and recursive solutions to this problem.

The fast enumeration solution takes advantage of two facts: -countByEnumeratingWithState: is given a chunk of space that can hold a number of pointers. Empirically, it looks like Cocoa provides 16 pointers worth of space. There is also some scratch space in the state structure for us to play with. The generator uses the stack space for pointers to NSNumbers for the sequence of numbers and uses the scratch space to hold the N-1 and N-2 values of the sequence.

The FibonacciSequence object does not have any state of its own, so any number of independent sequences can be generated from the same object.

Here is the class interface:

@​i​n​t​e​r​f​a​c​e​ ​F​i​b​o​n​a​c​c​i​S​e​q​u​e​n​c​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​<​N​S​F​a​s​t​E​n​u​m​e​r​a​t​i​o​n​>​
@​e​n​d​ ​/​/​ ​F​i​b​o​n​a​c​c​i​S​e​q​u​e​n​c​e​

And the lone method of this class does all of the dirty work:

-​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​c​o​u​n​t​B​y​E​n​u​m​e​r​a​t​i​n​g​W​i​t​h​S​t​a​t​e​:​ ​(​N​S​F​a​s​t​E​n​u​m​e​r​a​t​i​o​n​S​t​a​t​e​ ​*​)​ ​s​t​a​t​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​b​j​e​c​t​s​:​ ​(​i​d​ ​*​)​ ​s​t​a​c​k​b​u​f​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​u​n​t​:​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​l​e​n​ ​{​
 ​ ​ ​ ​a​s​s​e​r​t​(​l​e​n​ ​>​=​ ​2​)​;​ ​/​/​ ​b​e​c​a​u​s​e​ ​w​e​ ​p​r​e​-​p​o​p​u​l​a​t​e​ ​t​w​o​ ​v​a​l​u​e​s​ ​o​n​ ​f​i​r​s​t​-​c​a​l​l​
 ​ ​ ​ ​i​d​ ​*​s​c​a​n​,​ ​*​s​t​o​p​;​

 ​ ​ ​ ​i​f​ ​(​s​t​a​t​e​-​>​s​t​a​t​e​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​i​r​s​t​ ​c​a​l​l​,​ ​d​o​ ​i​n​i​t​i​a​l​i​z​a​t​i​o​n​s​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​a​t​e​-​>​s​t​a​t​e​ ​=​ ​1​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​a​t​e​-​>​m​u​t​a​t​i​o​n​s​P​t​r​ ​=​ ​(​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​*​)​s​e​l​f​;​ ​/​/​ ​n​o​t​ ​a​p​p​l​i​c​a​b​l​e​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​a​t​e​-​>​i​t​e​m​s​P​t​r​ ​=​ ​s​t​a​c​k​b​u​f​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​e​x​t​r​a​[​0​]​ ​h​a​s​ ​t​h​e​ ​N​ ​-​ ​2​ ​v​a​l​u​e​,​ ​e​x​t​r​a​[​1​]​ ​h​a​s​ ​t​h​e​ ​N​ ​-​ ​1​ ​v​a​l​u​e​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​s​e​e​d​ ​w​i​t​h​ ​c​o​r​r​e​c​t​ ​v​a​l​u​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​a​t​e​-​>​e​x​t​r​a​[​0​]​ ​=​ ​1​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​a​t​e​-​>​e​x​t​r​a​[​1​]​ ​=​ ​1​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​i​l​l​ ​i​n​ ​t​h​e​ ​f​i​r​s​t​ ​t​w​o​ ​v​a​l​u​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​a​t​e​-​>​i​t​e​m​s​P​t​r​[​0​]​ ​=​ ​[​N​S​N​u​m​b​e​r​ ​n​u​m​b​e​r​W​i​t​h​I​n​t​:​ ​s​t​a​t​e​-​>​e​x​t​r​a​[​0​]​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​a​t​e​-​>​i​t​e​m​s​P​t​r​[​1​]​ ​=​ ​[​N​S​N​u​m​b​e​r​ ​n​u​m​b​e​r​W​i​t​h​I​n​t​:​ ​s​t​a​t​e​-​>​e​x​t​r​a​[​1​]​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​t​w​e​a​k​ ​t​h​e​ ​s​c​a​n​n​i​n​g​ ​p​o​i​n​t​e​r​s​ ​b​e​c​a​u​s​e​ ​w​e​'​v​e​ ​a​l​r​e​a​d​y​ ​f​i​l​l​e​d​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​i​n​ ​t​h​e​ ​f​i​r​s​t​ ​t​w​o​ ​s​l​o​t​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​ ​=​ ​&​s​t​a​t​e​-​>​i​t​e​m​s​P​t​r​[​2​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​o​p​ ​=​ ​&​s​t​a​t​e​-​>​i​t​e​m​s​P​t​r​[​0​]​ ​+​ ​l​e​n​;​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​O​t​h​e​r​w​i​s​e​ ​w​e​'​r​e​ ​i​n​ ​t​h​e​ ​P​i​n​k​,​ ​a​n​d​ ​d​o​ ​n​o​r​m​a​l​ ​p​r​o​c​e​s​s​i​n​g​ ​f​o​r​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​a​l​l​ ​o​f​ ​t​h​e​ ​i​t​e​m​P​t​r​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​ ​=​ ​&​s​t​a​t​e​-​>​i​t​e​m​s​P​t​r​[​0​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​o​p​ ​=​ ​&​s​t​a​t​e​-​>​i​t​e​m​s​P​t​r​[​0​]​ ​+​ ​l​e​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​w​h​i​l​e​ ​(​s​c​a​n​ ​<​ ​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​D​o​ ​t​h​e​ ​F​i​b​o​n​a​c​c​i​ ​a​l​g​o​r​i​t​h​m​.​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​v​a​l​u​e​ ​=​ ​s​t​a​t​e​-​>​e​x​t​r​a​[​0​]​ ​+​ ​s​t​a​t​e​-​>​e​x​t​r​a​[​1​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​a​t​e​-​>​e​x​t​r​a​[​0​]​ ​=​ ​s​t​a​t​e​-​>​e​x​t​r​a​[​1​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​a​t​e​-​>​e​x​t​r​a​[​1​]​ ​=​ ​v​a​l​u​e​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​p​o​p​u​l​a​t​e​ ​t​h​e​ ​f​a​s​t​ ​e​n​u​m​ ​i​t​e​m​ ​p​o​i​n​t​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​*​s​c​a​n​ ​=​ ​[​N​S​N​u​m​b​e​r​ ​n​u​m​b​e​r​W​i​t​h​U​n​s​i​g​n​e​d​L​o​n​g​:​ ​v​a​l​u​e​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​a​n​d​ ​t​h​e​n​ ​s​c​o​o​t​ ​o​v​e​r​ ​t​o​ ​t​h​e​ ​n​e​x​t​ ​v​a​l​u​e​
 ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​+​+​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​A​l​w​a​y​s​ ​f​i​l​l​ ​u​p​ ​t​h​e​i​r​ ​s​t​a​c​k​ ​b​u​f​f​e​r​.​
 ​ ​ ​ ​r​e​t​u​r​n​ ​l​e​n​;​
}​ ​/​/​ ​c​o​u​n​t​B​y​E​n​u​m​e​r​a​t​i​n​g​W​i​t​h​S​t​a​t​e​

This loop spins through a portion of the sequence:

F​i​b​o​n​a​c​c​i​S​e​q​u​e​n​c​e​ ​*​f​i​b​b​y​ ​=​ ​[​[​F​i​b​o​n​a​c​c​i​S​e​q​u​e​n​c​e​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

i​n​t​ ​b​o​r​e​d​o​m​ ​=​ ​0​;​
f​o​r​ ​(​N​S​N​u​m​b​e​r​ ​*​n​u​m​b​e​r​ ​i​n​ ​f​i​b​b​y​)​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​%​@​"​,​ ​n​u​m​b​e​r​)​;​
 ​ ​ ​ ​i​f​ ​(​b​o​r​e​d​o​m​+​+​ ​>​ ​4​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​}​
}​

And when run, looks like this:

2​0​1​1​-​0​1​-​1​8​ ​1​5​:​4​7​:​1​5​.​5​3​6​ ​f​a​s​t​e​n​u​m​[​4​6​8​8​:​9​0​3​]​ ​1​
2​0​1​1​-​0​1​-​1​8​ ​1​5​:​4​7​:​1​5​.​5​3​6​ ​f​a​s​t​e​n​u​m​[​4​6​8​8​:​9​0​3​]​ ​1​
2​0​1​1​-​0​1​-​1​8​ ​1​5​:​4​7​:​1​5​.​5​3​6​ ​f​a​s​t​e​n​u​m​[​4​6​8​8​:​9​0​3​]​ ​2​
.​.​.​
2​0​1​1​-​0​1​-​1​8​ ​1​5​:​4​7​:​1​5​.​5​8​0​ ​f​a​s​t​e​n​u​m​[​4​6​8​8​:​9​0​3​]​ ​1​0​2​3​3​4​1​5​5​
2​0​1​1​-​0​1​-​1​8​ ​1​5​:​4​7​:​1​5​.​5​8​1​ ​f​a​s​t​e​n​u​m​[​4​6​8​8​:​9​0​3​]​ ​1​6​5​5​8​0​1​4​1​
2​0​1​1​-​0​1​-​1​8​ ​1​5​:​4​7​:​1​5​.​5​8​1​ ​f​a​s​t​e​n​u​m​[​4​6​8​8​:​9​0​3​]​ ​2​6​7​9​1​4​2​9​6​

Runtime enhancements and garbage collection

Objective-C 2.0 also enhances the language’s runtime component. The runtime has been rewritten for 64-bit programming, which includes things like compatibility with C++ exceptions, discussed in Chapter 5: Exceptions, Error Handling, and Signals, and solving the fragile base class problem, discussed in Chapter 2: The Compiler.

Objective-C 2.0 has an optional garbage collector, which can be used instead of manual reference counting with retain, release, and autorelease. The garbage collector is general purpose enough to be used outside of Objective-C and is discussed in Chapter 7: Memory.

Properties

Another common Cocoa idiom is setters and getters, pairs of methods that affect instance variables. Suppose you have a botanical simulation and you model plants with a name, a color, and a size. You will want to have methods for users of a plant to have a way to change its attributes:

@​i​n​t​e​r​f​a​c​e​ ​L​o​t​u​s​B​l​o​s​s​o​m​ ​:​ ​N​S​O​b​j​e​c​t​ ​{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​_​n​a​m​e​;​
 ​ ​ ​ ​N​S​C​o​l​o​r​ ​*​_​c​o​l​o​r​;​
 ​ ​ ​ ​i​n​t​ ​_​s​i​z​e​;​
}​

-​ ​(​v​o​i​d​)​ ​s​e​t​N​a​m​e​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​n​a​m​e​;​
-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​n​a​m​e​;​

-​ ​(​v​o​i​d​)​ ​s​e​t​C​o​l​o​r​:​ ​(​N​S​C​o​l​o​r​ ​*​)​ ​c​o​l​o​r​;​
-​ ​(​N​S​C​o​l​o​r​ ​*​)​ ​c​o​l​o​r​;​

-​ ​(​v​o​i​d​)​ ​s​e​t​S​i​z​e​:​ ​(​i​n​t​)​ ​s​i​z​e​;​
-​ ​(​i​n​t​)​ ​s​i​z​e​;​

@​e​n​d​ ​/​/​ ​L​o​t​u​s​B​l​o​s​s​o​m​

The data that comes into each -setWhatever: method is treated in different ways: a string should be copied, a color should be retained, and no memory management is necessary for an int. Plus you need to maintain memory management hygiene, along with making checks to make sure you are not releasing an object unnecessarily:

-​ ​(​v​o​i​d​)​ ​s​e​t​N​a​m​e​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​n​a​m​e​ ​{​
 ​ ​ ​ ​i​f​ ​(​_​n​a​m​e​ ​!​=​ ​n​a​m​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​_​n​a​m​e​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​n​a​m​e​ ​=​ ​[​n​a​m​e​ ​c​o​p​y​]​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​s​e​t​N​a​m​e​
-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​n​a​m​e​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​_​n​a​m​e​;​
}​ ​/​/​ ​n​a​m​e​

-​ ​(​v​o​i​d​)​ ​s​e​t​C​o​l​o​r​:​ ​(​N​S​C​o​l​o​r​ ​*​)​ ​c​o​l​o​r​ ​{​
 ​ ​ ​ ​i​f​ ​(​_​c​o​l​o​r​ ​!​=​ ​c​o​l​o​r​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​_​c​o​l​o​r​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​c​o​l​o​r​ ​=​ ​[​c​o​l​o​r​ ​r​e​t​a​i​n​]​;​
 ​ ​ ​ ​}​
}​
-​ ​(​N​S​C​o​l​o​r​ ​*​)​ ​c​o​l​o​r​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​_​c​o​l​o​r​;​
}​ ​/​/​ ​c​o​l​o​r​

-​ ​(​v​o​i​d​)​ ​s​e​t​S​i​z​e​:​ ​(​i​n​t​)​ ​s​i​z​e​ ​{​
 ​ ​ ​ ​_​s​i​z​e​ ​=​ ​s​i​z​e​;​
}​ ​/​/​ ​s​e​t​S​i​z​e​
-​ ​(​i​n​t​)​ ​s​i​z​e​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​_​s​i​z​e​;​
}​ ​/​/​ ​s​i​z​e​

This code sidesteps issues relating to atomicity or exception handling. Still, that is a lot of boilerplate code. There are utilities that will take a class definition and generate the setters and getters for you, but these are still lines of code that have to be maintained. Properties push the work of creating this repetitive code into the compiler. The equivalent code with Objective-C 2.0 properties looks like this:

@​i​n​t​e​r​f​a​c​e​ ​L​o​t​u​s​B​l​o​s​s​o​m​ ​:​ ​N​S​O​b​j​e​c​t​ ​{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​_​n​a​m​e​;​
 ​ ​ ​ ​N​S​C​o​l​o​r​ ​*​_​c​o​l​o​r​;​
 ​ ​ ​ ​i​n​t​ ​_​s​i​z​e​;​
}​
@​p​r​o​p​e​r​t​y​ ​(​c​o​p​y​)​ ​N​S​S​t​r​i​n​g​ ​*​n​a​m​e​;​
@​p​r​o​p​e​r​t​y​ ​(​r​e​t​a​i​n​)​ ​N​S​C​o​l​o​r​ ​*​c​o​l​o​r​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​i​n​t​ ​s​i​z​e​;​

@​e​n​d​ ​/​/​ ​L​o​t​u​s​B​l​o​s​s​o​m​ ​(​p​r​o​p​e​r​t​i​e​s​)​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​L​o​t​u​s​B​l​o​s​s​o​m​
@​s​y​n​t​h​e​s​i​z​e​ ​n​a​m​e​ ​=​ ​_​n​a​m​e​;​
@​s​y​n​t​h​e​s​i​z​e​ ​c​o​l​o​r​ ​=​ ​_​c​o​l​o​r​;​
@​s​y​n​t​h​e​s​i​z​e​ ​s​i​z​e​ ​=​ ​_​s​i​z​e​;​

-​ ​(​v​o​i​d​)​ ​d​e​a​l​l​o​c​ ​{​
 ​ ​ ​ ​[​_​n​a​m​e​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​_​c​o​l​o​r​ ​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​[​s​u​p​e​r​ ​d​e​a​l​l​o​c​]​;​
}​ ​/​/​ ​d​e​a​l​l​o​c​

-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​d​e​s​c​r​i​p​t​i​o​n​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​ ​@​"​b​l​o​s​s​o​m​ ​%​@​ ​:​ ​%​@​ ​/​ ​%​d​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​_​n​a​m​e​,​ ​_​c​o​l​o​r​,​ ​_​s​i​z​e​]​;​
}​ ​/​/​ ​d​e​s​c​r​i​p​t​i​o​n​

@​e​n​d​ ​/​/​ ​L​o​t​u​s​B​l​o​s​s​o​m​(​p​r​o​p​e​r​t​i​e​s​)​

Notice the use of underscores in instance variable names and the “assignment” in the @synthesize directive. This is not a required style, but a common one chosen to remove a source of errors. If the instance variable is the same as the property, it is very easy to use the instance variable, say name directly rather than referencing the property, say self.name, leading to inconsistent use of accessors. By prepending underscores to instance variables, there is no name, making it obvious when an instance variable is being accessed via _name and a property, via self.name.

Properties also add a new syntactic element to Objective-C 2.0: the dot operator. When used with an object pointer, the dot operator is the equivalent of using setters and getters:

L​o​t​u​s​B​l​o​s​s​o​m​ ​*​b​l​o​s​s​o​m​ ​=​ ​[​[​L​o​t​u​s​B​l​o​s​s​o​m​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
b​l​o​s​s​o​m​.​n​a​m​e​ ​=​ ​@​"​H​o​f​f​"​;​
b​l​o​s​s​o​m​.​c​o​l​o​r​ ​=​ ​[​N​S​C​o​l​o​r​ ​w​h​i​t​e​C​o​l​o​r​]​;​
b​l​o​s​s​o​m​.​s​i​z​e​ ​=​ ​2​3​;​

N​S​L​o​g​ ​(​@​"​%​@​ ​o​f​ ​c​o​l​o​r​ ​%​@​ ​h​a​s​ ​s​i​z​e​ ​%​d​"​,​
 ​ ​ ​ ​ ​ ​ ​b​l​o​s​s​o​m​.​n​a​m​e​,​ ​b​l​o​s​s​o​m​.​c​o​l​o​r​,​ ​b​l​o​s​s​o​m​.​s​i​z​e​)​;​

The dot notation can be used independently of properties. You can even use the above code with the first LotusBlossom class declaration that explicitly listed declarations for the setters and getters.

The use of the dot operator does not cause ambiguity with the language. You cannot use it with a pointer, so stealing it for properties lets Objective-C 2.0 clean up the general syntax.

Properties introduce three new directives: @property, which declares the contract the property declares: its name, its type, and how it behaves with respect to memory management. @synthesize is an optional way of telling the compiler to automatically create the accessor implementation at compile-time. The compiler requires implementation of the methods, whether through your explicit implementation, @synthesize, or some combination. Use @dynamic to tell the compiler to trust you that the accessor methods will be there at runtime, perhaps through some toolkit magic or loaded from a shared library.

There are four classes of parameters that @property provides:

Mutability: whether the property is immutable (readonly) or mutable (readwrite, the default)

Memory management: whether setting the property is a simple assignment (assign, the default), requires a retain (retain), or requires a copy (copy). For objects like delegates, you would use assign; for other object references, you would use retain or copy. You should copy strings unless there is a good reason not to. It is very easy for a mutable string to find its way into a graph of objects, especially if you are getting strings from user interface classes. The copy operation is a no-op for immutable strings, so you will not be paying a copy penalty for immutable strings. In a garbage collected world, assign and retain are both strong references.

Concurrency: In a garbage collected (GC) world, assignments to properties are atomic for free. There is no chance that another thread will see your assignment in-progress, even for complex types like C structs. In a non-GC world, assignments via properties are atomic, but it comes at a price of using an implicit lock when accessing the property. If you are in a non-GC program, noticing performance problems with properties, and accessing the properties in a single thread, then you can use nonatomic to suppress the use of these locks. Plain structure value assignments via the = operator are never atomic. If you assign a struct in an accessor and you want the operation to be atomic, you will need to provide explicit locking. For performance reasons on iOS, you should make all of your properties nonatomic unless you have a reason not to.

API control: By default, a property will use the property name as the name of the getter, and the name of the property prepended by “set” as the name of the setter. For example, for the property duration, the accessor names would be -duration and -setDuration:. You can override these with setter= and getter=. The methods you supply have to have the proper types for argument and return values.

Some examples:

@​p​r​o​p​e​r​t​y​ ​(​r​e​a​d​o​n​l​y​)​ ​N​S​S​t​r​i​n​g​ ​*​n​a​m​e​;​

name is an NSString, and it can only be read, not written.

@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​i​d​ ​d​e​l​e​g​a​t​e​;​

delegate can be read and changed. It is a simple pointer assignment and not retained in a non-GC world.

@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​c​o​p​y​)​ ​N​S​S​t​r​i​n​g​ ​*​f​o​o​b​a​g​e​;​

foobage can be read and changed, and when the property is changed, the string will be copied. It is non-atomic, so only set/get it in a single thread.

A subclass or a class extension can change a property from readonly to readwrite. This allows for mutable subclasses and private testing methods to change a property.

@synthesize is used to generate accessors. You can optionally tell @synthesize to use a particular instance variable to read or write; otherwise it will use an instance variable named after the property. The 32-bit (classic Objective-C) runtime requires that you declare the instance variables in the class @interface declaration. The 64-bit runtime does not require this. It will add the instance variable to the class at run-time.

Some examples:

@​s​y​n​t​h​e​s​i​z​e​ ​n​a​m​e​;​

This creates a -name method. Because name was declared readonly, no -setName: is created.

@​s​y​n​t​h​e​s​i​z​e​ ​d​e​l​e​g​a​t​e​ ​=​ ​h​o​o​b​i​e​d​o​o​;​

This creates -delegate and -setDelegate: methods, and the instance variable that holds the delegate value is called hoobiedoo. hoobiedoo is assigned the value of the new delegate, and it is not retained.

There was no @synthesize for foobage, so you are responsible for writing the -foobage and -setFoobage: methods and making sure that foobage has copy semantics. There is no compiler verification of this. It assumes we are upholding our end of the contract.

Lastly, there is @dynamic, which just takes the name of the property:

@​d​y​n​a​m​i​c​ ​s​o​m​e​P​r​o​p​e​r​t​y​;​

@dynamic tells the compiler not to look for or generate setters or getters for the property. Instead, you will provide them at runtime somehow. You might load a dynamic library that contains the implementation. In addition to the -forwardInvocation: method, NSObject also has added two new methods, +resolveInstanceMethod and +resolveClassMethod. Each takes a SEL and returns a BOOL. A class gets a chance to resolve a missing method and add it dynamically to the class at run-time before the forwarding machinery takes over. One of the +resolve calls will be invoked, and it has the chance of using the Objective-C runtime functions to add a method to a class. The +resolve are much faster than using -forwardInvocation:. Core Data also takes advantage of this mechanism to map properties to Core Data attributes.

Exercises

Use @dynamic, +resolveInstanceMethod, and the class_addMethod() function to add a property implementation method at runtime. Manipulating the Objective-C runtime is not covered here, but you can find out about this in Apple’s Objective-C 2.0 Runtime Reference.

Measure the performance of atomic and nonatomic properties in garbage collected and non-garbage collected environments. Are there real performance differences?

The requirement that collections not mutate while you are iterating over them can be inconvenient at times, especially if you are doing a “scan the collection and pull out stuff that shouldn’t be there” operation. What would it take to make a classic array enumerator support having the collection mutated during iteration? What impact would this have on fast enumeration?

C99, the current version of C, as well as Objective-C, have the restrict keyword. What does it do?

FibonacciSequence rapidly exceeds the range of values an unsigned long can hold, which is why the code here cheats and stops after 40 iterations. One way to fix this (or at least allow us to get farther into the sequence) would be to use more of the extra values in the NSFastEnumerationState structure. long longs may get you farther, as might the NSDecimal functions or the NSDecimalNumber class. There are also “BigNum” libraries out on the Internet that provide numbers of arbitrary size.

2
The Compiler

The compiler is the fundamental tool in your programming arsenal. No matter what editor you use for source code and no matter if you build your programs with Xcode or make, the compiler is involved. Apple ships the GNU Compiler Collection with the developer tools. You can invoke the compiler with the gcc command, but you can also use the cc command.

The Mac OS X 10.4 and 10.5 development tools include gcc versions 3.3 and 4.0. You can use the older compiler if you have code that will not compile with the newer compiler or if you need to support versions of Mac OS X prior to version 10.3.9. gcc 4.2 is the default for 10.6 and is incompatible with the 10.4 (and earlier) SDKs. Also, gcc 4.2 is the minimum compiler you can use with Xcode 4, so you can’t use the latest Xcode to build applications for 10.4.

Apple has been working on its own compiler technology for some time through the LLVM (Low-Level Virtual Machine) project. LLVM technology has been used behind the OpenGL Shader Language and OpenCL on the Mac. clang is a front-end to LLVM that parses C code. You can use clang to parse the code and use the LLVM or gcc back-ends to generate the object code. The clang front-end generates much better errors and warnings than gcc does. The Xcode static analyzer is based on clang. For maximal compatibility, you would use gcc for everything. To get the better errors and warnings, plus faster compile times, you can use “LLVM gcc,” which uses the clang parser and the gcc code generation. Use the “LLVM compiler” to get all of the new goodies.

gcc supports a number of languages. The C-dialect languages are the ones that are used for typical Mac OS X development. The extension of the source file tells the compiler what dialect to use:

.c | Regular C
.m | Objective-C
.C | C++ (but do not use on HFS+ file systems since it is not case sensitive)
.cpp, .cc | C++
.mm | Objective-C++, a blend of C++ and Objective-C

Handy Flags

The documentation for gcc is included with Xcode. Beware that gcc is huge and comes with lots of options, lots of features, lots of flags, and lots of extensions to the languages it compiles. It also has lots of cool stuff. Here are some command-line flags that you might find useful. You can control a number of these settings in Xcode, but not all of gcc’s flags are exposed via the GUI. You would need to add them in the Other C Flags or Other C++ Flags configuration.

-g | Add debugging symbols. Turn this on using the Level of Debug Symbols in Xcode’s Build tab in the Project or Target info panel.
-E | See preprocessor output. You can tell Xcode to preprocess a file by using the Preprocess menu item in the contextual menu in the Xcode 3 code editor. This menu item also lives in the Build menu. As of this writing, Xcode 4 has moved it to the Generated Output portion of the assistant.
-S | See generated assembly code.
-save-temps | Keep temporary files around.
-Wall, -Wmost | Show more warnings.
-Werror | Treat warnings as errors. Turn this on by checking Treat Warnings as Errors in Xcode’s Build tab in the Project or Target info panel.
-DSYMBOL | #define from the command line.
-DSYMBOL=value | #define with a value from the command line. You can tweak the macro settings by editing Preprocessor Macros in Xcode’s Build tab in the Project or Target info panel.
-O# | Set optimization levels (described below). Use the Optimization Level setting in the Project or Target info panel’s Build tab to control this in Xcode.
-std | Choose a standard, or non-standard, C dialect. Supplying -std=c99 will give you C99, which adds several gcc extensions and C++ features to the language but disallows any additional gcc extensions. You can also use -std=gnu99 to get both C99 and gcc’s extensions.

Debugging

The -g flag enables debugging symbols, which are chunks of extra data that allow debugging tools to map an arbitrary address in your executable code back to the source that generated it. It also contains information about your types, variable names, and data structures. Enabling debugging symbols can make your program bigger and may also expose some implementation details that you might prefer to be hidden. The strip command will remove these symbols, and Xcode provides a number of options for controlling symbol stripping.

The downside of stripping your executables, or not building your application with -g in the first place, means that you will not have symbolic stack traces when debugging in the field and you will not be able to easily analyze crash reporter stack traces that people email you.

If you held on to a version of your application prior to stripping, you can use the atos command to map addresses back to program symbols. You can also hang on to the external dSYM and feed it to atos. It is always a good idea to use -g during development because -g only adds debugging symbols and does not affect the code generation.

The Apple development tools support two different debug symbol information formats: Stabs and DWARF. Stabs was the default for older Xcodes, and DWARF is the default for Xcode 3 and beyond.

Warnings

Compiler warnings are a good thing. If the compiler is complaining about something you wrote, most likely your code has something questionable that could lead to errors somewhere down the line. A good goal to strive for is to have your code always compile cleanly without warnings. -Wall will show a lot of warnings for most everything the gcc developers consider questionable. -Wmost is a good middle ground when using the Cocoa frameworks.

Specific warnings can be independently turned on and off based on your specific coding style. Say you are using a library that uses macros that leave unused variables lying around. Messy, but pretty much harmless since the compiler won’t allocate space for them. Example 2.1 shows some warnings when compiling.

Example 2.1. warning.m

/​/​ ​w​a​r​n​i​n​g​.​m​ ​-​-​ ​s​h​o​w​ ​g​e​n​e​r​a​t​i​o​n​ ​o​f​ ​c​o​m​p​i​l​e​r​ ​w​a​r​n​i​n​g​s​

/​/​ ​g​c​c​ ​-​W​a​l​l​ ​-​o​ ​w​a​r​n​i​n​g​ ​w​a​r​n​i​n​g​.​m​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​i​;​
}​ ​/​/​ ​m​a​i​n​

Then if you build it:

$​ ​g​c​c​ ​-​W​a​l​l​ ​-​o​ ​w​a​r​n​i​n​g​ ​w​a​r​n​i​n​g​.​m​
w​a​r​n​i​n​g​.​m​:​5​:​ ​w​a​r​n​i​n​g​:​ ​s​e​c​o​n​d​ ​a​r​g​u​m​e​n​t​ ​o​f​ ​‘​m​a​i​n​’​ ​s​h​o​u​l​d​ ​b​e​ ​‘​c​h​a​r​ ​*​*​’​
w​a​r​n​i​n​g​.​m​:​ ​I​n​ ​f​u​n​c​t​i​o​n​ ​‘​m​a​i​n​’​:​
w​a​r​n​i​n​g​.​m​:​6​:​ ​w​a​r​n​i​n​g​:​ ​u​n​u​s​e​d​ ​v​a​r​i​a​b​l​e​ ​‘​i​’​
w​a​r​n​i​n​g​.​m​:​7​:​ ​w​a​r​n​i​n​g​:​ ​c​o​n​t​r​o​l​ ​r​e​a​c​h​e​s​ ​e​n​d​ ​o​f​ ​n​o​n​-​v​o​i​d​ ​f​u​n​c​t​i​o​n​

Two of those warnings are really interesting since they are actually errors: The parameters to main() are messed up, and nothing is being returned from main(). But (in this case) the unused variable is not a show-stopper. Fix the code otherwise:

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​i​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

$​ ​g​c​c​ ​-​W​a​l​l​ ​w​a​r​n​i​n​g​.​m​
w​a​r​n​i​n​g​.​m​:​ ​I​n​ ​f​u​n​c​t​i​o​n​ ​`​m​a​i​n​'​:​
w​a​r​n​i​n​g​.​m​:​6​:​ ​w​a​r​n​i​n​g​:​ ​u​n​u​s​e​d​ ​v​a​r​i​a​b​l​e​ ​‘​i​’​

You can turn that warning off with

$​ ​g​c​c​ ​-​W​a​l​l​ ​-​W​n​o​-​u​n​u​s​e​d​ ​w​a​r​n​i​n​g​.​m​
(​n​o​ ​c​o​m​p​l​a​i​n​t​s​)​

You will still get other warnings if you make mistakes. If you wanted to only see warnings for unused variables and no others, use -Wunused (drop the no-):

$​ ​g​c​c​ ​-​W​u​n​u​s​e​d​ ​w​a​r​n​i​n​g​.​m​
w​a​r​n​i​n​g​.​m​:​ ​I​n​ ​f​u​n​c​t​i​o​n​ ​`​m​a​i​n​'​:​
w​a​r​n​i​n​g​.​m​:​9​:​ ​w​a​r​n​i​n​g​:​ ​u​n​u​s​e​d​ ​v​a​r​i​a​b​l​e​ ​`​i​'​

The gcc documentation has the full set of warnings described in detail.

If you have a file that has warnings that you want to ignore, but you do not want to turn them off project-wide, you can add per-file compiler flags in Xcode. For Xcode 3, select the source file in the Compile Sources phase of the target section of your Xcode project, choose Get Info, and then add your compiler flags to the build tab of the info window. The important thing to remember is that you set this deep in your target, not on your source file up in your source file Xcode groups.

Xcode 4 has moved this feature to the Build Phases tab for your target. Expand Compile Sources and enter the compiler flags for the source file.

It is worth your time to reduce your warning count. If you have a lot of warnings that you “just ignore all the time,” useful warnings will get lost in the noise. It is best to either fix them or suppress the ones you consider useless. Treating warnings as errors is an excellent way to enforce good warning discipline.

And lastly, for a quick syntax check and no code generation, give gcc the -fsyntax-only flag.

Seeing Preprocessor Output

Sometimes you get an inscrutable error and have no idea why the compiler is complaining. Or you may have code that looks reasonable and compiles OK, but it behaves in a way that defies sanity even when you take the phase of the moon into account. This is a good time to examine the preprocessor’s output so you can see exactly what the compiler is seeing. The -E flag tells gcc to send the preprocessed source code to standard out. You can use Xcode 3’s Preprocess command as well, or Xcode 4’s generated output.

Example 2.2 looks simple enough. It will read a line from standard in and print it back out.

Example 2.2. preprocTest.m

/​/​ ​p​r​e​p​r​o​c​T​e​s​t​.​m​ ​-​-​ ​a​ ​p​r​o​g​r​a​m​ ​t​o​ ​s​h​o​w​ ​p​r​e​p​r​o​c​e​s​s​o​r​ ​o​u​t​p​u​t​

/​/​ ​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​p​r​e​p​r​o​c​T​e​s​t​ ​p​r​e​p​r​o​c​T​e​s​t​.​m​
/​/​ ​o​r​
/​/​ ​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​E​ ​p​r​e​p​r​o​c​T​e​s​t​.​m​ ​>​ ​j​u​n​k​.​i​
/​/​ ​(​a​n​d​ ​t​h​e​n​ ​l​o​o​k​ ​a​t​ ​j​u​n​k​.​i​)​

#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​

#​d​e​f​i​n​e​ ​B​U​F​F​E​R​_​S​I​Z​E​ ​2​0​4​8​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​B​U​F​F​E​R​_​S​I​Z​E​]​;​ ​ ​ ​/​*​ ​t​h​i​s​ ​i​s​ ​m​y​ ​b​u​f​f​e​r​,​ ​t​h​e​r​e​ ​a​r​e​ ​m​a​n​y​ ​l​i​k​e​ ​i​t​ ​*​/​
 ​ ​ ​ ​c​h​a​r​ ​*​t​h​i​n​g​;​

 ​ ​ ​ ​t​h​i​n​g​ ​=​ ​f​g​e​t​s​ ​(​b​u​f​f​e​r​,​ ​B​U​F​F​E​R​_​S​I​Z​E​,​ ​s​t​d​i​n​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​s​"​,​ ​t​h​i​n​g​)​;​

 ​ ​ ​ ​/​*​ ​h​a​p​p​i​n​e​s​s​ ​a​n​d​ ​l​i​g​h​t​ ​*​/​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​

}​ ​/​/​ ​m​a​i​n​

Compile it and run it:

$​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​p​r​e​p​r​o​c​T​e​s​t​ ​p​r​e​p​r​o​c​T​e​s​t​.​m​
$​ ​.​/​p​r​e​p​r​o​c​T​e​s​t​
h​e​l​l​o​[​r​e​t​u​r​n​]​
h​e​l​l​o​
$​

Now dig into its preprocessed output. Compile your program like this now:

$​ ​g​c​c​ ​-​W​a​l​l​ ​-​E​ ​p​r​e​p​r​o​c​T​e​s​t​.​m​ ​>​ ​j​u​n​k​.​i​

This command tells gcc to preprocess the source file and write the results to junk.i, where the .i extension is for preprocessed output. junk.i will be a couple of hundred lines long due to the size and complexity of the header files it includes. Open it up in your favorite text editor and scroll to the end. You will see something like this:

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​2​0​4​8​]​;​
 ​ ​ ​ ​c​h​a​r​ ​*​t​h​i​n​g​;​

 ​ ​ ​ ​t​h​i​n​g​ ​=​ ​f​g​e​t​s​ ​(​b​u​f​f​e​r​,​ ​2​0​4​8​,​ ​_​_​s​t​d​i​n​p​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​s​"​,​ ​t​h​i​n​g​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​

}​

It is somewhat recognizable as the original program. Notice that all the comments are gone. #define BUFFER_SIZE 2048 is gone also, but you can see where 2048 has been substituted into the text stream. Notice that stdin has been expanded into __stdinp. What is that? Search in junk.i for it and find

e​x​t​e​r​n​ ​F​I​L​E​ ​*​_​_​s​t​d​i​n​p​;​

which is a FILE pointer. Because you are seeing exactly what the compiler is seeing, you can look at the guts of the FILE structure: all sorts of goodies like function pointers, buffers, and block size variables are in there. This is not the kind of information you would want your code to depend on, but it can be a big help when debugging. Plus, it’s fun to dig into things, see how they work, and see how things change over time.

It is useful to view the preprocessed output when you are debugging macros. Write your macro, run it through the preprocessor, and see if it has the effect you want.

Seeing the Generated Assembly Code

The real hard-core hackers can look at the assembly code generated by the compiler. Sometimes you need this to track down compiler problems or OS problems. Or you can browse around just for general amusement and education. To get the assembly code, compile with the -S flag, and the results will be put into an .s file based on the name of your source file. For instance, running

$​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​S​ ​p​r​e​p​r​o​c​T​e​s​t​.​m​

will create a preprocTest.s. If you want to assemble the resulting file, feed it to the compiler like this:

$​ ​g​c​c​ ​-​o​ ​p​r​e​p​r​o​c​T​e​s​t​ ​p​r​e​p​r​o​c​T​e​s​t​.​s​

Xcode 3 provides a Show Assembly Code item in the editor’s contextual menu so you can see what your code will assemble into. Xcode 4 has removed this feature, as of this writing.

You can also use otool to disassemble existing programs if you do not want to muck around with compiler flags or finding an installer for Xcode 3. This command will disassemble preprocTest:

$​ ​o​t​o​o​l​ ​-​t​ ​-​V​ ​p​r​e​p​r​o​c​T​e​s​t​
.​.​.​
_​m​a​i​n​:​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​9​4​ ​ ​ ​ ​ ​ ​ ​ ​p​u​s​h​q​ ​ ​ ​%​r​b​p​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​9​5​ ​ ​ ​ ​ ​ ​ ​ ​m​o​v​q​ ​ ​ ​ ​%​r​s​p​,​%​r​b​p​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​9​8​ ​ ​ ​ ​ ​ ​ ​ ​s​u​b​q​ ​ ​ ​ ​$​0​x​0​0​0​0​0​8​3​0​,​%​r​s​p​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​9​f​ ​ ​ ​ ​ ​ ​ ​ ​m​o​v​l​ ​ ​ ​ ​%​e​d​i​,​0​x​f​f​f​f​f​7​d​c​(​%​r​b​p​)​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​a​5​ ​ ​ ​ ​ ​ ​ ​ ​m​o​v​q​ ​ ​ ​ ​%​r​s​i​,​0​x​f​f​f​f​f​7​d​0​(​%​r​b​p​)​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​a​c​ ​ ​ ​ ​ ​ ​ ​ ​m​o​v​q​ ​ ​ ​ ​0​x​0​0​0​0​0​1​8​5​(​%​r​i​p​)​,​%​r​a​x​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​b​3​ ​ ​ ​ ​ ​ ​ ​ ​m​o​v​q​ ​ ​ ​ ​(​%​r​a​x​)​,​%​r​d​x​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​b​6​ ​ ​ ​ ​ ​ ​ ​ ​m​o​v​q​ ​ ​ ​ ​%​r​d​x​,​0​x​f​8​(​%​r​b​p​)​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​b​a​ ​ ​ ​ ​ ​ ​ ​ ​x​o​r​l​ ​ ​ ​ ​%​e​d​x​,​%​e​d​x​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​b​c​ ​ ​ ​ ​ ​ ​ ​ ​m​o​v​q​ ​ ​ ​ ​0​x​0​0​0​0​0​1​7​d​(​%​r​i​p​)​,​%​r​a​x​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​c​3​ ​ ​ ​ ​ ​ ​ ​ ​m​o​v​q​ ​ ​ ​ ​(​%​r​a​x​)​,​%​r​d​x​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​c​6​ ​ ​ ​ ​ ​ ​ ​ ​l​e​a​q​ ​ ​ ​ ​0​x​f​f​f​f​f​7​f​0​(​%​r​b​p​)​,​%​r​d​i​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​c​d​ ​ ​ ​ ​ ​ ​ ​ ​m​o​v​l​ ​ ​ ​ ​$​0​x​0​0​0​0​0​8​0​0​,​%​e​s​i​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​d​2​ ​ ​ ​ ​ ​ ​ ​ ​c​a​l​l​q​ ​ ​ ​0​x​1​0​0​0​0​0​f​1​e​ ​ ​ ​ ​ ​;​ ​s​y​m​b​o​l​ ​s​t​u​b​ ​f​o​r​:​ ​_​f​g​e​t​s​
.​.​.​

If you want to save all the intermediate elements, including some not covered here, you can use the -save-temps flag.

Compiler Optimization

There are two classes of argument flags for controlling optimization, which controls how the compiler generates machine code from your C code, as well as how it rewrites your code to behave more optimally. You can use -O with a number or letter to control the optimization level.

-O0 | Do no optimizations.
-O1 | Do some optimization (also what’s used if you use -O without a number).
-O2 and -O3 | Use yet more aggressive optimization.
-Os | Optimize for size. It does the same optimizations as -O2 but doesn’t do function inlining.

Higher optimization levels can make code unstable as more and more mechanical operations happen to it, so be sure to test when changing levels. Sometimes failures that happen at higher optimization levels can actually be indications of programming errors, so if you have the time, it may be worthwhile to pursue any failures that happen at higher optimization levels.

If you know of specific optimizations that you want to enable or disable, like strength reduction or common subexpression evaluation, you can turn them on or off individually. For example:

-fstrength-reduce | Enables strength reduction.
-fno-strength-reduce | Turns off strength reduction, even if the -O# setting would have it enabled otherwise.

The gcc documentation describes all the available control flags, and there are a lot of them.

Having an optimization level set to -O2 or higher will issue a warning (when -Wall is engaged) if a variable is used before initialization. The compiler needs to do flow analysis to determine if this happens, and that analysis only happens with -O levels of 2 or higher.

gcc supports both -g (debugging) and -O (optimization) at the same time. This combination has historically been unsupported in C compilers. This means you can use gdb on an optimized program, but there may be unexpected behavior because code can be re-ordered and variables may be eliminated. For example, single-stepping through some code in the debugger can make your “current line” indicator bounce all over the place when you are dealing with code that has been run through the optimizer.

Apple recommends using -Os to optimize for size, even on fast new machines. It might not produce the best optimization for a specific program, but it gives the best overall system performance since the system working set seems to be a big constraint on performance, as well as letting more code live in the processor caches.

GCC Extensions

One of the constants of GNU products is a huge number of features. gcc has an incredible number of extensions available. They can help improve your code, but they can also destroy portability if you target a platform that does not have gcc or you are not using gcc on Mac OS X.

Here are a handful of interesting extensions:

long long and unsigned long long: quadword (i.e., 64-bit) integers are treated as first class citizens. You can perform basic math (+,-,*,/), modular arithmetic, and bitwise operations with them.

Complex numbers: in C++ you can create complex numbers as a first class type, but gcc C has them built in.

Variable length automatic arrays: like you can use in C++, declaring the size of a stack-based array at runtime:

 ​ ​ ​ ​ ​ ​i​n​t​ ​s​i​z​e​ ​=​ ​s​o​m​e​_​f​u​n​c​t​i​o​n​(​)​;​
 ​ ​ ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​s​i​z​e​]​;​

The array also gets deallocated when the brace level the array was declared in is exited.

Inline functions (like in C++).

Macros with variable number of arguments, very handy for wrappers around printf and friends.

Packed structures, which remove any alignment padding the compiler might otherwise include. Add __attribute((packed)) at the end of your structure definition.

A number of these extensions have made their way into C99, the most recent version of the ISO C standard, such as variable automatic arrays and complex numbers. Also, Objective-C with the gcc compiler lets you declare variables anywhere, just like in C++.

Name Mangling

C++ uses name mangling for doing typesafe linking. The compiler encodes type information in a function’s name, which can lead to unreadable error messages or indecipherable output from Unix tools like nm You might be poking around an object file and see something like this:

$​ ​n​m​ ​s​l​i​d​e​m​a​s​t​e​r​.​o​
.​.​.​
0​0​0​0​0​0​3​0​ ​s​ ​E​H​_​f​r​a​m​e​1​
 ​ ​ ​ ​ ​ ​ ​ ​ ​U​ ​_​_​Z​N​1​2​B​i​g​N​e​r​d​R​a​n​c​h​7​B​i​g​S​h​o​w​5​S​l​i​d​e​1​3​D​i​s​p​l​a​y​I​n​V​i​e​w​E​P​7​B​i​g​V​i​e​w​
 ​ ​ ​ ​ ​ ​ ​ ​ ​U​ ​_​_​_​g​x​x​_​p​e​r​s​o​n​a​l​i​t​y​_​v​0​
.​.​.​

That "__ZN12BigNerdRanch..." symbol looks interesting. Here it is made human readable:

$​ ​c​+​+​f​i​l​t​ ​_​_​Z​N​1​2​B​i​g​N​e​r​d​R​a​n​c​h​7​B​i​g​S​h​o​w​5​S​l​i​d​e​1​3​D​i​s​p​l​a​y​I​n​V​i​e​w​E​P​7​B​i​g​V​i​e​w​
B​i​g​N​e​r​d​R​a​n​c​h​:​:​B​i​g​S​h​o​w​:​:​S​l​i​d​e​:​:​D​i​s​p​l​a​y​I​n​V​i​e​w​(​B​i​g​V​i​e​w​*​)​

Testing the compiler version

gcc supplies a number of preprocessor macros that you can use in your code to test what version of the compiler you are using and enable or disable code based on it. For instance, you could use long doubles if the code is being compiled with gcc 4 but fall back and use regular doubles on gcc 3. The version macros are:

__GNUC__ | The major revision number. For gcc 4.0.1, __GNUC__ would be 4.
__GNUC_MINOR__ | The minor revision number. For gcc 4.0.1, __GNUC_MINOR__ would be 0.
__GNUC_PATCHLEVEL__ | The patch level number. For gcc 4.0.1, __GNUC_PATCHLEVEL__ would be 1.

The Optimizer

The optimizer gets changed and improved with every gcc version, as well as with new versions of clang/LLVM. Many of the same optimizer flags work as they did before, and new flags are added. It is entirely possible there are compiler bugs related to aggressive optimization of correct code. As with any change to the toolchain for building a program, it pays to do appropriate testing to make sure new problems are not introduced when upgrading your compiler.

-O0 is for no optimization and a maximally debuggable application. -O is a tradeoff of compile speed and execution speed. -O2 performs the optimizations that do not involve a space-for-time tradeoff, but it does attempt function inlining when specified. -O3 is best for code that makes heavy use of loops and lots of computation. It considers all functions in the current compilation unit (source file) for inlining, even those not declared inline. -Os optimizes for code size rather than speed. There is no loop unrolling, but performance should still be about -O2 levels. Apple still recommends using -Os, which will lead to smaller executables, so there will be less paging from disk for large programs, plus more code will fit in the processor’s caches.

-fast is an optimization flag that changes the overall optimization strategy for gcc. The optimizations happen at the expense of code size. -fast sets the optimization level to -O3, enables a number of optimization flags, and ignores all other flags, except for those that specify the target architecture.

-fastcp, doesn’t do anything different than -fast in gcc 4.0 and is for C++ users only. It may change in the future to add optimizations to C++ code. -fastf is for C code made from fortran-to-C translators or if your code has fortran semantics.

The -fast family of flags can break IEEE-754 conformance for floating point math. Round-off errors can grow if you are doing lots of calculations, so you will need to decide if your floating point calculations need IEEE-754’s guarantees of accuracy. It also changes the alignment mode of data types, which affects the layout of members of a struct. This can create binary compatibility issues. Code compiled with -fast cannot always be linked against code compiled without it.

Vectorization

gcc 4.0 also includes an autovectorizer. The compiler attempts to convert code, such as loops or sequences of similar operations over chunks of data, into code for the vector processor, such as PowerPC’s Altivec, or Intel’s SSE2/SSE3. To use the vectorizer, you need to supply the -ftree-vectorize compiler flag. This only works at optimization levels -O2 or higher because gcc only computes a probable data flow graph using -O2 and higher.

-ftree-vectorize also enables the -fstrict-aliasing which lets the compiler make some assumptions based on the type of expressions it sees. In particular, an object of one type is assumed never to reside at the same address as an object of a different type unless they are “almost the same.” An unsigned int can alias an int, but not a pointer or a double. Doing something like taking the address of an int and storing a double there breaks this strict-aliasing assumption. The -Wstrict-aliasing flag will cause the compiler to emit warnings about most places that might break the strict aliasing rules.

Even More Compiler Flags

gcc has always had a lot of command-line flags that can control many aspects of the compiler, such as the dialect of the language being compiled, the manipulations performed on the resulting object code, and controlling what warnings are emitted.

In Xcode, you can pass these flags to the compiler by setting the Other C Flags (for optimization and other general flags) and Other Warning Flags (for flags related to warnings) in the target inspector. The warning flags are placed before the other C flags in case there is an order dependency between a set of flags.

Here are some flags that might be of interest to you:

-Wno-protocol

This flag makes Objective-C’s @protocol feature more useful in some circumstances. The default behavior of @protocol is to issue a warning for every method declared in the protocol that is not defined in the class that is adopting the protocol, even if the methods are implemented by a superclass. By giving gcc this flag, methods from the superclass will be considered to be implemented, and no warning will be issued.

-fobjc-call-cxx-cdtors

Objective-C++ has had a limitation regarding C++ objects that are embedded in Objective-C objects: constructors for the C++ objects never get called when the Objective-C object was allocated and initialized. The whole block of memory for the Objective-C object was zeroed as usual by the alloc method, and so the C++ object also was cleared out to all zeros. But its constructor is not called. This means the C++ object might be in a broken state having not been constructed properly. Likewise, destructors were never invoked on the embedded C++ object when the Objective-C object is deallocated, so the C++ object could leak memory. You could work around this by using a pointer to a C++ object and allocating it with new.

By adding this flag, the compiler will create a pair of Objective-C methods (called .cxx_construct and .cxx_destruct) that are called by the Objective-C runtime to construct and destruct the C++ objects.

-Wundeclared-selector

This flag tells the compiler to warn you if you use a @selector() that it has not seen yet. You might have an NSTimer callback method called -moveMonsterTowardsPlayer:, and you accidentally use @selector(moveMonsterToPlayer:). The compiler will issue a warning if you use this flag. Needless to say, this can save you some headaches by catching typos at compile time rather than runtime.

-Q

This last one is purely for geeky fun. It tells the compiler to display the name of functions and methods as they are compiled, along with random statistics about the compilation:

 ​-​[​B​W​C​r​o​s​s​S​t​i​t​c​h​L​i​s​t​ ​a​d​d​S​t​i​t​c​h​:​a​t​R​o​w​:​c​o​l​u​m​n​:​]​
 ​-​[​B​W​C​r​o​s​s​S​t​i​t​c​h​L​i​s​t​ ​m​a​r​k​R​e​m​o​v​e​d​A​t​R​o​w​:​c​o​l​u​m​n​:​]​
 ​-​[​B​W​C​r​o​s​s​S​t​i​t​c​h​L​i​s​t​ ​c​h​a​n​g​e​E​n​u​m​e​r​a​t​o​r​]​
 ​-​[​B​W​C​r​o​s​s​S​t​i​t​c​h​L​i​s​t​ ​i​s​C​h​a​n​g​e​A​t​R​o​w​:​c​o​l​u​m​n​:​]​
 ​-​[​B​W​C​r​o​s​s​S​t​i​t​c​h​L​i​s​t​ ​c​o​u​n​t​]​

E​x​e​c​u​t​i​o​n​ ​t​i​m​e​s​ ​(​s​e​c​o​n​d​s​)​
 ​p​r​e​p​r​o​c​e​s​s​i​n​g​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​ ​ ​ ​0​.​0​2​ ​(​ ​1​%​)​ ​w​a​l​l​
 ​l​e​x​i​c​a​l​ ​a​n​a​l​y​s​i​s​ ​ ​ ​ ​ ​ ​:​ ​ ​ ​0​.​0​1​ ​(​ ​0​%​)​ ​w​a​l​l​
 ​p​a​r​s​e​r​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​ ​ ​ ​0​.​9​8​ ​(​6​1​%​)​ ​w​a​l​l​
 ​t​r​e​e​ ​g​i​m​p​l​i​f​y​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​ ​ ​ ​0​.​0​1​ ​(​ ​1​%​)​ ​w​a​l​l​
 ​e​x​p​a​n​d​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​ ​ ​ ​0​.​0​1​ ​(​ ​1​%​)​ ​w​a​l​l​
 ​g​l​o​b​a​l​ ​a​l​l​o​c​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​ ​ ​ ​0​.​0​2​ ​(​ ​1​%​)​ ​w​a​l​l​
 ​f​i​n​a​l​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​ ​ ​ ​0​.​0​1​ ​(​ ​0​%​)​ ​w​a​l​l​
 ​s​y​m​o​u​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​ ​ ​ ​0​.​0​5​ ​(​ ​3​%​)​ ​w​a​l​l​
 ​T​O​T​A​L​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​.​6​1​

64-Bit Computing

Mac OS X 10.4 introduced 64-bit computing to the Mac platform. 64-bit computing means that a process can directly address more than the 4 gig address space that 32-bit computing allows. The process might not actually get more than 4 gigs of memory to play with, but it will have the extra address space. That depends on what the user has installed in the machine, per-process limits, different operating system settings, and so on.

How much bigger is the 64-bit address space compared to the 32-bit one? A common analogy is this: if a byte is a dot the size of the period at the end of this sentence, then a 32-bit address space would cover the surface of the Golden Gate Bridge. A 64-bit address space would cover the entire land surface of the Earth.

Mac OS X supports 64-bit programming while using a 32-bit kernel. The kernel itself does not need to address huge chunks of memory directly, so it can easily run in a 32-bit address space. The data structures it manipulates can be 64-bit, so it can support a 64-bit program. This means that there is just one version of Mac OS X that users will be running, and no need for them to install a special 64-bit version of the OS to take advantage of 64-bit applications.

There is a 64-bit Mac OS X kernel available, which is 64-bit top to bottom. It can still run 32-bit applications fine. The main reason to run the 64-bit kernel is when you have a large amount of physical RAM, exceeding 32 gigabytes. The in-memory tables for managing the memory start consuming more and more of the 32-bit kernel’s address space, leaving less room for things like the file system cache. Note that all kernel extensions will need to be rebuilt for a 64-bit kernel. Some tools, such as the Shark profiler, do not work at all with the 64-bit kernel.

The 64-bit programming model

The 64-bit programming model used by Mac OS X is called LP64. This means that longs and pointers are 64 bits, while ints remain 32 bits. long long is still 64 bits. Unix systems, including Linux, use an LP64 model. In comparison, Windows is LLP64. In LLP64, long long is 64 bits, as are pointers, but ints and longs remain 32 bits. This is something to keep in mind if you have a cross-platform code base.

Code compiled with the 32-bit model is not compatible with code compiled with the 64-bit model. There are changes to the Mach-O ABI to support 64-bit computing. bools are still one byte in size, and the alignment in structs is “natural,” so fields will be padded so that the pointers are 8-byte aligned. Calling conventions in code differs between 32-bit and 64-bit. 64-bit code on Intel has access to many more registers, so more kinds of structs are passed by value in registers.

This also means that 64-bit programs can only use 64-bit frameworks. Mac OS X 10.4 only supplied 64-bit versions of the System and Accelerate frameworks. None of the user interface frameworks were supplied in a 64-bit version. Also, only C and C++ were supported in Tiger 64-bit code, not Objective-C. Supplying a user interface to your 64-bit programs required you to split the program into 32-bit client part and a 64-bit server part and then have them communicate via shared memory, sockets, pipes, or some other IPC mechanism.

OS X 10.5 and beyond have 64-bit versions of the system frameworks, meaning that you can have a 64-bit Cocoa application. There are some things that are 32-bit only, such as parts of QuickTime and the user interface portions of Carbon, and there are API differences with some deprecated calls and classes being unavailable in 64-bit mode.

Because you cannot mix 32-bit and 64-bit code, you cannot use 32-bit plugins in a 64-bit program. There is also a code impact from the change in the sizes of the primitive data types.

When using interprocess communication, networking, and binary data files, you should be careful when you choose your data types. Explicitly sized data types, like uint32_t, will stay the same size in both 32-bit and 64-bit worlds and are more predictable than generic types, like long, which can change sizes

Alignment of data also changes between the two worlds. Pointers and longs will need to have 64-bit rather than 32-bit alignment which can make structures larger. The general rule of thumb is putting larger elements early on in the structure. That should give you good use of space vs. padding caused by data alignment.

64-bit cleanliness

Be careful of mixing 64-bit (long) and 32-bit (int) values. You can get unexpected results like truncation of values. If you assign a pointer to a 64-bit long, pass it as a 32-bit int function argument, and convert the function result back into a pointer, then the upper 4 bytes will be stripped off due to the smaller int argument.

If you are seeing problems with data truncation, try using the -Wconversion compiler flag. This flag will cause the compiler to warn you about any data conversions it thinks are suspect. This will generate warnings for some legitimate conversions, but it will be a place to start looking for the problem.

There are also some pre-defined types to hold values that might overflow an int:

uintptr_t | Use this as the destination when casting between 64-bit pointers and integer types.
ptrdiff_t | An integer sufficiently large to hold the result of pointer arithmetic.
size_t | The type that sizeof returns. This has become a 64-bit value.
fpos_t | An integer sufficiently large to hold a file position. This type is primarily used with the standard C library, such as with fgetpos().
off_t | An integer sufficiently large to hold a file offset. This type is used by system calls, such as lseek().

Also, never assume you know the size of any type or structure. Always use sizeof.

The OS helps you catch some pointer truncation errors by making the first four gigabytes of a 64-bit process’ address space to be one huge zero page. Any attempt to read, write, or execute from this address range will result in a program crash. This forces all of your 64-bit pointers to have significant bits in the upper four bytes, causing any pointer truncation to end up with a value of zero there, at least when using Intel processors.

Bitmasks of type long have some gotchas. By default, masks expressed in code as constants are treated as an unsigned int, meaning that any significant digits implicitly added by the compiler will be zeros. That is fine if you want zeros in the upper bits of your mask. If you want ones in the upper part of your mask, you’ll want to write the mask as the bitwise inverse of the mask’s inverse. If you’re wanting the mask 0xfffc to be sign-extended through all 64 bits, you’ll want to do something like the second line. The first line shows you what bitmask would result without using this inversion trick:

0​x​f​f​f​f​f​f​f​c​ ​/​/​ ​0​x​0​0​0​0​0​0​0​0​f​f​f​f​f​f​f​c​ ​(​6​4​ ​b​i​t​s​)​
~​0​x​3​ ​ ​ ​ ​ ​ ​ ​/​/​ ​0​x​f​f​f​f​f​f​f​c​ ​(​3​2​ ​b​i​t​s​)​ ​o​r​ ​0​x​f​f​f​f​f​f​f​f​f​f​f​f​f​f​f​c​ ​(​6​4​ ​b​i​t​s​)​

Be careful of making assumptions about how many bits are in a long if you are shifting through its bits. Use the LONG_BIT constant to figure out the number of bits involved.

Cocoa on Leopard introduces the NSInteger and NSUInteger types. These are primitive types that are 32 bits wide in 32-bit code and 64 bits wide in 64-bit code. Most of the integral types used as Cocoa method and function parameters have been changed to be NS[U]Integers. Even though it is called “NSInteger,” these are not classes like NSNumber.

So why introduce these new types? Prior to 64-bit support, Cocoa methods took and returned integer values using native C types. -[NSArray count] returns an unsigned int, for instance. This return value would be 32 bits in LP64. It would be nice to be able to accept and return larger values. The Cocoa team could either change all ints to longs, but that would break 32-bit binary compatibility. By introducing a layer of abstraction with the NS[U]Integer types, Apple can build Cocoa with 32-bit integer types for 32-bit land and maintain binary compatibility, and at the same time build Cocoa with 64-bit integers.

The CGFloat is the floating-point equivalent to NSInteger. It is a float (32 bits) in 32-bit code and a double (64 bits) in 64-bit code. Graphical quantities such as NSRect and NSPoint use CGFloats now. In 64-bit code, CGRect and NSRect are identical, so there’s no need to play typecast games when converting between CGRect and NSRect. You can define the preprocessor token NS_BUILD_32_LIKE_64 to have your 32-bit code use these types.

Should you go 64-bit?

So, should you immediately go for 64 bits? Sure, if you need it now. For some developers, 64-bit computing will not have much of an immediate impact.

You truly need a 64-bit address space when you need random access to huge data objects (greater than 2 GiB) or you need concurrent access to a quantity of data that will not fit into a 32-bit address space, like multi-gig data modeling, data mining, web caches, large-scale 3D rendering, very large databases, etc.

If your app uses a streaming data access model, or just uses 64-bit integer math, you do not absolutely need 64-bit computing. You also don’t need 64-bit computing when dealing with very large files. The file system API is capable of handling 64-bit offsets.

You may see some performance improvement going to 64 bits on Intel processors. The 64-bit ABI has more registers available to it (16 vs. 8), and the registers are wider (64 vs. 32 bits). The 64-bit calling conventions are also register-based rather than stack-based.

You will not see much, if any, performance improvement with 64-bit PowerPC, since the PowerPC family was designed from the outset for 64-bitness. If anything, you could see a performance decrease: 64-bit code is larger, and it deals with larger data, so cache misses will happen more often. Larger apps and larger data can require more memory and may end up paging if there is not enough physical RAM on the machine.

Instruction sequences to get an address or a constant into a register are longer in 64-bit code. And some situations, such as using a 32-bit signed integer as an array index, will require the value to be sign-extended on every access if it is not stored in a register.

On the other hand, most of the applications shipped by Apple are 64-bit native, with iTunes being the main holdout. If your app is 32-bits, and no other 32-bit applications are running, the system will need to load all of the 32-bit frameworks it uses, which will increase overall system memory usage and delay your launch time.

New Objective-C runtime

Apple has upgraded the Objective-C runtime for 64-bit programming. This new runtime is also used on iOS.

The new runtime solves the fragile base class problem for Objective-C. When you access instance variables in Objective-C, the compiler turns the reference into a pointer+offset operation. Each instance variable lives at a fixed offset from the beginning of the object. Subclasses can add instance variables, which just get stuck at the end of the object structure. This makes it impossible for the base class to add or remove any instance variables or change the size of any of them (say, to move from 32-bit to 64-bit pointers). The new runtime solves this problem by waiting until runtime to calculate the instance variable offsets.

This means you can’t think of an Objective-C object as a fixed-size struct. If, for whatever reason, you were using sizeof() against an object, you would need to make another call such as class_getInstanceSize(). Similarly, offsetof(), which gives you the offset from the base pointer for an instance variable, must be replaced by a runtime call like ivar_getOffset(). Also, the @defs operator, which gives you the instance variable layout for a class, is no longer useful.

The new runtime adds stricter instance variable access control. Before, you could access @private instance variables. With the modern runtime, you can get a link error if you try directly accessing an instance variable of an object in a different framework.

Universally Fat Binaries

Mach-O supports “fat” files, officially called “universal” files by Apple. These let you have PowerPC, Intel, and the 32-bit and 64-bit flavors of each kind of code in the same file. This allows for a single application or a single framework to service all the computing worlds.

Fat binaries from the command line

You can use gcc on the command-line, or in makefiles, to generate object code for any of the supported architectures. You can also build the final fat binaries using these command-line tools.

gcc’s -arch flag controls which architecture(s) it should build. For Mac OS X, you can give -arch the ppc, i386, ppc64, and x86_64 flags. iOS is an ARM architecture, so you would use armv6 or armv7 if you were constructing your own gcc commands. armv7-capable processors appeared in the iPhone 3GS and later.

Example 2.3. sizeprinter.c

#​i​n​c​l​u​d​e​ ​<​s​t​d​i​o​.​h​>​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​s​i​z​e​o​f​(​i​n​t​*​)​ ​i​s​ ​%​z​u​\​n​"​,​ ​s​i​z​e​o​f​(​i​n​t​*​)​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​

}​ ​/​/​ ​m​a​i​n​

Compiling this code in 64-bit mode and running it on a 32-bit machine, such as a first-generation MacBook, will get you rejected:

m​a​c​b​o​o​k​$​ ​g​c​c​ ​-​a​r​c​h​ ​x​8​6​_​6​4​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​s​i​z​e​p​r​i​n​t​e​r​ ​s​i​z​e​p​r​i​n​t​e​r​.​c​
m​a​c​b​o​o​k​$​ ​.​/​s​i​z​e​p​r​i​n​t​e​r​
.​/​s​i​z​e​p​r​i​n​t​e​r​:​ ​B​a​d​ ​C​P​U​ ​t​y​p​e​ ​i​n​ ​e​x​e​c​u​t​a​b​l​e​.​

The program works fine when run on a 64-bit capable machine like a MacBookPro:

m​a​c​p​r​o​$​ ​.​/​s​i​z​e​p​r​i​n​t​e​r​
s​i​z​e​o​f​(​i​n​t​*​)​ ​i​s​ ​8​

You will still get rejected if you try running it on the wrong architecture. In this case, an x86_64 executable on a G4:

g​4​$​ ​.​/​s​i​z​e​p​r​i​n​t​e​r​
.​/​s​i​z​e​p​r​i​n​t​e​r​:​ ​B​a​d​ ​C​P​U​ ​t​y​p​e​ ​i​n​ ​e​x​e​c​u​t​a​b​l​e​.​

It is legal to run a PowerPC executable on Intel on Leopard, thanks to the Rosetta emulation technology. Snow Leopard has moved Rosetta to an optional install, so it might not be available on all user’s machines.

m​a​c​b​o​o​k​$​ ​g​c​c​ ​-​a​r​c​h​ ​p​p​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​s​i​z​e​p​r​i​n​t​e​r​ ​s​i​z​e​p​r​i​n​t​e​r​.​c​
m​a​c​b​o​o​k​$​ ​.​/​s​i​z​e​p​r​i​n​t​e​r​
s​i​z​e​o​f​(​i​n​t​*​)​ ​i​s​ ​4​

Rosetta does not support 64-bit PowerPC, just 32-bit.

An easy way to create a fat binary is to give gcc all of the architectures on the command line at once. Here is how to make a fat binary using the code from Example 2.3.

g​4​$​ ​g​c​c​ ​-​a​r​c​h​ ​p​p​c​ ​-​a​r​c​h​ ​p​p​c​6​4​ ​-​a​r​c​h​ ​i​3​8​6​ ​-​a​r​c​h​ ​x​8​6​_​6​4​ ​\​
 ​ ​ ​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​s​i​z​e​p​r​i​n​t​e​r​ ​s​i​z​e​p​r​i​n​t​e​r​.​c​

g​4​$​ ​.​/​s​i​z​e​p​r​i​n​t​e​r​
s​i​z​e​o​f​(​i​n​t​*​)​ ​i​s​ ​4​

and the same executable run on a G5:

g​5​$​ ​.​/​s​i​z​e​p​r​i​n​t​e​r​
s​i​z​e​o​f​(​i​n​t​*​)​ ​i​s​ ​8​

and the same executable run on a 32-bit MacBook:

3​8​6​$​ ​.​/​s​i​z​e​p​r​i​n​t​e​r​
s​i​z​e​o​f​(​i​n​t​*​)​ ​i​s​ ​4​

The file command will tell you about the fatness of a program:

$​ ​f​i​l​e​ ​s​i​z​e​p​r​i​n​t​e​r​
s​i​z​e​p​r​i​n​t​e​r​:​ ​M​a​c​h​-​O​ ​u​n​i​v​e​r​s​a​l​ ​b​i​n​a​r​y​ ​w​i​t​h​ ​4​ ​a​r​c​h​i​t​e​c​t​u​r​e​s​
s​i​z​e​p​r​i​n​t​e​r​ ​(​f​o​r​ ​a​r​c​h​i​t​e​c​t​u​r​e​ ​p​p​c​7​4​0​0​)​:​ ​M​a​c​h​-​O​ ​e​x​e​c​u​t​a​b​l​e​ ​p​p​c​
s​i​z​e​p​r​i​n​t​e​r​ ​(​f​o​r​ ​a​r​c​h​i​t​e​c​t​u​r​e​ ​p​p​c​6​4​)​:​ ​ ​ ​M​a​c​h​-​O​ ​6​4​-​b​i​t​ ​e​x​e​c​u​t​a​b​l​e​ ​p​p​c​6​4​
s​i​z​e​p​r​i​n​t​e​r​ ​(​f​o​r​ ​a​r​c​h​i​t​e​c​t​u​r​e​ ​i​3​8​6​)​:​ ​ ​ ​ ​M​a​c​h​-​O​ ​e​x​e​c​u​t​a​b​l​e​ ​i​3​8​6​
s​i​z​e​p​r​i​n​t​e​r​ ​(​f​o​r​ ​a​r​c​h​i​t​e​c​t​u​r​e​ ​x​8​6​_​6​4​)​:​ ​ ​M​a​c​h​-​O​ ​6​4​-​b​i​t​ ​e​x​e​c​u​t​a​b​l​e​ ​x​8​6​_​6​4​

Your file actually gets compiled multiple times when you specify multiple architectures, and the resulting set of object code is merged into the output file. Example 2.4 uses some preprocessor macros to figure what mode the compiler is currently in. __ppc__ is defined when compiling in 32-bit PowerPC mode, __ppc64__ when compiling in PPC 64-bit mode, __i386__ when compiling for Intel 32-bit, and __x86_64__ when compiling for Intel 64-bit.

__LP64__ is defined when using the LP64 model, so it will be defined when __ppc64__ or __x86_64__ is defined. If you are mainly interested in whether you are compiling for 64 bits, use __LP64__ rather than one of the specific chip architecture symbols.

Example 2.4 uses the #warning preprocessor directive to tell us what chunks of code are being compiled at any particular point in time.

Example 2.4. fat-macro-warn.c

/​*​ ​c​o​m​p​i​l​e​ ​w​i​t​h​
g​c​c​ ​-​o​ ​f​a​t​-​m​a​c​r​o​-​w​a​r​n​ ​f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​
o​r​
g​c​c​ ​-​a​r​c​h​ ​p​p​c​6​4​ ​-​o​ ​f​a​t​-​m​a​c​r​o​-​w​a​r​n​ ​f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​
o​r​
g​c​c​ ​-​a​r​c​h​ ​p​p​c​6​4​ ​-​a​r​c​h​ ​p​p​c​ ​-​a​r​c​h​ ​x​8​6​_​6​4​ ​\​
 ​ ​ ​ ​-​a​r​c​h​ ​i​3​8​6​ ​-​o​ ​f​a​t​-​m​a​c​r​o​-​w​a​r​n​ ​f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​
*​/​

i​n​t​ ​m​a​i​n​(​v​o​i​d​)​ ​{​
#​w​a​r​n​i​n​g​ ​c​o​m​p​i​l​i​n​g​ ​t​h​e​ ​f​i​l​e​

#​i​f​d​e​f​ ​_​_​L​P​6​4​_​_​
#​w​a​r​n​i​n​g​ ​i​n​ ​L​P​6​4​
#​e​n​d​i​f​

#​i​f​d​e​f​ ​_​_​p​p​c​6​4​_​_​
#​w​a​r​n​i​n​g​ ​i​n​ ​_​_​p​p​c​6​4​_​_​
#​e​n​d​i​f​

#​i​f​d​e​f​ ​_​_​p​p​c​_​_​
#​w​a​r​n​i​n​g​ ​i​n​ ​_​_​p​p​c​_​_​
#​e​n​d​i​f​

#​i​f​d​e​f​ ​_​_​i​3​8​6​_​_​
#​w​a​r​n​i​n​g​ ​i​n​ ​_​_​i​3​8​6​_​_​
#​e​n​d​i​f​

#​i​f​d​e​f​ ​_​_​x​8​6​_​6​4​_​_​
#​w​a​r​n​i​n​g​ ​i​n​ ​_​_​x​8​6​_​6​4​_​_​
#​e​n​d​i​f​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

Compiling it without extra flags on a G4 or G5 tells us the __ppc__ section gets compiled. On an Intel system, it will be compiling with __i386__ defined.

g​4​$​ ​g​c​c​ ​-​o​ ​f​a​t​-​m​a​c​r​o​-​w​a​r​n​ ​f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​
f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​:​1​2​:​2​:​ ​w​a​r​n​i​n​g​:​ ​#​w​a​r​n​i​n​g​ ​c​o​m​p​i​l​i​n​g​ ​t​h​e​ ​f​i​l​e​
f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​:​2​3​:​2​:​ ​w​a​r​n​i​n​g​:​ ​#​w​a​r​n​i​n​g​ ​i​n​ ​_​_​p​p​c​_​_​

Compiling it with -arch ppc64 tells us it finds the __ppc64__ and __LP64__ sections:

g​4​$​ ​g​c​c​ ​-​a​r​c​h​ ​p​p​c​6​4​ ​-​o​ ​f​a​t​-​m​a​c​r​o​-​w​a​r​n​ ​f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​
f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​:​1​2​:​2​:​ ​w​a​r​n​i​n​g​:​ ​#​w​a​r​n​i​n​g​ ​c​o​m​p​i​l​i​n​g​ ​t​h​e​ ​f​i​l​e​
f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​:​1​5​:​2​:​ ​w​a​r​n​i​n​g​:​ ​#​w​a​r​n​i​n​g​ ​i​n​ ​L​P​6​4​
f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​:​1​9​:​2​:​ ​w​a​r​n​i​n​g​:​ ​#​w​a​r​n​i​n​g​ ​i​n​ ​_​_​p​p​c​6​4​_​_​

And, finally, here is compiling for all four architectures at once:

%​ ​g​c​c​ ​-​a​r​c​h​ ​p​p​c​6​4​ ​-​a​r​c​h​ ​p​p​c​ ​-​a​r​c​h​ ​x​8​6​_​6​4​ ​-​a​r​c​h​ ​i​3​8​6​ ​-​o​ ​f​a​t​-​m​a​c​r​o​-​w​a​r​n​ ​f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​
f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​:​1​2​:​2​:​ ​w​a​r​n​i​n​g​:​ ​#​w​a​r​n​i​n​g​ ​c​o​m​p​i​l​i​n​g​ ​t​h​e​ ​f​i​l​e​
f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​:​2​3​:​2​:​ ​w​a​r​n​i​n​g​:​ ​#​w​a​r​n​i​n​g​ ​i​n​ ​_​_​p​p​c​_​_​
f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​:​1​2​:​2​:​ ​w​a​r​n​i​n​g​:​ ​#​w​a​r​n​i​n​g​ ​c​o​m​p​i​l​i​n​g​ ​t​h​e​ ​f​i​l​e​
f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​:​1​5​:​2​:​ ​w​a​r​n​i​n​g​:​ ​#​w​a​r​n​i​n​g​ ​i​n​ ​L​P​6​4​
f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​:​1​9​:​2​:​ ​w​a​r​n​i​n​g​:​ ​#​w​a​r​n​i​n​g​ ​i​n​ ​_​_​p​p​c​6​4​_​_​
f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​:​1​2​:​2​:​ ​w​a​r​n​i​n​g​:​ ​#​w​a​r​n​i​n​g​ ​c​o​m​p​i​l​i​n​g​ ​t​h​e​ ​f​i​l​e​
f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​:​2​7​:​2​:​ ​w​a​r​n​i​n​g​:​ ​#​w​a​r​n​i​n​g​ ​i​n​ ​_​_​i​3​8​6​_​_​
f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​:​1​2​:​2​:​ ​w​a​r​n​i​n​g​:​ ​#​w​a​r​n​i​n​g​ ​c​o​m​p​i​l​i​n​g​ ​t​h​e​ ​f​i​l​e​
f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​:​1​5​:​2​:​ ​w​a​r​n​i​n​g​:​ ​#​w​a​r​n​i​n​g​ ​i​n​ ​L​P​6​4​
f​a​t​-​m​a​c​r​o​-​w​a​r​n​.​c​:​3​1​:​2​:​ ​w​a​r​n​i​n​g​:​ ​#​w​a​r​n​i​n​g​ ​i​n​ ​_​_​x​8​6​_​6​4​_​_​

Notice the order, or lack thereof; it changes on each invocation of the command. gcc is parallelizing the compilation. If you watch Xcode’s build log, you can see that it actually compiles each architecture with its own command. This keeps the warnings and errors from becoming confusingly intermixed and also allows Xcode to apply per-architecture build settings. You do not have to compile multiple architectures at the same time. Instead, you can compile them separately and then use the lipo command to assemble them together into fat versions. “Lipo” comes from the Greek word for “fat,” which is why “fat” has been used in this chapter.

Be aware that the term “Universal” means different things on Mac OS X and iOS. On Mac OS X, universal binaries have different independent chip binaries packaged together. Different code is run whether it is run on a G4 or a 64-bit Intel machine. On iOS, a universal app is one that runs both on the iPhone and the iPad, but it’s same executable running on both devices. You make runtime checks to decide whether to enable iPad functionality and to choose whether to load iPhone or iPad nib files.

Continuing on with Example 2.3, here are steps that will create four object files and one fat binary. Here are the PowerPC versions:

$​ ​g​c​c​ ​-​a​r​c​h​ ​p​p​c​ ​-​g​ ​-​o​ ​s​i​z​e​p​r​i​n​t​e​r​-​3​2​ ​s​i​z​e​p​r​i​n​t​e​r​.​c​
$​ ​g​c​c​ ​-​a​r​c​h​ ​p​p​c​6​4​ ​-​g​ ​-​o​ ​s​i​z​e​p​r​i​n​t​e​r​-​6​4​ ​s​i​z​e​p​r​i​n​t​e​r​.​c​

And the Intel versions:

$​ ​g​c​c​ ​-​a​r​c​h​ ​i​3​8​6​ ​-​g​ ​-​o​ ​s​i​z​e​p​r​i​n​t​e​r​-​i​3​2​ ​s​i​z​e​p​r​i​n​t​e​r​.​c​
$​ ​g​c​c​ ​-​a​r​c​h​ ​x​8​6​_​6​4​ ​-​g​ ​-​o​ ​s​i​z​e​p​r​i​n​t​e​r​-​i​6​4​ ​s​i​z​e​p​r​i​n​t​e​r​.​c​

Join them with lipo and use the file command to verify the result is indeed a fat binary:

$​ ​l​i​p​o​ ​-​c​r​e​a​t​e​ ​-​o​u​t​p​u​t​ ​s​i​z​e​p​r​i​n​t​e​r​-​f​a​t​ ​\​
 ​ ​s​i​z​e​p​r​i​n​t​e​r​-​3​2​ ​s​i​z​e​p​r​i​n​t​e​r​-​6​4​ ​ ​ ​s​i​z​e​p​r​i​n​t​e​r​-​i​3​2​ ​s​i​z​e​p​r​i​n​t​e​r​-​i​6​4​
$​ ​f​i​l​e​ ​s​i​z​e​p​r​i​n​t​e​r​-​f​a​t​
s​i​z​e​p​r​i​n​t​e​r​:​ ​M​a​c​h​-​O​ ​u​n​i​v​e​r​s​a​l​ ​b​i​n​a​r​y​ ​w​i​t​h​ ​4​ ​a​r​c​h​i​t​e​c​t​u​r​e​s​
s​i​z​e​p​r​i​n​t​e​r​ ​(​f​o​r​ ​a​r​c​h​i​t​e​c​t​u​r​e​ ​p​p​c​7​4​0​0​)​:​ ​M​a​c​h​-​O​ ​e​x​e​c​u​t​a​b​l​e​ ​p​p​c​
s​i​z​e​p​r​i​n​t​e​r​ ​(​f​o​r​ ​a​r​c​h​i​t​e​c​t​u​r​e​ ​p​p​c​6​4​)​:​ ​ ​ ​M​a​c​h​-​O​ ​6​4​-​b​i​t​ ​e​x​e​c​u​t​a​b​l​e​ ​p​p​c​6​4​
s​i​z​e​p​r​i​n​t​e​r​ ​(​f​o​r​ ​a​r​c​h​i​t​e​c​t​u​r​e​ ​i​3​8​6​)​:​ ​ ​ ​ ​M​a​c​h​-​O​ ​e​x​e​c​u​t​a​b​l​e​ ​i​3​8​6​
s​i​z​e​p​r​i​n​t​e​r​ ​(​f​o​r​ ​a​r​c​h​i​t​e​c​t​u​r​e​ ​x​8​6​_​6​4​)​:​ ​ ​M​a​c​h​-​O​ ​6​4​-​b​i​t​ ​e​x​e​c​u​t​a​b​l​e​ ​x​8​6​_​6​4​

You can use the posix_spawn() function to control which architecture is run if you are launching your own process.

Fat binaries in Xcode

Xcode will of course let you create fat binaries in any permutation you want. The user interface for choosing which architectures to use seems to change with every Xcode version. Searching for “arch” in the Build Settings will get you to the place where you can make the settings.

Fat binary considerations

Even though Xcode makes it very easy to make N-way executables, the decision whether to go to a 2-way or 4-way fat binary is not one to take lightly because your testing matrix gets larger. A fat 32-bit executable requires testing both the Intel and PowerPC versions. You can do much of your 32-bit PowerPC testing in Rosetta on an Intel machine. Performance will be different, and it is good to test on a real live PowerPC machine to cover any unknown corner cases. Adding both 64-bit versions doubles the testing and verification burden.

Back during the Mac’s first architecture transition (from the Motorola 68000 to PowerPC), there was a clever mixed mode system where a single executable could execute either kind of code. There is no “mixed mode” in OS X between the Intel and PowerPC worlds and the 32-bit and 64-bit worlds.

As mentioned before, this means that there are Intel and PowerPC, 32-bit and 64-bit versions of the system frameworks. If you have a have a bunch of 32-bit applications running and then you run a 64-bit program, all of the 64-bit frameworks will need to be brought into memory, which can take a noticeable amount of time on first launch, not to mention the additional memory consumption. Eventually all applications will be 64-bit, and 32-bit applications will become the bad guys, dragging in system frameworks that only they are using.

Also, you can run into issues if your program supports plug-ins. The plug-ins need to match the host application’s architecture. If you ship a 4-way executable, then your plug-ins will need to be 4-way also. This may have an impact on any third parties that supply plug-ins.

3
Blocks

One shortcoming of C and Objective-C is code that performs a particular task becomes scattered around the code base. Say you are performing an operation on a C array of People structs and you need to sort the list by last name. Luckily, there is the library function qsort() to sort the array. qsort() is well-optimized and does its job well, but it needs some help. It does not know how items are compared to each other so that they end up in the proper sorted order.

.​.​.​
q​s​o​r​t​ ​(​p​e​o​p​l​e​A​r​r​a​y​,​ ​c​o​u​n​t​,​ ​s​i​z​e​o​f​(​P​e​r​s​o​n​)​,​ ​p​e​r​s​o​n​N​a​m​e​C​o​m​p​a​r​e​)​;​
.​.​.​

i​n​t​ ​p​e​r​s​o​n​N​a​m​e​C​o​m​p​a​r​e​ ​(​c​o​n​s​t​ ​v​o​i​d​ ​*​t​h​i​n​g​1​,​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​t​h​i​n​g​2​)​ ​{​
 ​ ​ ​ ​P​e​r​s​o​n​ ​*​p​1​ ​=​ ​(​P​e​r​s​o​n​ ​*​)​ ​t​h​i​n​g​1​;​
 ​ ​ ​ ​P​e​r​s​o​n​ ​*​p​2​ ​=​ ​(​P​e​r​s​o​n​ ​*​)​ ​t​h​i​n​g​2​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​t​r​c​m​p​(​p​1​-​>​l​a​s​t​N​a​m​e​,​ ​p​2​-​>​l​a​s​t​N​a​m​e​)​;​
}​ ​/​/​ ​c​o​m​p​a​r​e​

The compare function is not close to the invocation of qsort(). If you want to know exactly what the comparison is doing while you are reading or maintaining the code, you will need to find personNameCompare(), perhaps look at surrounding code, and then return to your original place.

Similarly, in Cocoa you frequently initiate some process that is handled in a place distant from the initial call, kicking asynchronous work that is completed in callbacks. Say the user pushes a button to load an image, and an NSOpenPanel is displayed:

-​ ​(​I​B​A​c​t​i​o​n​)​ ​s​t​a​r​t​I​m​a​g​e​L​o​a​d​:​ ​(​i​d​)​ ​s​e​n​d​e​r​ ​{​
 ​ ​ ​ ​N​S​O​p​e​n​P​a​n​e​l​ ​*​p​a​n​e​l​ ​=​ ​[​N​S​O​p​e​n​P​a​n​e​l​ ​o​p​e​n​P​a​n​e​l​]​;​
 ​ ​ ​ ​.​.​.​
 ​ ​ ​[​p​a​n​e​l​ ​b​e​g​i​n​S​h​e​e​t​F​o​r​D​i​r​e​c​t​o​r​y​:​ ​n​i​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​.​.​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​m​o​d​a​l​D​e​l​e​g​a​t​e​:​ ​s​e​l​f​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​d​E​n​d​S​e​l​e​c​t​o​r​:​ ​@​s​e​l​e​c​t​o​r​(​o​p​e​n​P​a​n​e​l​D​i​d​E​n​d​:​r​e​t​u​r​n​C​o​d​e​:​c​o​n​t​e​x​t​I​n​f​o​:​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​e​x​t​I​n​f​o​:​ ​n​i​l​]​;​
}​ ​/​/​ ​s​t​a​r​t​I​m​a​g​e​L​o​a​d​

The callback method is somewhere else in the source file:

-​ ​(​v​o​i​d​)​ ​o​p​e​n​P​a​n​e​l​D​i​d​E​n​d​:​ ​(​N​S​O​p​e​n​P​a​n​e​l​ ​*​)​ ​s​h​e​e​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​C​o​d​e​:​ ​(​i​n​t​)​ ​c​o​d​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​e​x​t​I​n​f​o​:​ ​(​v​o​i​d​ ​*​)​ ​c​o​n​t​e​x​t​ ​{​
 ​ ​ ​ ​i​f​ ​(​c​o​d​e​ ​=​=​ ​N​S​O​K​B​u​t​t​o​n​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​f​i​l​e​n​a​m​e​s​ ​=​ ​[​s​h​e​e​t​ ​f​i​l​e​n​a​m​e​s​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​D​o​ ​s​t​u​f​f​ ​w​i​t​h​ ​f​i​l​e​n​a​m​e​s​.​
 ​ ​ ​ ​}​
}​ ​/​/​ ​o​p​e​n​P​a​n​e​l​D​i​d​E​n​d​

The initiation of the open panel is separated both in time and space from the handling of the open panel’s results. You need to find a place to hide any data that needs to be communicated from one place to another, such as in a context parameter, instance variable, or global variable.

Wouldn’t it be nice to have these auxiliary chunks of code near where they are being invoked? That way you can take in the entirety of an operation in a single screenful of code without having to hop around your codebase.

Blocks are a new feature added by Apple to the C family of languages, available in Mac OS X 10.6 and later and iOS 4.0 and later. Blocks allow you to put code that does work on behalf of other code in one place.

The qsort() function call would look this when expressed with blocks:

q​s​o​r​t​_​b​ ​(​e​l​e​m​e​n​t​s​,​ ​c​o​u​n​t​,​ ​s​i​z​e​o​f​(​e​l​e​m​e​n​t​)​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​^​(​c​o​n​s​t​ ​v​o​i​d​ ​*​t​h​i​n​g​1​,​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​t​h​i​n​g​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​P​e​r​s​o​n​ ​*​p​1​ ​=​ ​(​P​e​r​s​o​n​ ​*​)​ ​t​h​i​n​g​1​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​P​e​r​s​o​n​ ​*​p​2​ ​=​ ​(​P​e​r​s​o​n​ ​*​)​ ​t​h​i​n​g​2​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​s​t​r​c​m​p​(​p​1​-​>​l​a​s​t​N​a​m​e​,​ ​p​2​-​>​l​a​s​t​N​a​m​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​}​ ​)​;​

The open panel code would look something like this:

[​p​a​n​e​l​ ​b​e​g​i​n​S​h​e​e​t​M​o​d​a​l​F​o​r​W​i​n​d​o​w​:​ ​w​i​n​d​o​w​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​.​.​.​
 ​ ​ ​ ​ ​ ​ ​c​o​m​p​l​e​t​i​o​n​H​a​n​d​l​e​r​:​ ​^​(​N​S​I​n​t​e​g​e​r​ ​r​e​s​u​l​t​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​N​S​O​K​B​u​t​t​o​n​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​f​i​l​e​N​a​m​e​s​ ​=​ ​[​s​h​e​e​t​ ​f​i​l​e​n​a​m​e​s​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​d​o​ ​s​t​u​f​f​ ​w​i​t​h​ ​f​i​l​e​N​a​m​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​}​ ​]​;​

Block Syntax

A block is simply a piece of inline code. Here is an NSBlockOperation that logs a line of text when the operation is scheduled to run.

 ​ ​ ​ ​N​S​B​l​o​c​k​O​p​e​r​a​t​i​o​n​ ​*​b​l​o​c​k​o​p​;​
 ​ ​ ​ ​b​l​o​c​k​o​p​ ​=​ ​[​N​S​B​l​o​c​k​O​p​e​r​a​t​i​o​n​ ​b​l​o​c​k​O​p​e​r​a​t​i​o​n​W​i​t​h​B​l​o​c​k​:​ ​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​T​h​e​ ​o​p​e​r​a​t​i​o​n​ ​b​l​o​c​k​ ​w​a​s​ ​i​n​v​o​k​e​d​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​]​;​

The block is introduced by the caret with the code of the block surrounded by braces. Bill Bumgarner from Apple said that the caret was chosen because “it is the only unary operator that cannot be overloaded in C++, and the snowman Block Syntax is out because we can’t use unicode.”

The code inside of the block is not executed at the same time as the function or method call that contains the block. The NSLog above will not be executed when the NSBlockOperation has been created; instead, it will be called at a later time when the operation is finally run.

Blocks can take arguments. NSArray’s -enumerateObjectsUsingBlock: will enumerate all objects in the array, invoking the block for each one. The block takes three parameters: the object to look at, the index of the object in the array, and a stop pointer to a BOOL. Setting *stop to YES will cause the iteration to cease before the array has been exhausted:

N​S​A​r​r​a​y​ ​*​a​r​r​a​y​ ​=​ ​[​N​S​A​r​r​a​y​ ​a​r​r​a​y​W​i​t​h​O​b​j​e​c​t​s​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​"​h​i​"​,​ ​@​"​b​o​r​k​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​"​b​a​d​g​e​r​"​,​ ​@​"​g​r​e​e​b​l​e​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​"​b​a​d​g​e​r​i​f​i​c​"​,​ ​n​i​l​]​;​

[​a​r​r​a​y​ ​e​n​u​m​e​r​a​t​e​O​b​j​e​c​t​s​U​s​i​n​g​B​l​o​c​k​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​^​(​i​d​ ​o​b​j​e​c​t​,​ ​N​S​U​I​n​t​e​g​e​r​ ​i​n​d​e​x​,​ ​B​O​O​L​ ​*​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​o​b​j​e​c​t​ ​a​t​ ​i​n​d​e​x​ ​%​d​ ​i​s​ ​%​@​"​,​ ​i​n​d​e​x​,​ ​o​b​j​e​c​t​)​;​
 ​ ​ ​ ​}​]​;​

will print:

 ​ ​o​b​j​e​c​t​ ​a​t​ ​i​n​d​e​x​ ​0​ ​i​s​ ​h​i​
 ​ ​o​b​j​e​c​t​ ​a​t​ ​i​n​d​e​x​ ​1​ ​i​s​ ​b​o​r​k​
 ​ ​o​b​j​e​c​t​ ​a​t​ ​i​n​d​e​x​ ​2​ ​i​s​ ​b​a​d​g​e​r​
 ​ ​o​b​j​e​c​t​ ​a​t​ ​i​n​d​e​x​ ​3​ ​i​s​ ​g​r​e​e​b​l​e​
 ​ ​o​b​j​e​c​t​ ​a​t​ ​i​n​d​e​x​ ​4​ ​i​s​ ​b​a​d​g​e​r​i​f​i​c​

Return Values

Blocks can also return values. The return type of the block can be specified after the caret, but if you omit it, the compiler will try to infer as much information about the return value as it can, allowing you to write more succinct code. This is a fully qualified block literal for NSArray’s -indexesOfObjectsPassingTest: method:

^​B​O​O​L​ ​(​i​d​ ​o​b​j​e​c​t​,​ ​N​S​U​I​n​t​e​g​e​r​ ​i​n​d​e​x​,​ ​B​O​O​L​ ​*​s​t​o​p​)​ ​{​ ​r​e​t​u​r​n​ ​Y​E​S​;​ ​}​

But you can reduce it a bit because the return type will be inferred:

^​(​i​d​ ​o​b​j​e​c​t​,​ ​N​S​U​I​n​t​e​g​e​r​ ​i​n​d​e​x​,​ ​B​O​O​L​ ​*​s​t​o​p​)​ ​{​ ​r​e​t​u​r​n​ ​Y​E​S​;​ ​}​

A block that takes no arguments and returns no value can be drastically reduced:

^​v​o​i​d​ ​(​v​o​i​d​)​ ​{​ ​.​.​.​ ​}​
^​(​v​o​i​d​)​ ​{​ ​.​.​.​ ​}​
^​{​ ​.​.​.​ ​}​;​

All three are equivalent: a block that takes no arguments and returns no values.

Rather than printing out each element of the array, say you want to know which elements contain the word “badger.” -indexesOfObjectsPassingTest: will invoke a block for each object in the array. It uses a return value of YES or NO to control whether that object’s index is added to the index set that is ultimately returned.

N​S​I​n​d​e​x​S​e​t​ ​*​i​n​d​i​c​e​s​ ​=​
 ​ ​ ​ ​[​a​r​r​a​y​ ​i​n​d​e​x​e​s​O​f​O​b​j​e​c​t​s​P​a​s​s​i​n​g​T​e​s​t​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​^​(​i​d​ ​o​b​j​e​c​t​,​ ​N​S​U​I​n​t​e​g​e​r​ ​i​n​d​e​x​,​ ​B​O​O​L​ ​*​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​R​a​n​g​e​ ​m​a​t​c​h​ ​=​ ​[​o​b​j​e​c​t​ ​r​a​n​g​e​O​f​S​t​r​i​n​g​:​ ​@​"​b​a​d​g​e​r​"​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​m​a​t​c​h​.​l​o​c​a​t​i​o​n​ ​!​=​ ​N​S​N​o​t​F​o​u​n​d​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​Y​E​S​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​N​O​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​}​]​;​

N​S​L​o​g​ ​(​@​"​%​@​"​,​ ​i​n​d​i​c​e​s​)​;​

This prints:

<​N​S​I​n​d​e​x​S​e​t​>​ ​[​n​u​m​b​e​r​ ​o​f​ ​i​n​d​e​x​e​s​:​ ​2​ ​(​i​n​ ​2​ ​r​a​n​g​e​s​)​,​ ​i​n​d​e​x​e​s​:​ ​(​2​ ​4​)​]​

which corresponds to “badger” and “badgerific.”

Accessing Enclosing Scope

Blocks can also access their enclosing scope. Say you wanted to print out each of the words that contains “badger.” You can use a variation of the previous code, using -enumerateObjectsUsingBlock:

[​a​r​r​a​y​ ​e​n​u​m​e​r​a​t​e​O​b​j​e​c​t​s​U​s​i​n​g​B​l​o​c​k​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​^​(​i​d​ ​o​b​j​e​c​t​,​ ​N​S​U​I​n​t​e​g​e​r​ ​i​n​d​e​x​,​ ​B​O​O​L​ ​*​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​R​a​n​g​e​ ​m​a​t​c​h​ ​=​ ​[​o​b​j​e​c​t​ ​r​a​n​g​e​O​f​S​t​r​i​n​g​:​ ​@​"​b​a​d​g​e​r​"​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​m​a​t​c​h​.​l​o​c​a​t​i​o​n​ ​!​=​ ​N​S​N​o​t​F​o​u​n​d​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​f​o​u​n​d​ ​a​ ​'​%​@​'​ ​:​ ​%​@​"​,​ ​@​"​b​a​d​g​e​r​"​,​ ​o​b​j​e​c​t​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​]​;​

Which would print out

 ​ ​f​o​u​n​d​ ​a​ ​'​b​a​d​g​e​r​'​ ​:​ ​b​a​d​g​e​r​
 ​ ​f​o​u​n​d​ ​a​ ​'​b​a​d​g​e​r​'​ ​:​ ​b​a​d​g​e​r​i​f​i​c​

It would be nice to be able to generalize this code so “badger” is not hard-coded. Blocks can capture the values of variables defined in the scope that contains the block. Here is a more general version:

N​S​S​t​r​i​n​g​ ​*​s​u​b​S​t​r​i​n​g​ ​=​ ​@​"​b​a​d​g​e​r​"​;​

[​a​r​r​a​y​ ​e​n​u​m​e​r​a​t​e​O​b​j​e​c​t​s​U​s​i​n​g​B​l​o​c​k​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​^​(​i​d​ ​o​b​j​e​c​t​,​ ​N​S​U​I​n​t​e​g​e​r​ ​i​n​d​e​x​,​ ​B​O​O​L​ ​*​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​R​a​n​g​e​ ​m​a​t​c​h​ ​=​ ​[​o​b​j​e​c​t​ ​r​a​n​g​e​O​f​S​t​r​i​n​g​:​ ​s​u​b​S​t​r​i​n​g​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​m​a​t​c​h​.​l​o​c​a​t​i​o​n​ ​!​=​ ​N​S​N​o​t​F​o​u​n​d​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​f​o​u​n​d​ ​a​ ​'​%​@​'​ ​:​ ​%​@​"​,​ ​s​u​b​S​t​r​i​n​g​,​ ​o​b​j​e​c​t​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​]​;​

As you would expect, this also prints out

 ​ ​f​o​u​n​d​ ​a​ ​'​b​a​d​g​e​r​'​ ​:​ ​b​a​d​g​e​r​
 ​ ​f​o​u​n​d​ ​a​ ​'​b​a​d​g​e​r​'​ ​:​ ​b​a​d​g​e​r​i​f​i​c​

The subString variable does not have to be a hard-coded assignment either. It could be a value retrieved from an NSTextField or UITextField.

So, what is happening here? The compiler emits code that captures the value of any variables in the outer scope that are used inside of the block. The block, in essence, takes a snapshot of the world at this point in time. Captured local variables are treated as constant. You cannot assign to subString inside of the block; if you do, you will get an error.

Captured Objective-C objects are retained. subString therefore has been retained. It will automatically be released when the block goes away.

Changing Enclosing Scope

Blocks can also change their enclosing scope. Say you want to count the number of badgers found inside of the list of words. You want the flexibility of providing subString to the block, but you also want a way for multiple invocations of the block to calculate a value and let the enclosing scope know about that value. A return value from the block will not work here because the badger count is calculated across an arbitrary number of block invocations. You cannot use a captured variable because they are const.

__block, with two leading underscores, is a new compiler keyword introduced to indicate that an enclosing scope variable can be modified from inside of the block.

Here is an enumeration that counts the number of badgers:

N​S​S​t​r​i​n​g​ ​*​s​u​b​S​t​r​i​n​g​ ​=​ ​@​"​b​a​d​g​e​r​"​;​

_​_​b​l​o​c​k​ ​i​n​t​ ​c​o​u​n​t​ ​=​ ​0​;​

[​a​r​r​a​y​ ​e​n​u​m​e​r​a​t​e​O​b​j​e​c​t​s​U​s​i​n​g​B​l​o​c​k​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​^​(​i​d​ ​o​b​j​e​c​t​,​ ​N​S​U​I​n​t​e​g​e​r​ ​i​n​d​e​x​,​ ​B​O​O​L​ ​*​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​R​a​n​g​e​ ​m​a​t​c​h​ ​=​ ​[​o​b​j​e​c​t​ ​r​a​n​g​e​O​f​S​t​r​i​n​g​:​ ​s​u​b​S​t​r​i​n​g​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​m​a​t​c​h​.​l​o​c​a​t​i​o​n​ ​!​=​ ​N​S​N​o​t​F​o​u​n​d​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​u​n​t​+​+​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​]​;​

N​S​L​o​g​ ​(​@​"​f​o​u​n​d​ ​%​d​ ​%​@​s​"​,​ ​c​o​u​n​t​,​ ​s​u​b​S​t​r​i​n​g​)​;​

prints

 ​ ​f​o​u​n​d​ ​2​ ​b​a​d​g​e​r​s​

The block will be invoked once for each object in the array executing the code from top to bottom. count is incremented if object contains subString. Because count is __block scoped, a common piece of memory is having its value changed. __block says “Hey, this variable can be changed inside of a block.” Of course, global variables and function-scoped static variables can be changed inside of a block without needing the __block qualifier.

Captured objects are not retained with __block. There are no thread safety guarantees with __block variables, so if your block could be executed on multiple threads simultaneously, you will need to take proper thread safety precautions.

Programmers familiar with other languages may recognize these block features under different names: Lambdas, closures, anonymous functions, as well as “blocks” in languages like Ruby and Smalltalk.

Block Variables

Blocks are not limited to living in-line in method or function calls. You can have variables, whether local, global, or instance, that point to blocks. The syntax is like standard C function pointer syntax but using a caret instead of a star:

v​o​i​d​ ​(​^​b​l​o​c​k​P​t​r​V​a​r​)​ ​(​N​S​S​t​r​i​n​g​ ​*​a​r​g​)​ ​=​ ​^​(​N​S​S​t​r​i​n​g​ ​*​a​r​g​)​ ​{​ ​N​S​L​o​g​ ​(​@​"​%​@​"​,​ ​a​r​g​)​;​ ​}​;​

The name of the variable is blockPtrVar. The block returns nothing (void) and takes a single NSString argument.

Invoke it like a function pointer:

b​l​o​c​k​P​t​r​V​a​r​ ​(​@​"​h​e​l​l​o​"​)​;​

which prints “hello.”

Things become more readable when you use a typedef:

t​y​p​e​d​e​f​ ​v​o​i​d​ ​(​^​B​l​o​c​k​T​y​p​e​)​ ​(​N​S​S​t​r​i​n​g​ ​*​a​r​g​)​;​

B​l​o​c​k​T​y​p​e​ ​b​l​o​c​k​P​t​r​V​a​r​ ​=​ ​^​(​N​S​S​t​r​i​n​g​ ​*​a​r​g​)​ ​{​ ​N​S​L​o​g​ ​(​@​"​%​@​"​,​ ​a​r​g​)​;​ ​}​;​

b​l​o​c​k​P​t​r​V​a​r​ ​(​@​"​t​h​e​r​e​"​)​;​

which prints out “there” as you would expect.

Variable Capture Redux

Let’s revisit variable capture. Each of the blocks here is the same code, but they are “created” at different times – each after the value of val has changed.

t​y​p​e​d​e​f​ ​v​o​i​d​ ​(​^​B​o​r​i​n​g​B​l​o​c​k​)​ ​(​v​o​i​d​)​;​

i​n​t​ ​v​a​l​ ​=​ ​2​3​;​
B​o​r​i​n​g​B​l​o​c​k​ ​b​l​o​c​k​1​ ​=​ ​^​{​ ​N​S​L​o​g​ ​(​@​"​%​d​"​,​ ​v​a​l​)​;​ ​}​;​

v​a​l​ ​=​ ​4​2​;​
B​o​r​i​n​g​B​l​o​c​k​ ​b​l​o​c​k​2​ ​=​ ​^​{​ ​N​S​L​o​g​ ​(​@​"​%​d​"​,​ ​v​a​l​)​;​ ​}​;​

v​a​l​ ​=​ ​1​7​;​
B​o​r​i​n​g​B​l​o​c​k​ ​b​l​o​c​k​3​ ​=​ ​^​{​ ​N​S​L​o​g​ ​(​@​"​%​d​"​,​ ​v​a​l​)​;​ ​}​;​

block1 points to a block that has captured val when it had a value of 23. block2 points to a block that has captured val when it had a value of 42, and likewise block3’s val captured the value of 17. Invoking the blocks prints out the captured values of val:

b​l​o​c​k​1​ ​(​)​;​
b​l​o​c​k​2​ ​(​)​;​
b​l​o​c​k​3​ ​(​)​;​

prints

 ​ ​2​3​
 ​ ​4​2​
 ​ ​1​7​

Now, make a single change to the code, making val a __block-scoped variable:

t​y​p​e​d​e​f​ ​v​o​i​d​ ​(​^​B​o​r​i​n​g​B​l​o​c​k​)​ ​(​v​o​i​d​)​;​

_​_​b​l​o​c​k​ ​i​n​t​ ​v​a​l​ ​=​ ​2​3​;​
B​o​r​i​n​g​B​l​o​c​k​ ​b​l​o​c​k​1​ ​=​ ​^​{​ ​N​S​L​o​g​ ​(​@​"​%​d​"​,​ ​v​a​l​)​;​ ​}​;​

v​a​l​ ​=​ ​4​2​;​
B​o​r​i​n​g​B​l​o​c​k​ ​b​l​o​c​k​2​ ​=​ ​^​{​ ​N​S​L​o​g​ ​(​@​"​%​d​"​,​ ​v​a​l​)​;​ ​}​;​

v​a​l​ ​=​ ​1​7​;​
B​o​r​i​n​g​B​l​o​c​k​ ​b​l​o​c​k​3​ ​=​ ​^​{​ ​N​S​L​o​g​ ​(​@​"​%​d​"​,​ ​v​a​l​)​;​ ​}​;​

Invoking the blocks as above will print:

 ​ ​1​7​
 ​ ​1​7​
 ​ ​1​7​

The same value is printed because all blocks are sharing the same storage for val rather than making copies.

Blocks as Objects

Interestingly enough, blocks are also objects and can be stored in collections. Blocks start out life on the call stack, making them the only stack-based Objective-C objects. The block’s executable code does not actually live on the stack, but all of the data the block uses, such as local variables and captured variables, lives there.

Because these blocks live on the stack, you need to make a copy of the block into the heap if it is to live beyond the current scope. When you copy a block, any captured variables are copied into the heap as well. Figure 3.1 shows the block data on the stack for this block:

i​n​t​ ​s​t​r​i​d​e​ ​=​ ​1​0​;​

_​_​b​l​o​c​k​ ​i​n​t​ ​s​u​m​m​a​t​i​o​n​ ​=​ ​0​;​

B​o​r​i​n​g​B​l​o​c​k​ ​b​l​o​c​k​P​t​r​ ​=​ ​^​{​
 ​ ​ ​ ​s​u​m​m​a​t​i​o​n​ ​+​=​ ​s​t​r​i​d​e​;​
}​;​

Because summation is __block-scoped, the code inside and outside of the block refers to the same location in memory. stride, on the other hand, is a simple const-captured variable. Its value, at the time the block is defined, is duplicated and placed in another location in memory, so any changes made to stride will not corrupt the value that has been captured.

Figure 3.1  Block storage starts on the stack

You can make a copy of a block using the -copy method, or with the Block_copy() function:

v​o​i​d​ ​*​B​l​o​c​k​_​c​o​p​y​ ​(​c​o​n​s​t​ ​v​o​i​d​ ​*​b​l​o​c​k​)​;​

You can -release the copied block or use Block_release():

v​o​i​d​ ​B​l​o​c​k​_​r​e​l​e​a​s​e​ ​(​c​o​n​s​t​ ​v​o​i​d​ ​*​b​l​o​c​k​)​;​

After the copy, memory looks like Figure 3.2. The captured values for summation and the captured stride have been moved to the heap, and a pointer has been put into place for summation so that the value can be found in the heap. This means that the address of a __block-scoped variable can change, so do not rely on it being constant.

Figure 3.2  Copies get moved to the heap

The first copy of a block can be expensive because it requires dynamic allocation. Subsequent copies of the block just turn into retains, and so are fast.

In general, -copy and Block_copy() are interchangeable, except when you are running under garbage collection. If you use -copy, you need to keep a strong reference to the block to prevent it from getting collected. Block_copy under GC behaves more like CFRetain, preventing the block from being collected even if there are no strong references to it.

Sending -retain to a block is a no-op until the block has been copied.

When To Copy

So when do you need to copy a block? Make a copy whenever a block will outlive the scope it is defined in. In particular, these snippets are broken:

i​f​ ​(​r​a​n​d​(​)​ ​%​ ​1​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​b​l​o​c​k​P​t​r​ ​=​ ​^​{​ ​N​S​L​o​g​ ​(​@​"​Y​o​u​ ​a​r​e​ ​a​ ​w​i​n​n​e​r​!​"​)​;​ ​}​;​
}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​b​l​o​c​k​P​t​r​ ​=​ ​^​{​ ​N​S​L​o​g​ ​(​@​"​P​l​e​a​s​e​ ​t​r​y​ ​a​g​a​i​n​!​"​)​;​ ​}​;​
}​

The braces for the branches of the if statement introduce a new scope, so the blocks are invalid afterwards.

b​l​o​c​k​P​t​r​ ​=​ ​^​{​ ​N​S​L​o​g​ ​(​@​"​H​e​l​p​ ​m​e​"​)​;​ ​}​;​
r​e​t​u​r​n​ ​b​l​o​c​k​P​t​r​;​

Like returning the address of a local variable, returning a block that is still on the stack will cause problems later on, especially if you capture any local variables whose storage vanishes when the function ends.

B​o​r​i​n​g​B​l​o​c​k​ ​b​l​o​c​k​s​[​1​0​]​;​
f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​1​0​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​b​l​o​c​k​s​[​i​]​ ​=​ ​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​a​p​t​u​r​e​d​ ​%​d​"​,​ ​i​)​;​
 ​ ​ ​ ​}​;​
}​

Similar to the if branches, the body of the for loop is a different scope, so the blocks are invalid once the loop ends.

Blocks in Collections

You need to copy a block before you add it to a Cocoa collection class. Consider this erroneous code:

N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​*​b​l​o​c​k​A​r​r​a​y​ ​=​ ​[​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​a​r​r​a​y​]​;​
[​b​l​o​c​k​A​r​r​a​y​ ​a​d​d​O​b​j​e​c​t​:​ ​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​J​e​n​ ​m​a​k​e​s​ ​g​r​e​a​t​ ​c​u​p​c​a​k​e​s​!​"​)​;​
 ​ ​ ​ ​}​]​;​

The stack-based block object has its address passed to -addObject:. -addObject: retains the passed-in object. Because the block is still on the stack, -retain is a no-op. The block becomes invalid when the calling function exits, waiting to blow up when you use it in the future. You fix this by copying it before putting it into the collection. Do not forget to offset the implicit retain from -copy.

[​a​r​r​a​y​ ​a​d​d​O​b​j​e​c​t​:​ ​[​[​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​J​e​n​ ​m​a​k​e​s​ ​g​r​e​a​t​ ​c​u​p​c​a​k​e​s​!​"​)​;​
 ​ ​ ​ ​}​ ​c​o​p​y​]​ ​a​u​t​o​r​e​l​e​a​s​e​]​]​;​

Yes. It looks weird, but it’s necessary.

Block Retain Cycles

Blocks have retain counts. If you have retain counts, you can get retain cycles. Here is a typedef for a simple block, and a class that references the block, along with a string instance variable.

t​y​p​e​d​e​f​ ​v​o​i​d​ ​(​^​B​o​r​i​n​g​B​l​o​c​k​)​(​v​o​i​d​)​;​

/​/​ ​T​h​e​ ​l​e​a​k​y​ ​o​b​j​e​c​t​.​
@​i​n​t​e​r​f​a​c​e​ ​L​e​a​k​z​o​r​ ​:​ ​N​S​O​b​j​e​c​t​ ​{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​_​s​t​r​i​n​g​;​
 ​ ​ ​ ​B​o​r​i​n​g​B​l​o​c​k​ ​_​b​l​o​c​k​h​e​a​d​;​
}​

/​/​ ​P​r​i​n​t​ ​|​s​t​r​i​n​g​|​.​
-​ ​(​v​o​i​d​)​ ​f​u​n​k​;​

@​e​n​d​ ​/​/​ ​L​e​a​k​z​o​r​

And here are -init and -dealloc.

-​ ​(​i​d​)​ ​i​n​i​t​ ​{​
 ​ ​ ​ ​i​f​ ​(​(​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​_​s​t​r​i​n​g​ ​=​ ​@​"​s​n​o​r​k​"​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​|​s​t​r​i​n​g​|​ ​i​s​ ​s​a​m​e​ ​a​s​ ​s​e​l​f​-​>​s​t​r​i​n​g​,​ ​s​o​ ​|​s​e​l​f​|​ ​i​s​ ​r​e​t​a​i​n​e​d​.​
 ​ ​ ​ ​ ​ ​ ​ ​_​b​l​o​c​k​h​e​a​d​ ​=​ ​B​l​o​c​k​_​c​o​p​y​(​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​s​t​r​i​n​g​ ​i​s​ ​%​@​"​,​ ​_​s​t​r​i​n​g​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​ ​/​/​ ​i​n​i​t​

-​ ​(​v​o​i​d​)​ ​d​e​a​l​l​o​c​ ​{​
 ​ ​ ​ ​[​_​s​t​r​i​n​g​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​_​b​l​o​c​k​h​e​a​d​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​s​u​p​e​r​ ​d​e​a​l​l​o​c​]​;​
}​ ​/​/​ ​d​e​a​l​l​o​c​

The _string instance variable is used in the block. Instance variable references like this are actually a pointer dereference off of self, so

N​S​L​o​g​ ​(​@​"​s​t​r​i​n​g​ ​i​s​ ​%​@​"​,​ ​_​s​t​r​i​n​g​)​;​

is the same as

N​S​L​o​g​ ​(​@​"​s​t​r​i​n​g​ ​i​s​ ​%​@​"​,​ ​s​e​l​f​-​>​_​s​t​r​i​n​g​)​;​

Because self is a variable in the outside scope that is being captured, it is retained. self will not be released until the block is released in -dealloc, but -dealloc will not be called until self has been released. This is the cause of the retain cycle.

You can break the retain cycle by using a __block-scoped local variable that points to self. __block-scoped objects are not retained automatically, so the retain cycle is not created:

-​ ​(​i​d​)​ ​i​n​i​t​ ​{​
 ​ ​ ​ ​i​f​ ​(​(​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​_​s​t​r​i​n​g​ ​=​ ​@​"​s​n​o​r​k​"​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​b​l​o​c​k​S​e​l​f​ ​i​s​ ​_​_​b​l​o​c​k​ ​s​c​o​p​e​,​ ​s​o​ ​w​o​n​'​t​ ​b​e​ ​a​u​t​o​-​r​e​t​a​i​n​e​d​
 ​ ​ ​ ​ ​ ​ ​ ​_​_​b​l​o​c​k​ ​L​e​a​k​z​o​r​ ​*​b​l​o​c​k​S​e​l​f​ ​=​ ​s​e​l​f​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​b​l​o​c​k​h​e​a​d​ ​=​ ​B​l​o​c​k​_​c​o​p​y​(​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​s​t​r​i​n​g​ ​i​s​ ​%​@​"​,​ ​b​l​o​c​k​S​e​l​f​-​>​_​s​t​r​i​n​g​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​ ​/​/​ ​i​n​i​t​

You do not have to jump through this hoop for every block you make because most blocks have a very short lifespan. Just be aware that if you have a block that has the same lifetime as its owning object and you are freeing the block at -dealloc time, you can get a retain cycle.

New API Using Blocks

Several new C library calls have been introduced using blocks. They have _b appended to their names to indicate they are block calls, similar to the way some library functions have _r appended to indicate re-entrant calls. Some of the more useful ones are:

qsort_b() | quicksort using a block for a comparator
bsearch_b() | binary search using a block for a comparator
psort_b() | parallel sort using a block for a comparator
glob_b() | generate pathnames matching a pattern, using a block for an error callback
scandir_b() | collect pathnames, using blocks for a path selection and comparison

Cocoa has introduced a large number of block-oriented methods. An easy way to find them all is to search through the framework headers looking for the caret. The caret operator is rarely used in Cocoa header files, so it is a good way to find block methods. Here are some interesting new Cocoa methods:

 ​ ​[​a​r​r​a​y​ ​e​n​u​m​e​r​a​t​e​O​b​j​e​c​t​s​U​s​i​n​g​B​l​o​c​k​:​
 ​ ​ ​ ​ ​^​(​i​d​ ​o​b​j​,​ ​N​S​U​I​n​t​e​g​e​r​ ​i​n​d​e​x​,​ ​B​O​O​L​ ​*​s​t​o​p​)​ ​{​ ​.​.​.​ ​}​]​;​

You have seen this before. It iterates through the array, invoking a block for each object.

 ​ ​[​a​r​r​a​y​ ​e​n​u​m​e​r​a​t​e​O​b​j​e​c​t​s​W​i​t​h​O​p​t​i​o​n​s​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​E​n​u​m​e​r​a​t​i​o​n​R​e​v​e​r​s​e​ ​|​ ​N​S​E​n​u​m​e​r​a​t​i​o​n​C​o​n​c​u​r​r​e​n​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​i​n​g​B​l​o​c​k​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​^​(​i​d​ ​o​b​j​,​ ​N​S​U​I​n​t​e​g​e​r​ ​i​n​d​e​x​,​ ​B​O​O​L​ ​*​s​t​o​p​)​ ​{​ ​.​.​.​ ​}​]​;​

You can include some extra options when enumerating an array. Interesting options are NSEnumerationReverse, which iterates through the array backwards, and NSEnumerationConcurrent, which will automatically parallelize the iteration.

 ​ ​[​d​i​c​t​i​o​n​a​r​y​ ​e​n​u​m​e​r​a​t​e​K​e​y​s​A​n​d​O​b​j​e​c​t​s​U​s​i​n​g​B​l​o​c​k​:​
 ​ ​ ​ ​ ​ ​ ​^​(​i​d​ ​k​e​y​,​ ​i​d​ ​o​b​j​e​c​t​,​ ​B​O​O​L​ ​*​s​t​o​p​)​ ​{​ ​.​.​.​ ​}​]​;​

This is the fastest way to iterate the keys and values in a dictionary. Fast enumeration over a dictionary only gives you the keys, so getting the corresponding value requires an -objectForKey: call.

 ​ ​ ​N​S​P​r​e​d​i​c​a​t​e​ ​*​p​ ​=​ ​[​N​S​P​r​e​d​i​c​a​t​e​ ​p​r​e​d​i​c​a​t​e​W​i​t​h​B​l​o​c​k​:​
 ​ ​ ​ ​ ​ ​ ​^​B​O​O​L​ ​(​i​d​ ​o​b​j​,​ ​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​b​i​n​d​i​n​g​s​)​ ​{​ ​.​.​.​ ​}​]​;​

This creates an NSPredicate that is backed by arbitrary code, which allows you to express more sophisticated predicates than can be constructed using the predicate format language.

Grand Central Dispatch is also heavily block-based and will be covered in Chapter 22: Grand Central Dispatch).

For the More Curious: Blocks Internals

Blocks and __block variables are implemented by a combination of structures and functions. These are generated by the compiler and maintained by a runtime environment. Knowing how things work under the hood is useful for understanding the details of memory management and debugging.

Implementation

The compiler interprets the new, block-specific syntax and generates data to interface with the runtime and code that relies on functions provided by the runtime. The blocks runtime enables the use of blocks while the application is actually running.

The specific compiler is irrelevant to our discussion. The most visible difference between gcc and clang is in the names generated for the private structures and functions created by the compiler to support blocks and __block variables.

Those private structures and functions make up the heart of the blocks implementation.

Block literals

Each block literal definition triggers the compiler to generate two structures and at least one function. The two structures describe the block and its runtime information. The function contains the executable code of the block.

The two structures are the block literal (also known as the “block holder”) and the block descriptor.

A block descriptor looks like:

static const struct block_descriptor_NAME {
    unsigned long reserved;
    unsigned long literal_size;

    /* helper functions - present only if needed */
    void (*copy_helper)(void *dst, void *src);
    void (*dispose_helper)(void *src);
};

The reserved field is currently unused. The literal_size field is set to the size of the corresponding block literal. The two helper function pointers are only present if needed. They are needed when the block references an Objective-C or C++ object or a __block variable. When helper functions are necessary, the compiler generates them in addition to the function implementing the body of the block literal.

A block literal looks like:

struct block_literal_NAME {
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(void *literal, ...);
    struct block_descriptor_NAME *descriptor;
    /* referenced captured variables follow */
};

The isa pointer is what makes a block into an Objective-C object. Even when not using Objective-C, the isa pointer is still used by the blocks runtime to indicate what kind of block it is dealing with.

The isa field will point to:

_NSConcreteStackBlock

when the block is on the stack.

_NSConcreteGlobalBlock

when the block is in global storage.

_NSConcreteMallocBlock

when the block is on the heap.

_NSConcreteAutoBlock

when the block is in collectable memory. This class is used when running under the garbage collector and a stack block not referencing a C++ object is copied to the heap.

_NSConcreteFinalizingBlock

when the block is in collectable memory and must have a finalizer run when it is collected. This class is used when running under the garbage collector and a stack block referencing a C++ object is copied to the heap, because the runtime must ensure that the C++ object’s destructor is called when the block is collected.

All of these block classes are subclasses of _NSAbstractBlock. The abstract class provides implementations for the memory-related methods used by blocks. The various concrete subclasses exist solely to indicate information about where the block is stored.

The flags field provides further information about the block:

enum {
    BLOCK_REFCOUNT_MASK    = (0xFFFF),
    BLOCK_NEEDS_FREE       = (1 << 24),
    BLOCK_HAS_COPY_DISPOSE = (1 << 25),
    BLOCK_HAS_CXX_OBJ      = (1 << 26),
    BLOCK_IS_GC            = (1 << 27),
    BLOCK_IS_GLOBAL        = (1 << 28),
    BLOCK_HAS_DESCRIPTOR   = (1 << 29),
};

BLOCK_REFCOUNT_MASK, BLOCK_NEEDS_FREE, and BLOCK_IS_GC are set as appropriate by the runtime when a block is copied.

BLOCK_IS_GLOBAL is set at compile time for blocks in global storage. Copying and releasing such a block has no effect, as the block is always present in the application’s memory. The compiler might opt to hoist a stack-local block into static memory and set the BLOCK_IS_GLOBAL flag if it has no references to any stack-local (which includes __block) variables.

BLOCK_HAS_DESCRIPTOR is always set. It was added to distinguish the version of the blocks implementation that was eventually released with Snow Leopard from an earlier implementation.

Every block invoke function takes a pointer to the calling block literal as its first argument. This provides the function with access to the block’s captured variables. This is functionally identical to the this pointer passed as the first argument to C++ member functions, which the member function uses to access the member variables, and the self pointer supplied as the first argument to Objective-C instance methods, which the method uses to access the instance variables. As in C++, the return value and remaining arguments of the block invoke function are those declared by the programmer. (Objective-C adds one more implicit argument between self and the programmer-declared arguments, _cmd, which is set to the method’s selector.)

Aside from the obvious referenced captured variables, blocks also are considered to have referenced all variables referenced by any blocks nested within them. Consider this brief example:

i​n​t​ ​x​ ​=​ ​0​;​
i​n​t​ ​y​ ​=​ ​1​;​
i​n​t​ ​(​^​b​)​(​v​o​i​d​)​ ​=​ ​^​{​
 ​ ​ ​ ​i​n​t​ ​(​^​c​)​(​v​o​i​d​)​ ​=​ ​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​y​;​
 ​ ​ ​ ​}​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​x​ ​+​ ​c​(​)​;​
}​

Here, the block assigned to b is considered to have referenced both x and y.

To see how block literals, block descriptors, and block invoke functions come together, consider this code:

v​o​i​d​ ​f​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​x​ ​=​ ​0​;​
 ​ ​ ​ ​i​n​t​ ​(​^​b​)​(​v​o​i​d​)​ ​=​ ​^​{​ ​r​e​t​u​r​n​ ​x​ ​+​ ​1​;​ ​}​;​
 ​ ​ ​ ​i​n​t​ ​y​ ​=​ ​b​(​)​;​
}​

The compiler would turn that code into something like this:

t​y​p​e​d​e​f​ ​v​o​i​d​ ​(​*​g​e​n​e​r​i​c​_​i​n​v​o​k​e​_​f​u​n​c​p​t​r​)​(​v​o​i​d​ ​*​,​ ​.​.​.​)​;​
s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​{​
 ​ ​ ​ ​v​o​i​d​ ​*​i​s​a​;​
 ​ ​ ​ ​i​n​t​ ​f​l​a​g​s​;​
 ​ ​ ​ ​i​n​t​ ​r​e​s​e​r​v​e​d​;​
 ​ ​ ​ ​g​e​n​e​r​i​c​_​i​n​v​o​k​e​_​f​u​n​c​p​t​r​ ​i​n​v​o​k​e​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​d​e​s​c​r​i​p​t​o​r​_​t​m​p​ ​*​d​e​s​c​r​i​p​t​o​r​;​
 ​ ​ ​ ​c​o​n​s​t​ ​i​n​t​ ​c​a​p​t​u​r​e​d​_​x​;​
}​;​

s​t​a​t​i​c​ ​c​o​n​s​t​ ​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​d​e​s​c​r​i​p​t​o​r​_​t​m​p​ ​{​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​r​e​s​e​r​v​e​d​;​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​l​i​t​e​r​a​l​_​s​i​z​e​;​
 ​ ​ ​ ​/​*​ ​n​o​ ​c​o​p​y​/​d​i​s​p​o​s​e​ ​h​e​l​p​e​r​s​ ​n​e​e​d​e​d​ ​*​/​
}​ ​_​_​b​l​o​c​k​_​d​e​s​c​r​i​p​t​o​r​_​t​m​p​ ​=​ ​{​
 ​ ​ ​ ​0​U​L​,​ ​s​i​z​e​o​f​(​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​)​
}​;​

/​/​ ​^​i​n​t​ ​(​v​o​i​d​)​ ​{​ ​r​e​t​u​r​n​ ​x​ ​+​ ​1​;​ ​}​
i​n​t​ ​_​_​f​_​b​l​o​c​k​_​i​n​v​o​k​e​_​(​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​*​b​p​)​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​b​p​-​>​c​a​p​t​u​r​e​d​_​x​ ​+​ ​1​;​
}​
t​y​p​e​d​e​f​ ​i​n​t​ ​(​*​i​v​_​f​u​n​c​p​t​r​)​(​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​*​)​;​

v​o​i​d​ ​f​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​x​ ​=​ ​0​;​
 ​ ​ ​ ​/​/​ ​i​n​t​ ​(​^​b​)​(​v​o​i​d​)​ ​=​ ​^​{​ ​r​e​t​u​r​n​ ​x​ ​+​ ​1​ ​}​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​_​_​b​ ​=​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​.​i​s​a​ ​=​ ​&​_​N​S​C​o​n​c​r​e​t​e​S​t​a​c​k​B​l​o​c​k​,​
 ​ ​ ​ ​ ​ ​ ​ ​.​f​l​a​g​s​ ​=​ ​B​L​O​C​K​_​H​A​S​_​D​E​S​C​R​I​P​T​O​R​,​
 ​ ​ ​ ​ ​ ​ ​ ​.​r​e​s​e​r​v​e​d​ ​=​ ​0​,​
 ​ ​ ​ ​ ​ ​ ​ ​.​i​n​v​o​k​e​ ​=​ ​(​g​e​n​e​r​i​c​_​i​n​v​o​k​e​_​f​u​n​c​p​t​r​)​_​_​f​_​b​l​o​c​k​_​i​n​v​o​k​e​_​,​
 ​ ​ ​ ​ ​ ​ ​ ​.​d​e​s​c​r​i​p​t​o​r​ ​=​ ​&​_​_​b​l​o​c​k​_​d​e​s​c​r​i​p​t​o​r​_​t​m​p​,​
 ​ ​ ​ ​ ​ ​ ​ ​.​c​a​p​t​u​r​e​d​_​x​ ​=​ ​x​
 ​ ​ ​ ​}​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​*​b​ ​=​ ​&​_​_​b​;​
 ​ ​ ​ ​i​n​t​ ​y​ ​=​ ​(​*​(​i​v​_​f​u​n​c​p​t​r​)​(​b​-​>​i​n​v​o​k​e​)​)​(​b​)​;​
}​

Notice that the block variable is really a pointer to a structure created on the stack. It can be helpful to keep this in mind when thinking about when a block literal must be copied or not.

__block variables

Like block literals, __block variables can move from the stack to the heap and their variable data can require memory management, such as when it is an Objective-C object. Consequently, __block variables are also compiled into a struct and, if necessary, ancillary functions.

In order that all manipulations of the __block variable deal with the current location of the variable, all access is mediated by a forwarding pointer. When a __block variable is copied from the stack to the heap, the forwarding pointers of both the on-stack and in-heap structures are updated to point to the in-heap structure.

Because all __block variable access is by reference, the names of the structure and functions associated with __block variables embed “byref.”

The byref structure looks like:

struct Block_byref {
    void *isa;
    struct Block_byref *forwarding;
    int flags;
    int size;

    /* helper functions - present only if needed */
    void (*byref_keep)(struct Block_byref *dst, struct Block_byref *src);
    void (*byref_destroy)(struct Block_byref *);

    /* actual variable data follows */
}

The isa field is always NULL to start with. When a __weak-qualified __block variable is copied, the field is set to &NSConcreteWeakBlockVariable.

The forwarding pointer always points to the start of the authoritative byref header. To begin with, this will always be the address of the containing byref structure itself.

The flags field is used to indicate whether copy and dispose helper functions are present. If they are not, it will be initialized to 0; otherwise, it will be initialized to BLOCK_HAS_COPY_DISPOSE. As with block literals, when the structure is copied at runtime, the flags field will be updated with memory management information. If it is copied into scanned memory, BLOCK_IS_GC will be set. Otherwise, BLOCK_NEEDS_FREE will be set and the bottom two bytes used to store a reference count.

The size is set to the size of the particular Block_byref structure.

Helper functions will be synthesized by the compiler if the byref variable is a block reference, an Objective-C object, or a C++ object. If they are present, flags will include BLOCK_HAS_COPY_DISPOSE. They will be invoked when copying and when releasing a block that references a __block variable.

When a block captures a __block variable, it holds onto the byref structure’s forwarding pointer and uses that to interact with the variable. The block will then need copy and dispose helpers to handle copying and disposing of the captured __block variable.

As an example, we will return to the function f. We will change it slightly by moving the referenced variable x from auto storage to __block storage:

v​o​i​d​ ​f​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​_​_​b​l​o​c​k​ ​i​n​t​ ​x​ ​=​ ​0​;​
 ​ ​ ​ ​i​n​t​ ​(​^​b​)​(​v​o​i​d​)​ ​=​ ​^​{​ ​r​e​t​u​r​n​ ​x​ ​+​ ​1​;​ ​}​;​
 ​ ​ ​ ​i​n​t​ ​y​ ​=​ ​b​(​)​;​
}​

In response, the compiler will generate something like the following code (the changes wrought by adding __block have been emphasized):

/​/​ ​_​_​b​l​o​c​k​ ​i​n​t​ ​x​
s​t​r​u​c​t​ ​_​_​b​y​r​e​f​_​x​ ​{​
 ​ ​/​*​ ​h​e​a​d​e​r​ ​*​/​
 ​ ​v​o​i​d​ ​*​i​s​a​;​
 ​ ​s​t​r​u​c​t​ ​_​_​b​y​r​e​f​_​x​ ​*​f​o​r​w​a​r​d​i​n​g​;​
 ​ ​i​n​t​ ​f​l​a​g​s​;​
 ​ ​i​n​t​ ​s​i​z​e​;​

 ​ ​/​*​ ​n​o​ ​h​e​l​p​e​r​s​ ​n​e​e​d​e​d​ ​*​/​

 ​ ​i​n​t​ ​x​;​
}​;​

t​y​p​e​d​e​f​ ​v​o​i​d​ ​(​*​g​e​n​e​r​i​c​_​i​n​v​o​k​e​_​f​u​n​c​p​t​r​)​(​v​o​i​d​ ​*​,​ ​.​.​.​)​;​
s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​{​
 ​ ​ ​ ​v​o​i​d​ ​*​i​s​a​;​
 ​ ​ ​ ​i​n​t​ ​f​l​a​g​s​;​
 ​ ​ ​ ​i​n​t​ ​r​e​s​e​r​v​e​d​;​
 ​ ​ ​ ​g​e​n​e​r​i​c​_​i​n​v​o​k​e​_​f​u​n​c​p​t​r​ ​i​n​v​o​k​e​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​d​e​s​c​r​i​p​t​o​r​_​t​m​p​ ​*​d​e​s​c​r​i​p​t​o​r​;​

 ​ ​ ​ ​s​t​r​u​c​t​ ​_​_​b​y​r​e​f​_​x​ ​*​c​a​p​t​u​r​e​d​_​x​;​

}​;​

v​o​i​d​ ​_​_​c​o​p​y​_​h​e​l​p​e​r​_​b​l​o​c​k​_​(​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​*​d​s​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​*​s​r​c​)​;​
v​o​i​d​ ​_​_​d​e​s​t​r​o​y​_​h​e​l​p​e​r​_​b​l​o​c​k​_​(​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​*​b​p​)​;​

t​y​p​e​d​e​f​ ​v​o​i​d​ ​(​*​g​e​n​e​r​i​c​_​c​o​p​y​_​f​u​n​c​p​t​r​)​(​v​o​i​d​ ​*​,​ ​v​o​i​d​ ​*​)​;​
t​y​p​e​d​e​f​ ​v​o​i​d​ ​(​*​g​e​n​e​r​i​c​_​d​i​s​p​o​s​e​_​f​u​n​c​p​t​r​)​(​v​o​i​d​ ​*​)​;​
s​t​a​t​i​c​ ​c​o​n​s​t​ ​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​d​e​s​c​r​i​p​t​o​r​_​t​m​p​ ​{​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​r​e​s​e​r​v​e​d​;​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​l​i​t​e​r​a​l​_​s​i​z​e​;​

 ​ ​ ​ ​/​*​ ​h​e​l​p​e​r​s​ ​t​o​ ​c​o​p​y​ ​_​_​b​l​o​c​k​ ​r​e​f​e​r​e​n​c​e​ ​c​a​p​t​u​r​e​d​_​x​ ​*​/​
 ​ ​ ​ ​g​e​n​e​r​i​c​_​c​o​p​y​_​f​u​n​c​p​t​r​ ​c​o​p​y​;​
 ​ ​ ​ ​g​e​n​e​r​i​c​_​d​i​s​p​o​s​e​_​f​u​n​c​p​t​r​ ​d​i​s​p​o​s​e​;​

}​ ​_​_​b​l​o​c​k​_​d​e​s​c​r​i​p​t​o​r​_​t​m​p​ ​=​ ​{​
 ​ ​ ​ ​0​U​L​,​ ​s​i​z​e​o​f​(​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​)​,​

 ​ ​ ​ ​(​g​e​n​e​r​i​c​_​c​o​p​y​_​f​u​n​c​p​t​r​)​_​_​c​o​p​y​_​h​e​l​p​e​r​_​b​l​o​c​k​_​,​
 ​ ​ ​ ​(​g​e​n​e​r​i​c​_​d​i​s​p​o​s​e​_​f​u​n​c​p​t​r​)​_​_​d​e​s​t​r​o​y​_​h​e​l​p​e​r​_​b​l​o​c​k​_​

}​;​

/​/​ ​^​i​n​t​ ​(​v​o​i​d​)​ ​{​ ​r​e​t​u​r​n​ ​x​ ​+​ ​1​;​ ​}​
i​n​t​ ​_​_​f​_​b​l​o​c​k​_​i​n​v​o​k​e​_​(​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​*​b​p​)​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​b​p​-​>​c​a​p​t​u​r​e​d​_​x​-​>​f​o​r​w​a​r​d​i​n​g​-​>​x​ ​+​ ​1​;​
}​
t​y​p​e​d​e​f​ ​i​n​t​ ​(​*​i​v​_​f​u​n​c​p​t​r​)​(​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​*​)​;​

v​o​i​d​ ​f​(​v​o​i​d​)​ ​{​

 ​ ​ ​ ​/​/​ ​_​_​b​l​o​c​k​ ​i​n​t​ ​x​ ​=​ ​0​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​_​_​b​y​r​e​f​_​x​ ​x​ ​=​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​.​i​s​a​ ​=​ ​N​U​L​L​,​
 ​ ​ ​ ​ ​ ​ ​ ​.​f​o​r​w​a​r​d​i​n​g​ ​=​ ​&​x​,​
 ​ ​ ​ ​ ​ ​ ​ ​.​f​l​a​g​s​ ​=​ ​0​,​
 ​ ​ ​ ​ ​ ​ ​ ​.​s​i​z​e​ ​=​ ​s​i​z​e​o​f​(​x​)​,​
 ​ ​ ​ ​ ​ ​ ​ ​.​x​ ​=​ ​0​
 ​ ​ ​ ​}​;​

 ​ ​ ​ ​/​/​ ​i​n​t​ ​(​^​b​)​(​v​o​i​d​)​ ​=​ ​^​{​ ​r​e​t​u​r​n​ ​x​ ​+​ ​1​ ​}​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​_​_​b​ ​=​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​.​i​s​a​ ​=​ ​&​_​N​S​C​o​n​c​r​e​t​e​S​t​a​c​k​B​l​o​c​k​,​
 ​ ​ ​ ​ ​ ​ ​ ​.​f​l​a​g​s​ ​=​ ​B​L​O​C​K​_​H​A​S​_​D​E​S​C​R​I​P​T​O​R​,​
 ​ ​ ​ ​ ​ ​ ​ ​.​r​e​s​e​r​v​e​d​ ​=​ ​0​,​
 ​ ​ ​ ​ ​ ​ ​ ​.​i​n​v​o​k​e​ ​=​ ​(​g​e​n​e​r​i​c​_​i​n​v​o​k​e​_​f​u​n​c​p​t​r​)​_​_​f​_​b​l​o​c​k​_​i​n​v​o​k​e​_​,​
 ​ ​ ​ ​ ​ ​ ​ ​.​d​e​s​c​r​i​p​t​o​r​ ​=​ ​&​_​_​b​l​o​c​k​_​d​e​s​c​r​i​p​t​o​r​_​t​m​p​,​

 ​ ​ ​ ​ ​ ​ ​ ​.​c​a​p​t​u​r​e​d​_​x​ ​=​ ​x​.​f​o​r​w​a​r​d​i​n​g​

 ​ ​ ​ ​}​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​*​b​ ​=​ ​&​_​_​b​;​
 ​ ​ ​ ​i​n​t​ ​y​ ​=​ ​(​*​(​i​v​_​f​u​n​c​p​t​r​)​(​b​-​>​i​n​v​o​k​e​)​)​(​b​)​;​

 ​ ​ ​ ​/​/​ ​C​l​e​a​n​ ​u​p​ ​b​e​f​o​r​e​ ​l​e​a​v​i​n​g​ ​s​c​o​p​e​ ​o​f​ ​x​.​
 ​ ​ ​ ​_​B​l​o​c​k​_​o​b​j​e​c​t​_​d​i​s​p​o​s​e​(​x​.​f​o​r​w​a​r​d​i​n​g​,​ ​B​L​O​C​K​_​F​I​E​L​D​_​I​S​_​B​Y​R​E​F​)​;​

}​

v​o​i​d​ ​_​_​c​o​p​y​_​h​e​l​p​e​r​_​b​l​o​c​k​_​(​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​*​d​s​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​*​s​r​c​)​ ​{​
 ​ ​_​B​l​o​c​k​_​o​b​j​e​c​t​_​a​s​s​i​g​n​(​&​d​s​t​-​>​c​a​p​t​u​r​e​d​_​x​,​ ​s​r​c​-​>​c​a​p​t​u​r​e​d​_​x​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​B​L​O​C​K​_​F​I​E​L​D​_​I​S​_​B​Y​R​E​F​)​;​
}​

v​o​i​d​ ​_​_​d​e​s​t​r​o​y​_​h​e​l​p​e​r​_​b​l​o​c​k​_​(​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​ ​*​b​p​)​ ​{​
 ​ ​_​B​l​o​c​k​_​o​b​j​e​c​t​_​d​i​s​p​o​s​e​(​b​p​-​>​c​a​p​t​u​r​e​d​_​x​,​ ​B​L​O​C​K​_​F​I​E​L​D​_​I​S​_​B​Y​R​E​F​)​;​
}​

Of particular note here is the call to _Block_object_dispose() at the end of f(). This is because, when garbage collection is not being used, the runtime must adjust the reference count of the __block variable whenever it goes out of scope. When all references have been eliminated, the runtime releases any allocated storage.

The functions used by the helper functions, _Block_object_assign() and _Block_object_dispose(), are provided by the blocks runtime for use by the compiler. Their behavior is heavily determined by the final argument, const int flags, which provides information on the type of the object being assigned or disposed of. The possible values of this field are:

enum {
    BLOCK_FIELD_IS_OBJECT   =  3,
    BLOCK_FIELD_IS_BLOCK    =  7,
    BLOCK_FIELD_IS_BYREF    =  8,
    BLOCK_FIELD_IS_WEAK     = 16,
    BLOCK_BYREF_CALLER      = 128
};

The BLOCK_BYREF_CALLER flag is used to signal to the functions that they are being called by a byref structure’s byref_keep or byref_destroy function. It is only ever set by such functions.

The other flags are set as appropriate for the type of the object being assigned or disposed. Notice that a block field is also an object, since BLOCK_FIELD_IS_BLOCK & BLOCK_FIELD_IS_OBJECT results in BLOCK_FIELD_IS_OBJECT. Where distinguishing between an object and a block object is important, the runtime functions are careful to test whether the block flag is set before testing whether the object flag is set.

Debugging

Debugging blocks can be tricky as the debugging environment straddles the line between the abstraction and the implementation. gcc provides far better debugging information than clang, but this might change in the future.

gdb comes with only one block-specific command: invoke-block, which you can unambiguously abbreviate to inv. Its arguments are a block reference or the address of a block literal structure followed by the declared arguments to the block function. The arguments are separated by spaces, so arguments with spaces must be enclosed in double quotation marks. Double quotation marks within quoted arguments must be escaped with a backslash. The only time you are likely to encounter this is in passing a string argument to a block; the resulting command would look like:

i​n​v​ ​s​t​r​i​n​g​_​b​l​o​c​k​ ​"​\​"​s​t​r​i​n​g​ ​a​r​g​u​m​e​n​t​\​"​"​

gcc and clang differ significantly in the debugging information they supply for blocks.

gcc’s debugging information

gcc embeds a goodly amount of debugging information about blocks. The print command (p for short) picks up that block references are pointers, and you can use ptype to print the compiler-generated type of a block:

(​g​d​b​)​ ​p​ ​l​o​c​a​l​_​b​l​o​c​k​
$​1​ ​=​ ​(​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​_​2​ ​*​)​ ​0​x​b​f​f​f​f​8​5​4​
(​g​d​b​)​ ​p​t​y​p​e​ ​l​o​c​a​l​_​b​l​o​c​k​
t​y​p​e​ ​=​ ​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​_​2​ ​{​
 ​ ​ ​ ​v​o​i​d​ ​*​_​_​i​s​a​;​
 ​ ​ ​ ​i​n​t​ ​_​_​f​l​a​g​s​;​
 ​ ​ ​ ​i​n​t​ ​_​_​r​e​s​e​r​v​e​d​;​
 ​ ​ ​ ​v​o​i​d​ ​*​_​_​F​u​n​c​P​t​r​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​d​e​s​c​r​i​p​t​o​r​_​w​i​t​h​c​o​p​y​d​i​s​p​o​s​e​ ​*​_​_​d​e​s​c​r​i​p​t​o​r​;​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​e​n​c​_​v​b​v​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​_​_​B​l​o​c​k​_​b​y​r​e​f​_​1​_​i​ ​*​i​;​
}​ ​*​
(​g​d​b​)​ ​p​t​y​p​e​ ​l​o​c​a​l​_​b​l​o​c​k​-​>​_​_​d​e​s​c​r​i​p​t​o​r​
t​y​p​e​ ​=​ ​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​d​e​s​c​r​i​p​t​o​r​_​w​i​t​h​c​o​p​y​d​i​s​p​o​s​e​ ​{​
 ​ ​ ​ ​l​o​n​g​ ​u​n​s​i​g​n​e​d​ ​i​n​t​ ​r​e​s​e​r​v​e​d​;​
 ​ ​ ​ ​l​o​n​g​ ​u​n​s​i​g​n​e​d​ ​i​n​t​ ​S​i​z​e​;​
 ​ ​ ​ ​v​o​i​d​ ​*​C​o​p​y​F​u​n​c​P​t​r​;​
 ​ ​ ​ ​v​o​i​d​ ​*​D​e​s​t​r​o​y​F​u​n​c​P​t​r​;​
}​ ​*​

You can also use the Objective-C command print-object (po for short) to get a different view on the block:

(​g​d​b​)​ ​p​o​ ​l​o​c​a​l​_​b​l​o​c​k​
<​_​_​N​S​S​t​a​c​k​B​l​o​c​k​_​_​:​ ​0​x​b​f​f​f​f​8​5​4​>​

Getting information on local variables within a block will show that gcc adds the __func__ variable, which is set to the name of the function. If you get information on the function arguments, you will see the implicit block literal pointer argument:

(​g​d​b​)​ ​i​ ​a​r​g​s​
.​b​l​o​c​k​_​d​e​s​c​r​i​p​t​o​r​ ​=​ ​(​s​t​r​u​c​t​ ​_​_​b​l​o​c​k​_​l​i​t​e​r​a​l​_​2​ ​*​)​ ​0​x​b​f​f​f​f​8​5​4​

The debugging information generated by gcc pretends that __block variables are identical to their auto counterparts, so that if you have a variable __block int i, you will find that printing the i and its size will behave the same as printing a variable int i.

clang’s debugging information

clang, unfortunately, provides no debugging information for block references. The debugger finds no type information for block references. It also has no way to look up the block implementation function for a block so that invoke-block always fails.

You can still set breakpoints in blocks by setting them at a line in a file or at the invocation function, if you can determine its name, but you will find that clang pretends that block implementation functions have the same arguments as the block literal, so you cannot readily gain access to the implicit block literal pointer argument. Interestingly, clang does not report any __func__ local variable; it generates a warning if you use it from a block literal, but you will find that the variable is in fact present, regardless of what the debugging information says.

clang also emits no debugging information for __block variables. They do not appear in the list of local variables, and any attempt to reference them results in a message like:

N​o​ ​s​y​m​b​o​l​ ​"​i​"​ ​i​n​ ​c​u​r​r​e​n​t​ ​c​o​n​t​e​x​t​.​

While you could make headway by using what you know of the blocks implementation to cadge the desired information out of a program compiled using clang, until these issues are fixed, you would do well to use gcc when compiling an application using blocks where you plan to rely on the debugging information in future.

Dumping runtime information

Apple’s blocks runtime includes a couple functions for dumping information about a block reference and a __block variable.

These functions are:

const char *_Block_dump(const void *block);
const char *_Block_byref_dump(struct Block_byref *src);

You can call these from gdb to dump information about a block or __block variable. If you have the following declarations:

_​_​b​l​o​c​k​ ​i​n​t​ ​i​ ​=​ ​2​3​;​
v​o​i​d​ ​(​^​l​o​c​a​l​_​b​l​o​c​k​)​(​v​o​i​d​)​ ​=​ ​^​{​ ​/​*​.​.​.​*​/​ ​}​;​

then you can dump information about them as follows:

(​g​d​b​)​ ​c​a​l​l​ ​(​v​o​i​d​)​p​r​i​n​t​f​(​(​c​o​n​s​t​ ​c​h​a​r​ ​*​)​_​B​l​o​c​k​_​d​u​m​p​(​l​o​c​a​l​_​b​l​o​c​k​)​)​
^​0​x​b​f​f​f​f​8​5​4​ ​(​n​e​w​ ​l​a​y​o​u​t​)​ ​=​
i​s​a​:​ ​s​t​a​c​k​ ​B​l​o​c​k​
f​l​a​g​s​:​ ​H​A​S​D​E​S​C​R​I​P​T​O​R​ ​H​A​S​H​E​L​P​
r​e​f​c​o​u​n​t​:​ ​0​
i​n​v​o​k​e​:​ ​0​x​1​e​5​0​
d​e​s​c​r​i​p​t​o​r​:​ ​0​x​2​0​b​c​
d​e​s​c​r​i​p​t​o​r​-​>​r​e​s​e​r​v​e​d​:​ ​0​
d​e​s​c​r​i​p​t​o​r​-​>​s​i​z​e​:​ ​2​8​
d​e​s​c​r​i​p​t​o​r​-​>​c​o​p​y​ ​h​e​l​p​e​r​:​ ​0​x​1​e​2​8​
d​e​s​c​r​i​p​t​o​r​-​>​d​i​s​p​o​s​e​ ​h​e​l​p​e​r​:​ ​0​x​1​e​0​a​

(​g​d​b​)​ ​s​e​t​ ​$​a​d​d​r​ ​=​ ​(​c​h​a​r​ ​*​)​&​i​ ​-​ ​2​*​s​i​z​e​o​f​(​i​n​t​)​ ​-​ ​2​*​s​i​z​e​o​f​(​v​o​i​d​ ​*​)​
(​g​d​b​)​ ​c​a​l​l​ ​(​v​o​i​d​)​p​r​i​n​t​f​(​(​c​o​n​s​t​ ​c​h​a​r​ ​*​)​_​B​l​o​c​k​_​b​y​r​e​f​_​d​u​m​p​(​$​a​d​d​r​)​)​
b​y​r​e​f​ ​d​a​t​a​ ​b​l​o​c​k​ ​0​x​b​f​f​f​f​8​7​0​ ​c​o​n​t​e​n​t​s​:​
 ​ ​f​o​r​w​a​r​d​i​n​g​:​ ​0​x​b​f​f​f​f​8​7​0​
 ​ ​f​l​a​g​s​:​ ​0​x​0​
 ​ ​s​i​z​e​:​ ​2​0​

Note that, though the debugging information supplied by gcc pretends that the __block variable i is simply an int variable, the address of the variable is in fact its address within the byref structure. Since we know the layout of the structure, we can calculate the address of the start of the structure and pass that to _Block_byref_dump().

You can wrap these calls in user-defined commands. Adding the following definitions to your .gdbinit file will make them available whenever you run gdb:

d​e​f​i​n​e​ ​d​u​m​p​-​b​l​o​c​k​-​l​i​t​e​r​a​l​
 ​ ​ ​ ​p​r​i​n​t​f​ ​"​%​s​"​,​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​)​_​B​l​o​c​k​_​d​u​m​p​(​$​a​r​g​0​)​
e​n​d​

d​o​c​u​m​e​n​t​ ​d​u​m​p​-​b​l​o​c​k​-​l​i​t​e​r​a​l​
 ​ ​ ​ ​D​u​m​p​s​ ​r​u​n​t​i​m​e​ ​i​n​f​o​r​m​a​t​i​o​n​ ​a​b​o​u​t​ ​t​h​e​ ​s​u​p​p​l​i​e​d​ ​b​l​o​c​k​ ​r​e​f​e​r​e​n​c​e​.​
 ​ ​ ​ ​A​r​g​u​m​e​n​t​ ​i​s​ ​t​h​e​ ​n​a​m​e​ ​o​r​ ​a​d​d​r​e​s​s​ ​o​f​ ​a​ ​b​l​o​c​k​ ​r​e​f​e​r​e​n​c​e​.​
e​n​d​

d​e​f​i​n​e​ ​d​u​m​p​-​b​l​o​c​k​-​b​y​r​e​f​
 ​ ​ ​ ​s​e​t​ ​$​_​d​b​b​_​a​d​d​r​ ​=​ ​(​c​h​a​r​ ​*​)​&​$​a​r​g​0​ ​-​ ​2​*​s​i​z​e​o​f​(​i​n​t​)​ ​-​ ​2​*​s​i​z​e​o​f​(​v​o​i​d​ ​*​)​
 ​ ​ ​ ​p​r​i​n​t​f​ ​"​%​s​"​,​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​)​_​B​l​o​c​k​_​b​y​r​e​f​_​d​u​m​p​(​$​_​d​b​b​_​a​d​d​r​)​
e​n​d​

d​o​c​u​m​e​n​t​ ​d​u​m​p​-​b​l​o​c​k​-​b​y​r​e​f​
 ​ ​ ​ ​D​u​m​p​s​ ​r​u​n​t​i​m​e​ ​i​n​f​o​r​m​a​t​i​o​n​ ​a​b​o​u​t​ ​t​h​e​ ​s​u​p​p​l​i​e​d​ ​_​_​b​l​o​c​k​ ​v​a​r​i​a​b​l​e​.​
 ​ ​ ​ ​A​r​g​u​m​e​n​t​ ​i​s​ ​a​ ​p​o​i​n​t​e​r​ ​t​o​ ​t​h​e​ ​v​a​r​i​a​b​l​e​ ​e​m​b​e​d​d​e​d​ ​i​n​ ​a​ ​b​l​o​c​k​ ​b​y​r​e​f​ ​s​t​r​u​c​t​u​r​e​.​
e​n​d​

With these commands defined, dumping that information is as simple as:

(​g​d​b​)​ ​d​u​m​p​-​b​l​o​c​k​-​l​i​t​e​r​a​l​ ​l​o​c​a​l​_​b​l​o​c​k​
^​0​x​b​f​f​f​f​8​5​4​ ​(​n​e​w​ ​l​a​y​o​u​t​)​ ​=​
i​s​a​:​ ​s​t​a​c​k​ ​B​l​o​c​k​
f​l​a​g​s​:​ ​H​A​S​D​E​S​C​R​I​P​T​O​R​ ​H​A​S​H​E​L​P​
r​e​f​c​o​u​n​t​:​ ​0​
i​n​v​o​k​e​:​ ​0​x​1​e​5​0​
d​e​s​c​r​i​p​t​o​r​:​ ​0​x​2​0​b​c​
d​e​s​c​r​i​p​t​o​r​-​>​r​e​s​e​r​v​e​d​:​ ​0​
d​e​s​c​r​i​p​t​o​r​-​>​s​i​z​e​:​ ​2​8​
d​e​s​c​r​i​p​t​o​r​-​>​c​o​p​y​ ​h​e​l​p​e​r​:​ ​0​x​1​e​2​8​
d​e​s​c​r​i​p​t​o​r​-​>​d​i​s​p​o​s​e​ ​h​e​l​p​e​r​:​ ​0​x​1​e​0​a​
(​g​d​b​)​ ​d​u​m​p​-​b​l​o​c​k​-​b​y​r​e​f​ ​i​
b​y​r​e​f​ ​d​a​t​a​ ​b​l​o​c​k​ ​0​x​b​f​f​f​f​8​7​0​ ​c​o​n​t​e​n​t​s​:​
 ​ ​f​o​r​w​a​r​d​i​n​g​:​ ​0​x​b​f​f​f​f​8​7​0​
 ​ ​f​l​a​g​s​:​ ​0​x​0​
 ​ ​s​i​z​e​:​ ​2​0​

Evolving the implementation

This chapter has described the blocks runtime as released with Mac OS X 10.6 (Snow Leopard). The blocks runtime is unlikely to make any changes that would break code compiled to that interface, but it will not stop evolving.

There are several extension points built into the current runtime. The various flags fields can be carefully extended; reserved fields can be repurposed; and new fields can be tacked on at the ends of the various structures.

One minor extension that might see release is the addition of a signature field at the end of the block descriptor structure. This field would contain a pointer to the Objective-C type encoding of the block invoke function.

(For those curious, blocks themselves are encoded by the @encode directive as @?; this parallels the function pointer encoding of ^?, which literally reads as “pointer to unknown type.”)

To indicate that this field is present, the BLOCK_HAS_DESCRIPTOR flag would no longer be set, and a new flag, BLOCK_HAS_SIGNATURE = (1 << 30), would be set in all blocks compiled for the new runtime.

With the ability to test for BLOCK_HAS_SIGNATURE to check for a block compiled against a newer version of the runtime, the way is opened for other changes, including repurposing BLOCK_HAS_DESCRIPTOR to signal that a block returns a structure large enough to require special handling on some architectures. The flag could be renamed BLOCK_USE_STRET. This is similar to the way that objc_msgSend_stret() is used by the Objective-C runtime instead of objc_msgSend() in the same situation.

A more significant change would be to add the signature of the variables captured by a block. This would allow the runtime to eliminate helper functions in favor of using the type information now encoded along with the block to itself do the right thing for all captured variables when a block is copied or disposed.

Compiler-generated names

Both gcc and clang automatically generate names for the structures and functions that make up blocks and __block variables. Sometimes during debugging, it would be useful to be able to guess the names generated in implementing a given block or __block variable.

Unfortunately, outside of toy examples, this is generally not possible without reference to a disassembly of the code. Both compilers disambiguate the names of the structures and helper functions they generate by appending numbers to the end of the same string. Thus, you can judge the type of support structure or helper you are looking at, but you cannot readily trace it back to the block or __block variable that caused its generation.

Fortunately, the outlook is not so bleak when it comes to block invoke functions. Block invoke functions not defined at global scope embed the name of the outermost enclosing function. The first block in a function f() will be named __f_block_invoke_1 if generated by gcc and __f_block_invoke_ if generated by clang. The numeric suffix is incremented prior to generating the name of the block invoke function of each subsequent block encountered within the function. (clang starts by appending the number 1 and increments it like gcc thereafter.) Objective-C method names will be embedded just as C function names, leading to block invoke function names like __-[Foo init]_block_invoke_1. C++ member functions are neither qualified nor mangled for embedding, so a block defined within the member function Foo::Bar() will cause the compiler to generate a block invoke function named __Bar_block_invoke_ (append a 1 if the compiler is gcc).

The block invoke functions of blocks defined at global scope are harder to track down, since they have no enclosing function to anchor them. gcc names such functions starting with __block_global_1. clang uses the same name scheme as for blocks defined within a function, only it substitutes global as the function name. Consequently, the first block defined at global scope that clang encounters is named __global_block_invoke_, and the second, __global_block_invoke_1.

One surprising result of the naming conventions for blocks at global scope is that when several source files are compiled into a single executable, each one can have its own __global_block_invoke_ function. The resulting executable will have several functions with the identical name distinguished by their being at different addresses.

Whenever possible, specify breakpoints in block functions using the file and line number rather than the name. The name generation scheme could change in future and does not guarantee uniqueness within a linked executable, only within a compilation unit.

Exercises

Rewrite this function as a block:

i​n​t​ ​k​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​1​;​
}​

Write a function create_k that accepts a single integer argument i and returns a block. The returned block accepts no arguments and always returns the value of i passed to create_k(). Use this function in a test program. (Be careful with the block memory management!)

Define two blocks called add_one and add_two that accept no arguments and return nothing. These should both increment the same integer by one or two. (You will need the __block storage specifier.)

Use typedefs to simplify the following block reference declaration:

i​n​t​ ​(​^​(​*​(​^​g​e​t​_​b​l​o​c​k​_​f​a​c​t​o​r​y​_​f​u​n​c​p​t​r​)​(​v​o​i​d​)​)​(​i​n​t​)​)​(​v​o​i​d​)​;​

4
Command-Line Programs

Much of the power that Unix brings to the user is in the command-line tools, where the user can set up pipelines of independent programs that manipulate data. It’s time to take a peek under the hood of a typical command-line tool and see how it works, as many of the command-line concepts (processing arguments, checking the environment) apply to any Unix program. Here you are going to write a program that filters its input by changing any letters it finds to upper or lower case.

Figure 4.1 shows a typical command-line program along with its three communication streams. The standard-in stream (also called “stdin”) supplies data to the program. The data you are to process comes from standard-in. You are done once the stream dries up. While you process the data, you write new data to the standard-out stream (also called “stdout”). If you need to report any errors or output any information other than the processed data, you can write that information to the standard-error stream (also called “stderr”).

When you type a pipeline command in a shell, like cat words.txt | wc -l, the shell creates new processes, one for each command. For this command, there are two new processes, one for cat and one for wc. The shell then hooks the standard-out of cat to the standard-in of wc, and also hooks the standard-out of wc back to the shell so that the shell can read its output. By using the stream redirection features of your shell, you can pour a file’s contents into the standard-in of a program or save the standard-out output to a file.

Figure 4.1  The standard file streams

The Basic Program

The stdio.h header file (among other stuff) defines global symbols that represent the three file streams: stdin, stdout, and stderr, which represent standard-in, standard-out, and standard-error respectively.

Here’s a source file called upcase.m. First import the header files. You will always need some kind of header files:

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​B​O​O​L​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​F​A​I​L​U​R​E​/​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​a​n​d​a​r​d​ ​I​/​O​ ​s​t​u​f​f​

Importing Foundation.h pulls in the headers for the Cocoa Foundation Kit. For now, you’re just going to be using the BOOL type.

stdlib.h brings in much of the C standard library. Here we will be using the EXIT_FAILURE and EXIT_SUCCESS constants.

stdio.h provides the declarations for the standard input/output types and functions.

Next is a function to change the case of all the characters in a buffer:

v​o​i​d​ ​c​h​a​n​g​e​c​a​s​e​B​u​f​f​e​r​ ​(​c​h​a​r​ ​b​u​f​f​e​r​[​]​,​ ​s​i​z​e​_​t​ ​l​e​n​g​t​h​,​ ​B​O​O​L​ ​u​p​c​a​s​e​)​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​*​s​c​a​n​ ​=​ ​b​u​f​f​e​r​;​
 ​ ​ ​ ​c​h​a​r​ ​*​c​o​n​s​t​ ​s​t​o​p​ ​=​ ​b​u​f​f​e​r​ ​+​ ​l​e​n​g​t​h​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​s​c​a​n​ ​<​ ​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​*​s​c​a​n​ ​=​ ​u​p​c​a​s​e​?​ ​t​o​u​p​p​e​r​(​*​s​c​a​n​)​ ​:​ ​t​o​l​o​w​e​r​(​*​s​c​a​n​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​+​+​;​
 ​ ​ ​ ​}​
}​ ​ ​/​/​ ​c​h​a​n​g​e​c​a​s​e​B​u​f​f​e​r​

changecaseBuffer() scans over every byte in the buffer calling the toupper() or tolower() function on each one. The functions change a character’s case appropriately if it is a letter, and they leave it alone if not. This code assumes an ASCII-style character encoding. A string in a multi-byte encoding like UTF-8 will get trashed.

You will need a chunk of memory to hold the incoming data while it is being processed. The program will process things in 2-KiB chunks.

#​d​e​f​i​n​e​ ​B​U​F​F​E​R​_​S​I​Z​E​ ​(​2​0​4​8​)​

Last is the main() function, which is where program control flow starts when the program is run. main() reads and processes the input:

i​n​t​ ​m​a​i​n​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​B​U​F​F​E​R​_​S​I​Z​E​]​;​
 ​ ​ ​ ​c​o​n​s​t​ ​B​O​O​L​ ​u​p​c​a​s​e​ ​=​ ​Y​E​S​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​!​f​e​o​f​(​s​t​d​i​n​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​s​i​z​e​_​t​ ​l​e​n​g​t​h​ ​=​ ​f​r​e​a​d​(​b​u​f​f​e​r​,​ ​1​,​ ​B​U​F​F​E​R​_​S​I​Z​E​,​ ​s​t​d​i​n​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​h​a​n​g​e​c​a​s​e​B​u​f​f​e​r​(​b​u​f​f​e​r​,​ ​l​e​n​g​t​h​,​ ​u​p​c​a​s​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​w​r​i​t​e​(​b​u​f​f​e​r​,​ ​1​,​ ​l​e​n​g​t​h​,​ ​s​t​d​o​u​t​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​ ​/​/​ ​m​a​i​n​

You can build this program in Xcode using the Command Line Tool template as a starting point. (In Xcode 3, look for the Foundation Tool template.) Or you can compile it on the command line:

$​ ​g​c​c​ ​-​o​ ​u​p​c​a​s​e​ ​u​p​c​a​s​e​.​m​

This command runs the C compiler on upcase.m, which generates an executable program with the name upcase. If you do not include -o upcase, the compiler will, for historical reasons, use the name a.out.

Invoke your new program with ./upcase. The program will just sit there doing nothing. You need to type some stuff, then press return followed by Control-D, the end-of-file key sequence:

$​ ​.​/​u​p​c​a​s​e​ ​
H​o​o​V​e​R​ ​N​i​ ​B​O​r​K​
^​D​
H​O​O​V​E​R​ ​N​I​ ​B​O​R​K​

This program also works in simple pipelines:

$​ ​e​c​h​o​ ​"​H​o​o​V​e​R​"​ ​|​ ​.​/​u​p​c​a​s​e​
H​O​O​V​E​R​

It works for more complex pipelines as well:

$​ ​c​a​t​ ​/​u​s​r​/​s​h​a​r​e​/​d​i​c​t​/​w​o​r​d​s​ ​|​ ​.​/​u​p​c​a​s​e​ ​|​ ​g​r​e​p​ ​B​A​D​G​E​R​
B​A​D​G​E​R​
B​A​D​G​E​R​B​R​U​S​H​
B​A​D​G​E​R​E​R​
B​A​D​G​E​R​I​N​G​L​Y​
.​.​.​
(​a​n​d​ ​a​ ​c​o​u​p​l​e​ ​m​o​r​e​ ​B​A​D​G​E​R​-​l​i​k​e​ ​w​o​r​d​s​)​

Inside the Central Loop

Take a look at the central loop in main:

 ​ ​ ​ ​w​h​i​l​e​ ​(​!​f​e​o​f​(​s​t​d​i​n​)​)​ ​{​

Loop over the body of the while statement until the standard-in stream indicates that it has reached EOF (end of file). End of file happens when you explicitly type Control-D on a new line if you are typing text into the program. When in a pipeline, the standard-in stream will be closed when the program writing to it exits or otherwise closes the outgoing pipe on its end.

 ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​s​i​z​e​_​t​ ​l​e​n​g​t​h​ ​=​ ​f​r​e​a​d​ ​(​b​u​f​f​e​r​,​ ​1​,​ ​B​U​F​F​E​R​_​S​I​Z​E​,​ ​s​t​d​i​n​)​;​

Read in no more than BUFFER_SIZE bytes.

The fread() function is a record-oriented function that you'll see more of in Chapter 11: Files, Part 1: I/O and Permissions. This function reads x items composed of n bytes. Here you are interested in the number of bytes read, so we tell fread() to read up to BUFFER_SIZE records of one byte each.

 ​ ​ ​ ​ ​ ​ ​ ​c​h​a​n​g​e​c​a​s​e​B​u​f​f​e​r​ ​(​b​u​f​f​e​r​,​ ​l​e​n​g​t​h​,​ ​u​p​c​a​s​e​)​;​

Call changecaseBuffer to process the buffer. Note that the length is passed as an argument. fread will not append a null terminating character ('\0', hex 0x00) to the string, so the receiving function will not know where to stop processing unless you tell it where to stop.

Finally, use the same idea of “writing length records of 1 byte each” to write to the standard-out stream:

 ​ ​ ​ ​ ​ ​ ​ ​f​w​r​i​t​e​ ​(​b​u​f​f​e​r​,​ ​1​,​ ​l​e​n​g​t​h​,​ ​s​t​d​o​u​t​)​;​

Changing Behavior By Name

Now it’s time to let the user control the program behavior. One easy way is to look at the name the user used to invoke the program. There is a Linux utility called BusyBox that implements many of the standard Unix command-line programs in one small executable, changing its behavior based on the name. On your system you probably have a /usr/bin/ranlib which is a symbolic link to the libtool program. In this case, libtool works like ranlib when invoked using the ranlib name.

Example 4.1 changes the program’s behavior depending on if it is run as “upcase” or “downcase.” To do this, look at the argv array that is passed to main. argv is a contraction of the phrase “argument vector.” You could call that parameter anything you wanted to, but typically it will just be argv.

The argv array has all of the command-line arguments passed to the program. The shell breaks the command the user typed into arguments (usually at whitespace characters, but that can be overridden by quotes). The program invocation is in the first element of the array at argv[0]: this is the command as entered by the user. If the program was run with ./upcase, argv[0] would be “./upcase.” If the program was started with /Users/bork/projects/book/chapter2/upcase, argv[0] would have this value too, so you cannot really depend on finding a specific string value there. Luckily, there is a little convenience function called fnmatch that does shell-style filename matching. By seeing if the string in argv[0] matches “*upcase” or “*downcase,” you can decide how the program will operate.

Example 4.1. upcase.m

/​/​ ​u​p​c​a​s​e​.​m​ ​-​-​ ​c​o​n​v​e​r​t​ ​t​e​x​t​ ​t​o​ ​u​p​p​e​r​ ​c​a​s​e​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​u​p​c​a​s​e​ ​u​p​c​a​s​e​.​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​B​O​O​L​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​F​A​I​L​U​R​E​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​a​n​d​a​r​d​ ​I​/​O​ ​s​t​u​f​f​
#​i​m​p​o​r​t​ ​<​f​n​m​a​t​c​h​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​f​n​m​a​t​c​h​(​)​

#​d​e​f​i​n​e​ ​B​U​F​F​E​R​_​S​I​Z​E​ ​(​2​0​4​8​)​

/​/​ ​c​h​a​n​g​e​c​a​s​e​B​u​f​f​e​r​ ​i​s​ ​u​n​c​h​a​n​g​e​d​

i​n​t​ ​m​a​i​n​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​B​U​F​F​E​R​_​S​I​Z​E​]​;​

 ​ ​ ​ ​B​O​O​L​ ​u​p​c​a​s​e​ ​=​ ​Y​E​S​;​

 ​ ​ ​ ​i​f​ ​(​f​n​m​a​t​c​h​(​"​*​u​p​c​a​s​e​"​,​ ​a​r​g​v​[​0​]​,​ ​0​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​"​u​p​c​a​s​e​!​\​n​"​,​ ​s​t​d​e​r​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​u​p​c​a​s​e​ ​=​ ​Y​E​S​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​f​n​m​a​t​c​h​(​"​*​d​o​w​n​c​a​s​e​"​,​ ​a​r​g​v​[​0​]​,​ ​0​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​"​d​o​w​n​c​a​s​e​!​\​n​"​,​ ​s​t​d​e​r​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​u​p​c​a​s​e​ ​=​ ​N​O​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​w​h​i​l​e​ ​(​!​f​e​o​f​(​s​t​d​i​n​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​s​i​z​e​_​t​ ​l​e​n​g​t​h​ ​=​ ​f​r​e​a​d​(​b​u​f​f​e​r​,​ ​1​,​ ​B​U​F​F​E​R​_​S​I​Z​E​,​ ​s​t​d​i​n​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​h​a​n​g​e​c​a​s​e​B​u​f​f​e​r​(​b​u​f​f​e​r​,​ ​l​e​n​g​t​h​,​ ​u​p​c​a​s​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​w​r​i​t​e​(​b​u​f​f​e​r​,​ ​1​,​ ​l​e​n​g​t​h​,​ ​s​t​d​o​u​t​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​ ​/​/​ ​m​a​i​n​

Compile your program as before with:

$​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​u​p​c​a​s​e​ ​u​p​c​a​s​e​.​m​

Here is a sample run:

$​ ​e​c​h​o​ ​"​b​L​a​R​g​"​ ​|​ ​.​/​u​p​c​a​s​e​
u​p​c​a​s​e​!​
B​L​A​R​G​

Now make a symbolic link that points to upcase:

$​ ​l​n​ ​-​s​ ​u​p​c​a​s​e​ ​d​o​w​n​c​a​s​e​

and run the program like this:

$​ ​e​c​h​o​ ​"​b​L​a​R​g​"​ ​|​ ​.​/​d​o​w​n​c​a​s​e​
d​o​w​n​c​a​s​e​!​
b​l​a​r​g​

Looking at the Environment

Another way to influence program behavior is through environment variables. Environment variables, such as those in Figure 4.2, are key-value pairs that are under user control via the shell. The getenv() function is used to read the variables the user has set (directly or indirectly) in the environment from which your program has been run.

Figure 4.2  Environment variables

Supply getenv() with the name of the environment variable you want. If the variable does not exist in the environment, you get NULL back. If it does exist, you will get back a string with the value. The system owns the memory for the string returned by getenv(), so you do not need to free it.

The next version of the program uses the CASE_CONV environment variable to control the case conversion program A value of LOWER will have it massage strings to lower case, and UPPER, to upper case. The default behavior is to upper case.

Remove

#​i​m​p​o​r​t​ ​<​f​n​m​a​t​c​h​.​h​>​

because you won’t be using fnmatch(). The declarations for getenv() and family live in stdlib.h, which has already been included.

Also remove the two if blocks that use fnmatch() and replace them with:

c​o​n​s​t​ ​c​h​a​r​ ​*​e​n​v​S​e​t​t​i​n​g​ ​=​ ​g​e​t​e​n​v​ ​(​"​C​A​S​E​_​C​O​N​V​"​)​;​
i​f​ ​(​e​n​v​S​e​t​t​i​n​g​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​s​t​r​c​m​p​(​e​n​v​S​e​t​t​i​n​g​,​ ​"​U​P​P​E​R​"​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​p​p​e​r​!​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​u​p​c​a​s​e​ ​=​ ​Y​E​S​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​s​t​r​c​m​p​(​e​n​v​S​e​t​t​i​n​g​,​ ​"​L​O​W​E​R​"​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​l​o​w​e​r​!​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​u​p​c​a​s​e​ ​=​ ​N​O​;​
 ​ ​ ​ ​}​
}​

Recompile it with:

$​ ​g​c​c​ ​-​o​ ​u​p​c​a​s​e​ ​u​p​c​a​s​e​.​m​

and give it a whirl:

$​ ​e​x​p​o​r​t​ ​C​A​S​E​_​C​O​N​V​=​L​O​W​E​R​
$​ ​.​/​u​p​c​a​s​e​
l​o​w​e​r​!​
G​r​E​E​b​l​E​
g​r​e​e​b​l​e​

Check out the other case:

$​ ​e​x​p​o​r​t​ ​C​A​S​E​_​C​O​N​V​=​U​P​P​E​R​
$​ ​.​/​u​p​c​a​s​e​
u​p​p​e​r​!​
G​r​E​E​b​l​E​
G​R​E​E​B​L​E​

Parsing the Command Line

Aside from reading some configuration file, the last way to influence program behavior is through command-line arguments. This is also the most common way to control program behavior for command-line programs.

Recall earlier that the “choose behavior based on program name” version of upcase looked at the first element of argv to get the program name. The argv array in fact has all of the program’s arguments, living at argv indexes greater than zero. Recall that argc is the number of arguments on the command line, including the program name.

Example 4.2 is a quick little program that prints out command-line arguments:

Example 4.2. dumpargs.m

/​/​ ​d​u​m​p​a​r​g​s​.​m​ ​-​-​ ​s​h​o​w​ ​p​r​o​g​r​a​m​ ​a​r​g​u​m​e​n​t​s​

g​c​c​ ​-​g​ ​-​s​t​d​=​c​9​9​ ​-​W​a​l​l​ ​-​o​ ​d​u​m​p​a​r​g​s​ ​d​u​m​p​a​r​g​s​.​m​

#​i​n​c​l​u​d​e​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​
#​i​n​c​l​u​d​e​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​

i​n​t​ ​m​a​i​n​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​a​r​g​c​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​(​"​%​d​:​ ​%​s​\​n​"​,​ ​i​,​ ​a​r​g​v​[​i​]​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​ ​/​/​ ​m​a​i​n​

Now feed this program various arguments:

$​ ​.​/​d​u​m​p​a​r​g​s​
0​:​ ​.​/​d​u​m​p​a​r​g​s​

As expected, the name of the program lives in the 0th element of the argv array.

$​ ​.​/​d​u​m​p​a​r​g​s​ ​-​o​o​p​ ​-​a​c​k​ ​-​b​l​a​r​g​
0​:​ ​.​/​d​u​m​p​a​r​g​s​
1​:​ ​-​o​o​p​
2​:​ ​-​a​c​k​
3​:​ ​-​b​l​a​r​g​

You can also see the effect of shell file name globbing and quotation marks:

$​ ​.​/​d​u​m​p​a​r​g​s​ ​"​d​u​m​p​*​"​ ​d​u​m​p​*​
0​:​ ​.​/​d​u​m​p​a​r​g​s​
1​:​ ​d​u​m​p​*​
2​:​ ​d​u​m​p​a​r​g​s​
3​:​ ​d​u​m​p​a​r​g​s​.​m​

The first argument, because it is in quotes, is given to you explicitly as dump*. The second dump* was intercepted by the shell and expanded to list all of the files whose names start with dump. In this case, it matches the name of the program and the name of the .m file.

Now back to upcase.m. We’ll use the -u flag for uppercase and -l (ell) for lowercase.

Remove the code you added for handling the environment variables and add this:

 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​>​=​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​s​t​r​c​m​p​(​a​r​g​v​[​1​]​,​ ​"​-​u​"​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​p​p​e​r​!​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​p​c​a​s​e​ ​=​ ​Y​E​S​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​s​t​r​c​m​p​(​a​r​g​v​[​1​]​,​ ​"​-​l​"​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​l​o​w​e​r​!​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​p​c​a​s​e​ ​=​ ​N​O​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

Recompile and try it out:

$​ ​.​/​u​p​c​a​s​e​ ​-​u​
u​p​p​e​r​!​
G​r​E​E​b​L​e​
G​R​E​E​B​L​E​

$​ ​.​/​u​p​c​a​s​e​ ​-​l​
l​o​w​e​r​!​
G​r​E​E​b​L​e​
g​r​e​e​b​l​e​

One thing some experienced programmers will notice is the distinct lack of error checking in these programs. Much of the work of programming Unix is catching and handling errors, whether they are user errors (typing invalid or conflicting command arguments) or system errors (a disk fills up).

You may have noticed the return EXIT_SUCCESS; at the end of each of the main() functions throughout this book. EXIT_SUCCESS is a macro that expands to zero, which when returned, tells the shell that the command succeeded. Any non-zero return value tells the shell that the command failed. The shell uses this return value to decide whether to continue with the work it is doing, whether running a shell script or just a command pipeline. For upcase, you should do a little checking of arguments, such as whether the user entered too many or entered one that is invalid.

Return EXIT_FAILURE, a macro that expands to the value 1, to tell the shell that something went wrong. You can use any number less than 256 for the return value, but using the symbolic constant makes it obvious that you’re indicating an error condition. An attempt has been made to categorize some error return values. Check out /usr/include/sysexits.h, which has an email-subsystem flavor.

Here is upcase with some error checking (replace the argv code you entered above with this):

 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​>​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​(​s​t​d​e​r​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​%​s​:​ ​T​o​o​ ​m​a​n​y​ ​a​r​g​u​m​e​n​t​s​ ​-​ ​s​u​p​p​l​y​ ​a​t​ ​m​o​s​t​ ​o​n​e​.​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​a​r​g​c​ ​=​=​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​B​O​O​L​ ​f​o​u​n​d​ ​=​ ​N​O​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​s​t​r​c​m​p​(​a​r​g​v​[​1​]​,​ ​"​-​u​"​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​p​c​a​s​e​ ​=​ ​Y​E​S​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​u​n​d​ ​=​ ​Y​E​S​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​s​t​r​c​m​p​(​a​r​g​v​[​1​]​,​ ​"​-​l​"​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​p​c​a​s​e​ ​=​ ​N​O​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​u​n​d​ ​=​ ​Y​E​S​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​!​f​o​u​n​d​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​%​s​:​ ​u​n​e​x​p​e​c​t​e​d​ ​c​o​m​m​a​n​d​-​l​i​n​e​ ​a​r​g​u​m​e​n​t​:​ ​'​%​s​'​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​r​g​v​[​0​]​,​ ​a​r​g​v​[​1​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​%​s​:​ ​e​x​p​e​c​t​e​d​:​ ​-​u​ ​o​r​ ​-​l​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

When you discover that something is wrong, print out a complaint message to the standard error stream and bail out with an error code.

In real life programs, there is usually some cleanup work that would need to be done, like closing files or freeing memory before returning from a function that detected errors. The operating system will clean up any open files or allocated memory for your program when it terminates, but it’s a good idea to be tidy so that resource monitoring tools won’t accuse you of leaking resources. You might want to do this cleanup in debug versions, so you can have maximal tool support, but also have the fastest possible shutdown in release versions.

Throughout this book, you will see one way of handling errors: keeping all of the cleanup code at the end of the function and using a goto to jump to a label at the end and bypass any additional work in the function. Unfortunately, too many in the programming industry have been trained to have a knee-jerk reaction to goto, when in many cases it can lead to much more readable cleanup than the alternatives.

For example, in pseudo-code:

i​n​t​ ​s​o​m​e​F​u​n​c​t​i​o​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​.​.​.​
 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​f​a​i​l​u​r​e​;​
 ​ ​ ​ ​b​l​a​h​ ​=​ ​a​l​l​o​c​a​t​e​_​s​o​m​e​_​m​e​m​o​r​y​(​)​;​
 ​ ​ ​ ​i​f​ ​(​d​o​_​s​o​m​e​t​h​i​n​g​(​b​l​a​h​)​ ​=​=​ ​f​a​i​l​u​r​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​a​c​k​ ​=​ ​o​p​e​n​_​a​_​f​i​l​e​(​)​;​
 ​ ​ ​ ​i​f​ ​(​p​r​o​c​e​s​s​_​f​i​l​e​(​b​l​a​h​,​ ​a​c​k​)​ ​=​=​ ​f​a​i​l​u​r​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​h​o​o​v​e​r​ ​=​ ​d​o​_​s​o​m​e​t​h​i​n​g​_​e​l​s​e​(​)​;​
 ​ ​ ​ ​i​f​ ​(​h​a​v​e​_​f​u​n​(​b​l​a​h​,​ ​h​o​o​v​e​r​)​ ​=​=​ ​f​a​i​l​u​r​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​/​/​ ​w​e​ ​s​u​r​v​i​v​e​d​!​ ​y​a​y​
 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​s​u​c​c​e​s​s​;​

 ​ ​b​a​i​l​o​u​t​:​
 ​ ​ ​ ​i​f​ ​(​b​l​a​h​)​ ​f​r​e​e​_​t​h​e​_​m​e​m​o​r​y​ ​(​b​l​a​h​)​;​
 ​ ​ ​ ​i​f​ ​(​a​c​k​)​ ​c​l​o​s​e​_​t​h​e​_​f​i​l​e​ ​(​a​c​k​)​;​
 ​ ​ ​ ​i​f​ ​(​h​o​o​v​e​r​)​ ​c​l​e​a​n​_​t​h​i​s​_​u​p​ ​(​h​o​o​v​e​r​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​r​e​s​u​l​t​;​
}​

If you are using C++, you can use exception handling (a fancy form of goto) and stack-based cleanup objects to simplify cleanup. Objective-C users can use a @finally block to accomplish something similar. In plain C, you must either nest ifs, introduce an auxiliary success variable, or employ goto.

Here is the final main function, including error checking of the command-line arguments and a single exit point:

i​n​t​ ​m​a​i​n​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​e​x​i​t​R​e​t​u​r​n​ ​=​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​B​U​F​F​E​R​_​S​I​Z​E​]​;​
 ​ ​ ​ ​B​O​O​L​ ​u​p​c​a​s​e​ ​=​ ​Y​E​S​;​

 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​>​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​%​s​:​ ​T​o​o​ ​m​a​n​y​ ​a​r​g​u​m​e​n​t​s​ ​-​ ​s​u​p​p​l​y​ ​a​t​ ​m​o​s​t​ ​o​n​e​.​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​a​r​g​c​ ​=​=​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​B​O​O​L​ ​f​o​u​n​d​ ​=​ ​N​O​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​s​t​r​c​m​p​(​a​r​g​v​[​1​]​,​ ​"​-​u​"​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​p​c​a​s​e​ ​=​ ​Y​E​S​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​u​n​d​ ​=​ ​Y​E​S​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​s​t​r​c​m​p​(​a​r​g​v​[​1​]​,​ ​"​-​l​"​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​p​c​a​s​e​ ​=​ ​N​O​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​u​n​d​ ​=​ ​Y​E​S​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​!​f​o​u​n​d​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​%​s​:​ ​u​n​e​x​p​e​c​t​e​d​ ​c​o​m​m​a​n​d​-​l​i​n​e​ ​a​r​g​u​m​e​n​t​:​ ​'​%​s​'​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​r​g​v​[​0​]​,​ ​a​r​g​v​[​1​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​%​s​:​ ​e​x​p​e​c​t​e​d​:​ ​-​u​ ​o​r​ ​-​l​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​w​h​i​l​e​ ​(​!​f​e​o​f​(​s​t​d​i​n​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​s​i​z​e​_​t​ ​l​e​n​g​t​h​ ​=​ ​f​r​e​a​d​ ​(​b​u​f​f​e​r​,​ ​1​,​ ​B​U​F​F​E​R​_​S​I​Z​E​,​ ​s​t​d​i​n​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​h​a​n​g​e​c​a​s​e​B​u​f​f​e​r​ ​(​b​u​f​f​e​r​,​ ​l​e​n​g​t​h​,​ ​u​p​c​a​s​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​w​r​i​t​e​ ​(​b​u​f​f​e​r​,​ ​1​,​ ​l​e​n​g​t​h​,​ ​s​t​d​o​u​t​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​e​x​i​t​R​e​t​u​r​n​ ​=​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

 ​b​a​i​l​o​u​t​:​
 ​ ​ ​ ​r​e​t​u​r​n​ ​e​x​i​t​R​e​t​u​r​n​;​
}​ ​ ​/​/​ ​m​a​i​n​

getopt_long()

Parsing Unix command lines correctly is actually a very tricky proposition. You can have individual arguments:

$​ ​s​o​m​e​p​r​o​g​r​a​m​ ​-​a​ ​-​b​ ​-​c​

By convention, you can glom all of the single-character arguments together:

$​ ​s​o​m​e​p​r​o​g​r​a​m​ ​-​a​b​c​

Some arguments can take additional values:

$​ ​s​o​m​e​p​r​o​g​r​a​m​ ​-​d​T​w​i​t​t​e​r​ ​-​f​ ​f​i​l​e​n​a​m​e​

and there are also more verbose (called “long”) arguments, introduced by two dashes:

$​ ​s​o​m​e​p​r​o​g​r​a​m​ ​-​-​k​t​h​x​ ​-​-​c​h​e​e​z​b​u​r​g​e​r​

There is a convention where -- will terminate processing of command-line arguments and use the remaining contents of the command line as-is, even if they look suspiciously like more command-line arguments.

$​ ​s​o​m​e​p​r​o​g​r​a​m​ ​-​-​h​o​o​v​e​r​ ​-​-​ ​-​n​o​t​ ​-​-​r​e​a​l​l​y​ ​-​-​a​r​g​u​m​e​n​t​s​ ​-​n​o​w​

That’s an awful lot of complexity. Luckily, you do not have to implement all of this logic yourself. The standard library supplies getopt_long() to handle this drudge-work. There is an older function called getopt() that performs similar duties, but it does not handle long argument processing. getopt_long() takes a number of parameters:

     int getopt_long (int argc, char * const *argv,
                      const char *optstring,
                      const struct option *longopts,
                      int *longindex);

argc and argv are the command-line parameters passed to main(). optstring is a character string that describes the short, single-character options, while longopts describes the longer, more verbose options. longindex, if supplied, returns the index into longopts when it processes an argument. Usually you will just pass NULL here.

Call getopt_long() repeatedly in a loop until it indicates that it has processed all of the arguments by returning -1. It will return ':' if there was a missing argument to an option that requires an argument, or '?' if the user supplied an unknown or ambiguous option. Otherwise, it returns a value, controlled by optstring and longopts, that you can then use for argument processing.

optstring is a string that has all of the single character arguments you wish to support. Use a colon after arguments that take a required additional argument and two colons after arguments that take an optional additional argument. So, if we had a program that took arguments of -c, -o and -f, where -f took an argument (say a filename), the optstring argument would look like "cof:". getopt_long() will let you use the command-line syntax -fBlah as well as -f Blah for indicating the argument. getopt_long() returns the appropriate character when it sees any of these arguments as it runs through the command line.

longopts is a bit more complicated. You provide getopt_long() an array of option structures, terminated by a sentinel that has zero/NULL values in all fields. This array is almost always statically created in your source file. The option structure looks like this:

s​t​r​u​c​t​ ​o​p​t​i​o​n​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​*​n​a​m​e​;​
 ​ ​ ​ ​i​n​t​ ​ ​ ​h​a​s​_​a​r​g​;​
 ​ ​ ​ ​i​n​t​ ​ ​*​f​l​a​g​;​
 ​ ​ ​ ​i​n​t​ ​ ​ ​v​a​l​;​
}​;​

name is the verbose name of the option. has_arg is one of the constants no_argument, optional_argument, and required_argument that describe whether this particular command-line option requires an additional argument.

flag and val work together. If flag is NULL, val will be the return value from getopt_long() if it sees the long option indicated by name. You typically use a single-character return value that matches one of the options described in your optstring. This lets you process single-character and verbose commands that are the same.

Non-NULL flag values should be a pointer to an integer value. When getopt_long() processes the verbose option described by name, it puts val’s value into the integer where flag points. When getopt_long() processes one of these options, it returns zero to indicate that it just set a variable for you. This mechanism only works for verbose arguments.

To phrase it another way, if you have a verbose option that also has a single-character equivalent, you want flag to be NULL and val to be that character. Otherwise, if you have a verbose option that does not take an argument, you can have getopt_long() automatically set an arbitrary integer variable to a given value.

Example 4.3 shows getopt_long() in-action.

Example 4.3. argparse.m

/​/​ ​a​r​g​p​a​r​s​e​.​m​ ​-​-​ ​u​s​i​n​g​ ​g​e​t​o​p​t​_​l​o​n​g​ ​t​o​ ​p​a​r​s​e​ ​a​r​g​u​m​e​n​t​s​

/​/​ ​g​c​c​ ​-​W​a​l​l​ ​-​s​t​d​=​c​9​9​ ​-​g​ ​-​o​ ​a​r​g​p​a​r​s​e​ ​a​r​g​p​a​r​s​e​.​m​

#​i​m​p​o​r​t​ ​<​g​e​t​o​p​t​.​h​>​ ​ ​/​/​ ​f​o​r​ ​g​e​t​o​p​t​_​l​o​n​g​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​

s​t​a​t​i​c​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​o​p​t​s​t​r​i​n​g​ ​=​ ​"​g​f​:​c​:​:​o​"​;​

s​t​a​t​i​c​ ​i​n​t​ ​t​h​i​n​g​1​,​ ​t​h​i​n​g​2​,​ ​t​h​i​n​g​3​;​

s​t​a​t​i​c​ ​s​t​r​u​c​t​ ​o​p​t​i​o​n​ ​l​o​n​g​o​p​t​s​[​]​ ​=​ ​{​
 ​ ​ ​ ​{​ ​"​f​i​l​e​n​a​m​e​"​,​ ​ ​r​e​q​u​i​r​e​d​_​a​r​g​u​m​e​n​t​,​ ​ ​N​U​L​L​,​ ​ ​ ​ ​'​f​'​ ​}​,​
 ​ ​ ​ ​{​ ​"​c​a​t​t​o​y​"​,​ ​ ​ ​ ​o​p​t​i​o​n​a​l​_​a​r​g​u​m​e​n​t​,​ ​ ​N​U​L​L​,​ ​ ​ ​ ​'​c​'​ ​}​,​
 ​ ​ ​ ​{​ ​"​o​o​p​"​,​ ​ ​ ​ ​ ​ ​ ​n​o​_​a​r​g​u​m​e​n​t​,​ ​ ​ ​ ​ ​ ​ ​ ​N​U​L​L​,​ ​ ​ ​ ​'​o​'​ ​}​,​
 ​ ​ ​ ​{​ ​"​t​h​i​n​g​1​"​,​ ​ ​ ​ ​n​o​_​a​r​g​u​m​e​n​t​,​ ​ ​ ​ ​ ​ ​ ​ ​&​t​h​i​n​g​1​,​ ​ ​1​ ​}​,​
 ​ ​ ​ ​{​ ​"​t​h​i​n​g​2​"​,​ ​ ​ ​ ​n​o​_​a​r​g​u​m​e​n​t​,​ ​ ​ ​ ​ ​ ​ ​ ​&​t​h​i​n​g​2​,​ ​ ​2​ ​}​,​
 ​ ​ ​ ​{​ ​"​t​h​i​n​g​3​"​,​ ​ ​ ​ ​n​o​_​a​r​g​u​m​e​n​t​,​ ​ ​ ​ ​ ​ ​ ​ ​&​t​h​i​n​g​3​,​ ​ ​3​ ​}​,​
 ​ ​ ​ ​{​ ​ ​N​U​L​L​,​ ​ ​ ​ ​ ​ ​ ​0​,​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​U​L​L​,​ ​ ​ ​ ​ ​0​ ​}​
}​;​

i​n​t​ ​m​a​i​n​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​c​h​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​(​c​h​ ​=​ ​g​e​t​o​p​t​_​l​o​n​g​(​a​r​g​c​,​ ​a​r​g​v​,​ ​o​p​t​s​t​r​i​n​g​,​ ​l​o​n​g​o​p​t​s​,​ ​N​U​L​L​)​)​ ​!​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​w​i​t​c​h​ ​(​c​h​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​'​g​'​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​u​t​s​ ​(​"​ ​ ​g​r​e​e​b​l​e​!​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​'​f​'​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​f​i​l​e​ ​n​a​m​e​ ​i​s​ ​%​s​\​n​"​,​ ​o​p​t​a​r​g​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​'​c​'​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​c​a​t​ ​t​o​y​ ​i​s​ ​%​s​\​n​"​,​ ​(​o​p​t​a​r​g​ ​=​=​ ​N​U​L​L​)​ ​?​ ​"​s​t​r​i​n​g​"​ ​:​ ​o​p​t​a​r​g​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​'​o​'​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​u​t​s​ ​(​"​ ​ ​o​o​p​!​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​0​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​u​t​s​ ​(​"​ ​ ​g​e​t​o​p​t​_​l​o​n​g​ ​s​e​t​ ​a​ ​v​a​r​i​a​b​l​e​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​'​:​'​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​u​t​s​ ​(​"​ ​ ​m​i​s​s​i​n​g​ ​r​e​q​u​i​r​e​d​ ​a​r​g​u​m​e​n​t​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​'​?​'​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​u​t​s​ ​(​"​ ​ ​o​o​p​s​,​ ​u​n​k​n​o​w​n​ ​o​p​t​i​o​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​S​e​e​ ​i​f​ ​t​h​e​ ​t​h​i​n​g​ ​v​a​r​i​a​b​l​e​s​ ​g​o​t​ ​m​a​n​i​p​u​l​a​t​e​d​.​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​t​h​i​n​g​1​:​ ​%​d​ ​ ​t​h​i​n​g​2​:​ ​%​d​ ​ ​t​h​i​n​g​3​:​ ​%​d​\​n​"​,​ ​t​h​i​n​g​1​,​ ​t​h​i​n​g​3​,​ ​t​h​i​n​g​3​)​;​

 ​ ​ ​ ​/​/​ ​M​o​p​ ​u​p​ ​a​n​y​ ​r​e​m​a​i​n​i​n​g​ ​a​r​g​u​m​e​n​t​s​.​
 ​ ​ ​ ​a​r​g​c​ ​-​=​ ​o​p​t​i​n​d​;​
 ​ ​ ​ ​a​r​g​v​ ​+​=​ ​o​p​t​i​n​d​;​

 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​>​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​u​t​s​ ​(​"​a​d​d​i​t​i​o​n​a​l​ ​t​r​a​i​l​i​n​g​ ​a​r​g​u​m​e​n​t​s​:​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​a​r​g​c​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​%​s​\​n​"​,​ ​a​r​g​v​[​i​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​ ​/​/​ ​m​a​i​n​

The first thing of interest is the optstring:

 ​ ​s​t​a​t​i​c​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​o​p​t​s​t​r​i​n​g​ ​=​ ​"​g​f​:​c​:​:​o​"​;​

This says that there are four single-character command-line options, -g, -f, -c, and -o. -f requires an argument, while -c takes an optional argument.

After that is the array describing the longopts:

s​t​a​t​i​c​ ​s​t​r​u​c​t​ ​o​p​t​i​o​n​ ​l​o​n​g​o​p​t​s​[​]​ ​=​ ​{​
 ​ ​ ​ ​{​ ​"​f​i​l​e​n​a​m​e​"​,​ ​ ​r​e​q​u​i​r​e​d​_​a​r​g​u​m​e​n​t​,​ ​ ​N​U​L​L​,​ ​ ​ ​ ​'​f​'​ ​}​,​
 ​ ​ ​ ​{​ ​"​c​a​t​t​o​y​"​,​ ​ ​ ​ ​o​p​t​i​o​n​a​l​_​a​r​g​u​m​e​n​t​,​ ​ ​N​U​L​L​,​ ​ ​ ​ ​'​c​'​ ​}​,​
 ​ ​ ​ ​{​ ​"​o​o​p​"​,​ ​ ​ ​ ​ ​ ​ ​n​o​_​a​r​g​u​m​e​n​t​,​ ​ ​ ​ ​ ​ ​ ​ ​N​U​L​L​,​ ​ ​ ​ ​'​o​'​ ​}​,​
 ​ ​ ​ ​{​ ​"​t​h​i​n​g​1​"​,​ ​ ​ ​ ​n​o​_​a​r​g​u​m​e​n​t​,​ ​ ​ ​ ​ ​ ​ ​ ​&​t​h​i​n​g​1​,​ ​ ​1​ ​}​,​
 ​ ​ ​ ​{​ ​"​t​h​i​n​g​2​"​,​ ​ ​ ​ ​n​o​_​a​r​g​u​m​e​n​t​,​ ​ ​ ​ ​ ​ ​ ​ ​&​t​h​i​n​g​2​,​ ​ ​2​ ​}​,​
 ​ ​ ​ ​{​ ​"​t​h​i​n​g​3​"​,​ ​ ​ ​ ​n​o​_​a​r​g​u​m​e​n​t​,​ ​ ​ ​ ​ ​ ​ ​ ​&​t​h​i​n​g​3​,​ ​ ​3​ ​}​,​
 ​ ​ ​ ​{​ ​ ​N​U​L​L​,​ ​ ​ ​ ​ ​ ​ ​0​,​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​U​L​L​,​ ​ ​ ​ ​ ​0​ ​}​
}​;​

There is no direct connection between the two argument descriptions. In a real program, you’d want to generate the option string from your longopts table so you wouldn’t have two places to modify if you added more arguments at a later time.

There are three long arguments that correspond to three of our short arguments. There is one short argument, -g, that has no verbose equivalent. This is perfectly OK. You can see that --cattoy takes an optional argument.

The three “--thing” arguments have no single-character equivalent. If --thing1 is seen on the command line, the thing1 variable will be set to the value 1. If --thing3 is seen, then thing3 will have the value of 3.

The body of main() then calls getopt_long() repeatedly. As described earlier, the return value is the character for the argument, for single-character arguments.

One of the cases is interesting:

 ​ ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​'​c​'​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​c​a​t​ ​t​o​y​ ​i​s​ ​%​s​\​n​"​,​ ​(​o​p​t​a​r​g​ ​=​=​ ​N​U​L​L​)​ ​?​ ​"​s​t​r​i​n​g​"​ ​:​ ​o​p​t​a​r​g​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​

This case will be hit if the argument -c (or --cattoy) was provided. optarg is a global variable that is set to the argument provided by the user. If no argument is provided, which is possible for this case since --cattoy’s argument has been flagged as optional both in the optstring by the two colons following 'c' and in the longopts, then optarg will be NULL. That’s your cue to supply whatever default value is appropriate.

The loop terminates when getopt_long() returns -1. After that, the values of the three thing variables are printed. These values are set if the user provides arguments like --thing1, and the only hint this happens is when zero is returned during the loop.

There is another global variable manipulated by getopt_long() used here – optind. This is how far it processed into argv. Arguments after this have not been processed and are yours to do with as you please. A program might use them as file names to process. getopt_long() automatically handles using -- as an indicator to stop processing arguments.

Here are some runs of this program:

Lack of arguments is not terribly interesting:

$​ ​.​/​a​r​g​p​a​r​s​e​
t​h​i​n​g​1​:​ ​0​ ​ ​t​h​i​n​g​2​:​ ​0​ ​ ​t​h​i​n​g​3​:​ ​0​

Single-character arguments are handled as well:

$​ ​.​/​a​r​g​p​a​r​s​e​ ​-​o​g​
 ​ ​o​o​p​!​
 ​ ​g​r​e​e​b​l​e​!​
t​h​i​n​g​1​:​ ​0​ ​ ​t​h​i​n​g​2​:​ ​0​ ​ ​t​h​i​n​g​3​:​ ​0​

The optional argument is handled as well:

$​ ​.​/​a​r​g​p​a​r​s​e​ ​-​-​c​a​t​t​o​y​
 ​ ​c​a​t​ ​t​o​y​ ​i​s​ ​s​t​r​i​n​g​
t​h​i​n​g​1​:​ ​0​ ​ ​t​h​i​n​g​2​:​ ​0​ ​ ​t​h​i​n​g​3​:​ ​0​

As are things that accept arguments:

$​ ​.​/​a​r​g​p​a​r​s​e​ ​-​-​f​i​l​e​n​a​m​e​=​s​o​m​e​f​i​l​e​ ​-​f​ ​f​i​l​e​
 ​ ​f​i​l​e​ ​n​a​m​e​ ​i​s​ ​s​o​m​e​f​i​l​e​
 ​ ​f​i​l​e​ ​n​a​m​e​ ​i​s​ ​f​i​l​e​
t​h​i​n​g​1​:​ ​0​ ​ ​t​h​i​n​g​2​:​ ​0​ ​ ​t​h​i​n​g​3​:​ ​0​

As well as trailing arguments:

$​ ​.​/​a​r​g​p​a​r​s​e​ ​-​-​f​i​l​e​n​a​m​e​=​s​o​m​e​f​i​l​e​ ​-​-​ ​-​f​ ​f​i​l​e​
 ​ ​f​i​l​e​ ​n​a​m​e​ ​i​s​ ​s​o​m​e​f​i​l​e​
t​h​i​n​g​1​:​ ​0​ ​ ​t​h​i​n​g​2​:​ ​0​ ​ ​t​h​i​n​g​3​:​ ​0​
a​d​d​i​t​i​o​n​a​l​ ​t​r​a​i​l​i​n​g​ ​a​r​g​u​m​e​n​t​s​:​
 ​ ​-​f​
 ​ ​f​i​l​e​

getopt_long() also does some rudimentary error checking:

$​ ​.​/​a​r​g​p​a​r​s​e​ ​-​f​
a​r​g​p​a​r​s​e​:​ ​o​p​t​i​o​n​ ​r​e​q​u​i​r​e​s​ ​a​n​ ​a​r​g​u​m​e​n​t​ ​-​-​ ​f​
o​o​p​s​,​ ​u​n​k​n​o​w​n​ ​o​p​t​i​o​n​
t​h​i​n​g​1​:​ ​0​ ​ ​t​h​i​n​g​2​:​ ​0​ ​ ​t​h​i​n​g​3​:​ ​0​

The first line is emitted by getopt_long() itself, while the “oops” line is generated by the program. Note that getopt_long() has returned '?' even though we’ve encountered a missing argument, not an unknown option. You can disable getopt_long()’s error messages by setting opterr to 0. To get it to return ':' for a missing argument, you must begin optstring with a colon; this also disables its error messages. You cannot configure it both to return ':' and supply error messages.

User Defaults

Cocoa’s NSUserDefaults class can also be used as a crude form of command-line processing. NSUserDefaults automatically takes parameters of the form -parameter value and adds them to the defaults domain NSArgumentDomain. Every parameter must take a single argument. You cannot have optional arguments or parameters with no arguments when using this technique.

This is a volatile domain, so its values will not be saved to the user preferences. You probably do not want to use NSUserDefaults as the main configuration interface for command-line programs because it is inflexible, but it can be useful when you want to supply some optional command-line options. This is handy for supplying debugging or testing flags to a GUI program.

Example 4.4 shows a program that looks for file names and cat toys on the command line and prints out the values it sees.

Example 4.4. defargs.m

/​/​ ​d​e​f​a​r​g​s​.​m​ ​-​-​ ​G​e​t​ ​c​o​m​m​a​n​d​-​l​i​n​e​ ​a​r​g​u​m​e​n​t​s​ ​f​r​o​m​ ​u​s​e​r​ ​d​e​f​a​u​l​t​s​.​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​<​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​o​ ​d​e​f​a​r​g​s​ ​d​e​f​a​r​g​s​.​m​

i​n​t​ ​m​a​i​n​(​i​n​t​ ​a​r​g​c​,​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​c​o​n​s​t​ ​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​N​S​U​s​e​r​D​e​f​a​u​l​t​s​ ​*​c​o​n​s​t​ ​d​e​f​s​ ​=​ ​[​N​S​U​s​e​r​D​e​f​a​u​l​t​s​ ​s​t​a​n​d​a​r​d​U​s​e​r​D​e​f​a​u​l​t​s​]​;​

 ​ ​ ​ ​N​S​L​o​g​(​@​"​c​a​t​ ​t​o​y​:​ ​%​@​"​,​ ​[​d​e​f​s​ ​s​t​r​i​n​g​F​o​r​K​e​y​:​@​"​c​a​t​t​o​y​"​]​)​;​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​f​i​l​e​ ​n​a​m​e​:​ ​%​@​"​,​ ​[​d​e​f​s​ ​s​t​r​i​n​g​F​o​r​K​e​y​:​@​"​f​i​l​e​n​a​m​e​"​]​)​;​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​ ​/​/​ ​m​a​i​n​

Just running the program shows that there are no values:

$​ ​.​/​d​e​f​a​r​g​s​
2​0​1​0​-​0​9​-​2​0​ ​1​9​:​3​4​:​5​9​.​5​4​9​ ​d​e​f​a​u​l​t​s​[​5​4​0​5​:​8​0​7​]​ ​c​a​t​ ​t​o​y​:​ ​(​n​u​l​l​)​
2​0​1​0​-​0​9​-​2​0​ ​1​9​:​3​4​:​5​9​.​5​5​1​ ​d​e​f​a​u​l​t​s​[​5​4​0​5​:​8​0​7​]​ ​f​i​l​e​ ​n​a​m​e​:​ ​(​n​u​l​l​)​

But you can provide arguments:

$​ ​.​/​d​e​f​a​r​g​s​ ​-​c​a​t​t​o​y​ ​f​l​a​m​i​n​g​o​
2​0​1​0​-​0​9​-​2​0​ ​1​9​:​3​5​:​1​5​.​7​6​5​ ​d​e​f​a​u​l​t​s​[​5​4​0​6​:​8​0​7​]​ ​c​a​t​ ​t​o​y​:​ ​f​l​a​m​i​n​g​o​
2​0​1​0​-​0​9​-​2​0​ ​1​9​:​3​5​:​1​5​.​7​6​7​ ​d​e​f​a​u​l​t​s​[​5​4​0​6​:​8​0​7​]​ ​f​i​l​e​ ​n​a​m​e​:​ ​(​n​u​l​l​)​

$​ ​.​/​d​e​f​a​r​g​s​ ​-​f​i​l​e​n​a​m​e​ ​b​o​r​k​ ​-​c​a​t​t​o​y​ ​f​l​a​m​i​n​g​o​
2​0​1​0​-​0​9​-​2​0​ ​1​9​:​5​3​:​4​9​.​2​2​6​ ​d​e​f​a​r​g​s​[​5​5​0​1​:​8​0​7​]​ ​c​a​t​ ​t​o​y​:​ ​f​l​a​m​i​n​g​o​
2​0​1​0​-​0​9​-​2​0​ ​1​9​:​5​3​:​4​9​.​2​2​7​ ​d​e​f​a​r​g​s​[​5​5​0​1​:​8​0​7​]​ ​f​i​l​e​ ​n​a​m​e​:​ ​b​o​r​k​

One nice thing about using NSUserDefaults for settings like these is that you can use the defaults command-line tool to set them persistently. Because this program has no bundle identifier, you must use the program name. Set a default like this:

$​ ​d​e​f​a​u​l​t​s​ ​w​r​i​t​e​ ​d​e​f​a​r​g​s​ ​f​i​l​e​n​a​m​e​ ​-​s​t​r​i​n​g​ ​"​b​o​r​k​"​

And then run the program:

$​ ​.​/​d​e​f​a​r​g​s​
2​0​1​0​-​0​9​-​2​0​ ​1​9​:​5​5​:​2​9​.​0​3​8​ ​d​e​f​a​r​g​s​[​5​5​0​4​:​8​0​7​]​ ​c​a​t​ ​t​o​y​:​ ​(​n​u​l​l​)​
2​0​1​0​-​0​9​-​2​0​ ​1​9​:​5​5​:​2​9​.​0​3​9​ ​d​e​f​a​r​g​s​[​5​5​0​4​:​8​0​7​]​ ​f​i​l​e​ ​n​a​m​e​:​ ​b​o​r​k​

5
Exceptions, Error Handling, and Signals

One of the grisly facts of programming life is that errors can happen, and program code must react to those errors and deal with them appropriately. With the Unix APIs, there are two primary ways that exceptional conditions are communicated to programs. One is through return codes from function calls plus a global variable that describes the error in more detail. The other is through signals sent to the program from the OS. The Objective-C compiler and Cocoa framework in Mac OS X also provide an exception-handling architecture that programs can take advantage of.

errno

Most of the library functions and system calls provided by Unix systems have a return value that signifies that an error happened during the execution of the call. The global integer variable errno will be set to a value to indicate what went wrong. If nothing went wrong, errno is meaningless; it should only be consulted when the function returns a value that indicates failure (generally, -1) and is documented to set errno on failure.

You can use the function strerror() to get a human description of the error. The prototype for strerror() lives in <string.h>. It is necessary to #include <errno.h> to get the definition of errno. You can look in /usr/include/sys/errno.h for the complete set of errno values. It is important to include errno.h rather than providing your own extern int errno, since errno is implemented in a thread-safe manner that is more than just a simple global int.

The manpages for Unix calls spell out in detail what the error code is and what specific errno values are set. For example, from the open(2) manpage:

O​P​E​N​(​2​)​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​y​s​t​e​m​ ​C​a​l​l​s​ ​M​a​n​u​a​l​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​O​P​E​N​(​2​)​

N​A​M​E​
 ​ ​ ​ ​ ​o​p​e​n​ ​-​ ​o​p​e​n​ ​o​r​ ​c​r​e​a​t​e​ ​a​ ​f​i​l​e​ ​f​o​r​ ​r​e​a​d​i​n​g​ ​o​r​ ​w​r​i​t​i​n​g​

S​Y​N​O​P​S​I​S​
 ​ ​ ​ ​ ​#​i​n​c​l​u​d​e​ ​<​f​c​n​t​l​.​h​>​

 ​ ​ ​ ​ ​i​n​t​
 ​ ​ ​ ​ ​o​p​e​n​ ​(​c​h​a​r​ ​*​p​a​t​h​,​ ​i​n​t​ ​f​l​a​g​s​,​ ​m​o​d​e​_​t​ ​m​o​d​e​)​;​

D​E​S​C​R​I​P​T​I​O​N​
 ​ ​ ​ ​ ​T​h​e​ ​f​i​l​e​ ​n​a​m​e​ ​s​p​e​c​i​f​i​e​d​ ​b​y​ ​p​a​t​h​ ​i​s​ ​o​p​e​n​e​d​ ​f​o​r​ ​r​e​a​d​i​n​g​ ​a​n​d​/​o​r​ ​w​r​i​t​i​n​g​,​ ​a​s​
 ​ ​ ​ ​ ​s​p​e​c​i​f​i​e​d​ ​b​y​ ​t​h​e​ ​a​r​g​u​m​e​n​t​ ​o​f​l​a​g​;​ ​t​h​e​ ​f​i​l​e​ ​d​e​s​c​r​i​p​t​o​r​ ​i​s​ ​r​e​t​u​r​n​e​d​ ​t​o​ ​t​h​e​
 ​ ​ ​ ​ ​c​a​l​l​i​n​g​ ​p​r​o​c​e​s​s​.​
 ​ ​ ​ ​ ​.​.​.​.​

R​E​T​U​R​N​ ​V​A​L​U​E​S​
 ​ ​ ​ ​ ​I​f​ ​s​u​c​c​e​s​s​f​u​l​,​ ​o​p​e​n​(​)​ ​r​e​t​u​r​n​s​ ​a​ ​n​o​n​-​n​e​g​a​t​i​v​e​ ​i​n​t​e​g​e​r​,​ ​t​e​r​m​e​d​ ​a​ ​f​i​l​e​
 ​ ​ ​ ​ ​d​e​s​c​r​i​p​t​o​r​.​ ​ ​I​t​ ​r​e​t​u​r​n​s​ ​-​1​ ​o​n​ ​f​a​i​l​u​r​e​,​ ​a​n​d​ ​s​e​t​s​ ​e​r​r​n​o​ ​t​o​ ​i​n​d​i​c​a​t​e​ ​t​h​e​
 ​ ​ ​ ​ ​e​r​r​o​r​.​

E​R​R​O​R​S​
 ​ ​ ​ ​ ​T​h​e​ ​n​a​m​e​d​ ​f​i​l​e​ ​i​s​ ​o​p​e​n​e​d​ ​u​n​l​e​s​s​:​
 ​ ​ ​
 ​ ​ ​ ​ ​[​E​N​O​S​P​C​]​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​O​_​C​R​E​A​T​ ​i​s​ ​s​p​e​c​i​f​i​e​d​,​ ​t​h​e​ ​f​i​l​e​ ​d​o​e​s​ ​n​o​t​ ​e​x​i​s​t​,​ ​a​n​d​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​h​e​r​e​ ​a​r​e​ ​n​o​ ​f​r​e​e​ ​i​n​o​d​e​s​ ​o​n​ ​t​h​e​ ​f​i​l​e​ ​s​y​s​t​e​m​ ​o​n​ ​w​h​i​c​h​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​h​e​ ​f​i​l​e​ ​i​s​ ​b​e​i​n​g​ ​c​r​e​a​t​e​d​.​

 ​ ​ ​ ​ ​[​E​N​O​T​D​I​R​]​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​A​ ​c​o​m​p​o​n​e​n​t​ ​o​f​ ​t​h​e​ ​p​a​t​h​ ​p​r​e​f​i​x​ ​i​s​ ​n​o​t​ ​a​ ​d​i​r​e​c​t​o​r​y​.​

 ​ ​ ​ ​ ​[​E​N​A​M​E​T​O​O​L​O​N​G​]​ ​ ​ ​ ​ ​A​ ​c​o​m​p​o​n​e​n​t​ ​o​f​ ​a​ ​p​a​t​h​n​a​m​e​ ​e​x​c​e​e​d​e​d​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​{​N​A​M​E​_​M​A​X​}​ ​c​h​a​r​a​c​t​e​r​s​,​ ​o​r​ ​a​n​ ​e​n​t​i​r​e​ ​p​a​t​h​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​a​m​e​ ​e​x​c​e​e​d​e​d​ ​{​P​A​T​H​_​M​A​X​}​ ​c​h​a​r​a​c​t​e​r​s​.​

 ​ ​ ​ ​ ​[​E​N​O​E​N​T​]​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​O​_​C​R​E​A​T​ ​i​s​ ​n​o​t​ ​s​e​t​ ​a​n​d​ ​t​h​e​ ​n​a​m​e​d​ ​f​i​l​e​ ​d​o​e​s​ ​n​o​t​ ​e​x​i​s​t​.​

 ​ ​ ​ ​ ​[​E​N​O​E​N​T​]​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​A​ ​c​o​m​p​o​n​e​n​t​ ​o​f​ ​t​h​e​ ​p​a​t​h​ ​n​a​m​e​ ​t​h​a​t​ ​m​u​s​t​ ​e​x​i​s​t​ ​d​o​e​s​ ​n​o​t​ ​e​x​i​s​t​.​

 ​ ​ ​ ​ ​.​.​.​

 ​ ​ ​ ​ ​[​E​F​A​U​L​T​]​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​P​a​t​h​ ​p​o​i​n​t​s​ ​o​u​t​s​i​d​e​ ​t​h​e​ ​p​r​o​c​e​s​s​’​ ​a​l​l​o​c​a​t​e​d​ ​a​d​d​r​e​s​s​ ​s​p​a​c​e​.​

 ​ ​ ​ ​ ​[​E​E​X​I​S​T​]​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​O​_​C​R​E​A​T​ ​a​n​d​ ​O​_​E​X​C​L​ ​w​e​r​e​ ​s​p​e​c​i​f​i​e​d​ ​a​n​d​ ​t​h​e​ ​f​i​l​e​ ​e​x​i​s​t​s​.​

 ​ ​ ​ ​ ​[​E​O​P​N​O​T​S​U​P​P​]​ ​ ​ ​ ​ ​ ​ ​A​n​ ​a​t​t​e​m​p​t​ ​w​a​s​ ​m​a​d​e​ ​t​o​ ​o​p​e​n​ ​a​ ​s​o​c​k​e​t​ ​(​n​o​t​ ​c​u​r​r​e​n​t​l​y​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​m​p​l​e​m​e​n​t​e​d​.​

The symbols in square brackets are the errno values that open() can set when it returns -1. Example 5.1 has code that looks for specific errors. A real life program would handle these errors in an appropriate manner.

Example 5.1. open.m

/​/​ ​o​p​e​n​.​m​ ​-​-​ ​T​r​y​ ​o​p​e​n​i​n​g​ ​f​i​l​e​s​ ​a​n​d​ ​g​e​t​t​i​n​g​ ​d​i​f​f​e​r​e​n​t​ ​e​r​r​o​r​s​.​

/​/​ ​g​c​c​ ​-​g​ ​-​W​m​o​s​t​ ​-​o​ ​o​p​e​n​ ​o​p​e​n​.​m​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​f​c​n​t​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​o​p​e​n​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​,​ ​e​t​c​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​(​)​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​t​a​t​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​e​r​m​i​s​s​i​o​n​ ​f​l​a​g​s​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​c​l​o​s​e​(​)​

/​/​ ​G​i​v​e​n​ ​a​ ​p​a​t​h​ ​a​n​d​ ​a​c​c​e​s​s​ ​f​l​a​g​s​,​ ​t​r​y​ ​t​o​ ​o​p​e​n​ ​t​h​e​ ​f​i​l​e​.​ ​ ​I​f​ ​a​n​ ​e​r​r​o​r​
/​/​ ​h​a​p​p​e​n​s​,​ ​w​r​i​t​e​ ​i​t​ ​o​u​t​ ​t​o​ ​s​t​a​n​d​a​r​d​ ​e​r​r​o​r​.​

v​o​i​d​ ​t​r​y​O​p​e​n​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​p​a​t​h​,​ ​i​n​t​ ​f​l​a​g​s​)​ ​{​
 ​ ​ ​ ​/​/​ ​A​t​t​e​m​p​t​ ​t​o​ ​o​p​e​n​ ​r​e​a​d​/​w​r​i​t​e​ ​f​o​r​ ​u​s​e​r​/​g​r​o​u​p​.​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​o​p​e​n​ ​(​p​a​t​h​,​ ​f​l​a​g​s​,​ ​S​_​I​R​U​S​R​ ​|​ ​S​_​I​W​U​S​R​ ​|​ ​S​_​I​R​G​R​P​ ​ ​|​ ​S​_​I​W​G​R​P​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​a​n​ ​e​r​r​o​r​ ​h​a​p​p​e​n​e​d​ ​o​p​e​n​i​n​g​ ​%​s​\​n​"​,​ ​p​a​t​h​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​s​w​i​t​c​h​ ​(​e​r​r​n​o​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​E​N​O​T​D​I​R​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​ ​ ​ ​ ​p​a​r​t​ ​o​f​ ​t​h​e​ ​p​a​t​h​ ​i​s​ ​n​o​t​ ​a​ ​d​i​r​e​c​t​o​r​y​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​E​N​O​E​N​T​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​ ​ ​ ​ ​s​o​m​e​t​h​i​n​g​ ​d​o​e​s​n​'​t​ ​e​x​i​s​t​,​ ​l​i​k​e​ ​p​a​r​t​ ​o​f​ ​a​ ​p​a​t​h​,​ ​o​r​\​n​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​ ​ ​ ​ ​O​_​C​R​E​A​T​ ​i​s​ ​n​o​t​ ​s​e​t​ ​a​n​d​ ​t​h​e​ ​f​i​l​e​ ​d​o​e​s​n​'​t​ ​e​x​i​s​t​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​E​I​S​D​I​R​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​ ​ ​ ​ ​t​r​i​e​d​ ​t​o​ ​o​p​e​n​ ​a​ ​d​i​r​e​c​t​o​r​y​ ​f​o​r​ ​w​r​i​t​i​n​g​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​f​a​u​l​t​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​ ​ ​ ​ ​a​n​o​t​h​e​r​ ​e​r​r​o​r​ ​h​a​p​p​e​n​e​d​:​ ​ ​e​r​r​n​o​ ​%​d​,​ ​s​t​r​e​r​r​o​r​:​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​l​o​s​e​ ​(​r​e​s​u​l​t​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​\​n​"​)​;​

}​ ​/​/​ ​t​r​y​O​p​e​n​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​/​/​ ​t​r​i​g​g​e​r​ ​E​N​O​T​D​I​R​
 ​ ​ ​ ​t​r​y​O​p​e​n​ ​(​"​/​m​a​c​h​.​s​y​m​/​b​l​a​h​/​b​l​a​h​"​,​ ​O​_​R​D​O​N​L​Y​)​;​

 ​ ​ ​ ​/​/​ ​t​r​i​g​g​e​r​ ​E​N​O​E​N​T​,​ ​p​a​r​t​ ​o​f​ ​t​h​e​ ​p​a​t​h​ ​d​o​e​s​n​'​t​ ​e​x​i​s​t​
 ​ ​ ​ ​t​r​y​O​p​e​n​ ​(​"​/​S​y​s​t​e​m​/​F​r​a​m​e​w​o​r​k​s​/​b​o​r​k​/​m​y​-​f​i​l​e​"​,​ ​O​_​R​D​O​N​L​Y​)​;​

 ​ ​ ​ ​/​/​ ​t​r​i​g​g​e​r​ ​E​N​O​E​N​T​,​ ​O​_​C​R​E​A​T​ ​n​o​t​ ​s​e​t​ ​a​n​d​ ​f​i​l​e​ ​d​o​e​s​n​'​t​ ​e​x​i​s​t​
 ​ ​ ​ ​t​r​y​O​p​e​n​ ​(​"​/​t​m​p​/​m​y​-​f​i​l​e​"​,​ ​O​_​R​D​O​N​L​Y​)​;​

 ​ ​ ​ ​/​/​ ​t​r​i​g​g​e​r​ ​E​I​S​D​I​R​
 ​ ​ ​ ​t​r​y​O​p​e​n​ ​(​"​/​d​e​v​"​,​ ​O​_​W​R​O​N​L​Y​)​;​

 ​ ​ ​ ​/​/​ ​t​r​i​g​g​e​r​ ​E​E​X​I​S​T​
 ​ ​ ​ ​t​r​y​O​p​e​n​ ​(​"​/​p​r​i​v​a​t​e​/​v​a​r​/​l​o​g​/​s​y​s​t​e​m​.​l​o​g​"​,​ ​O​_​C​R​E​A​T​ ​|​ ​O​_​E​X​C​L​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

In most cases it is not possible to handle every possible error condition (like ENFILE: System file table is full). In general, try to handle whichever errors makes sense and have a catch-all case that will log the error.

The main downside with this return code/errno reporting technique is that it is necessary to check the result code of every library function call, which can get tedious pretty quickly. Plus, all of the error-handling code obscures the flow of control. Some programmers write error-handling wrappers around library functions so that return codes do not pollute the mainline code; others instead use goto to jump to error-handling code at the end of the function.

With that caveat in mind, you are free to use this convention for your own code, which can be nice when you are supplying a library to programmers familiar with the Unix conventions. There is no standard way to add your own error strings to strerror(), unfortunately.

setjmp, longjmp

Languages like C++ and Java have exception-handling features built in. This is where code can happily go about its business, ignoring anything that might go wrong with the functions it is calling. But if something does go wrong, an exception can be thrown which will terminate the current flow of execution. Control resumes execution at a previously registered exception handler, which can then decide how best to recover from the problem and resume the work.

C has a primitive form of exception handling that can be used in a similar manner. The setjmp and longjmp functions are used like a super-goto:

int setjmp (jmp_buf env);

void longjmp (jmp_buf env, int value);

jmp_buf is a data structure that holds the current execution context (the current program counter, stack pointer, etc). You setjmp where you want execution to return (equivalent to your exception handler) and call longjmp() when you want to branch back to that point (equivalent to throwing an exception).

Example 5.2 shows how to use longjmp().

Example 5.2. longjmp.m

/​/​ ​l​o​n​g​j​m​p​.​m​ ​-​-​ ​u​s​e​ ​s​e​t​j​m​p​,​ ​l​o​n​g​j​m​p​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​l​o​n​g​j​m​p​ ​l​o​n​g​j​m​p​.​m​

#​i​m​p​o​r​t​ ​<​s​e​t​j​m​p​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​e​t​j​m​p​ ​/​ ​l​o​n​g​j​m​p​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​

s​t​a​t​i​c​ ​j​m​p​_​b​u​f​ ​h​a​n​d​l​e​r​;​

v​o​i​d​ ​d​o​E​v​e​n​M​o​r​e​S​t​u​f​f​ ​(​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​ ​ ​ ​ ​ ​ ​e​n​t​e​r​i​n​g​ ​d​o​E​v​e​n​M​o​r​e​S​t​u​f​f​\​n​"​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​ ​ ​ ​ ​ ​ ​d​o​n​e​ ​w​i​t​h​ ​d​o​E​v​e​n​M​o​r​e​S​t​u​f​f​\​n​"​)​;​
}​ ​/​/​ ​d​o​E​v​e​n​M​o​r​e​S​t​u​f​f​

v​o​i​d​ ​d​o​M​o​r​e​S​t​u​f​f​ ​(​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​ ​ ​e​n​t​e​r​i​n​g​ ​d​o​M​o​r​e​S​t​u​f​f​\​n​"​)​;​
 ​ ​ ​ ​d​o​E​v​e​n​M​o​r​e​S​t​u​f​f​ ​(​)​;​
 ​ ​ ​ ​l​o​n​g​j​m​p​ ​(​h​a​n​d​l​e​r​,​ ​2​3​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​ ​ ​d​o​n​e​ ​w​i​t​h​ ​d​o​M​o​r​e​S​t​u​f​f​\​n​"​)​;​
}​ ​/​/​ ​d​o​M​o​r​e​S​t​u​f​f​

v​o​i​d​ ​d​o​S​t​u​f​f​ ​(​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​e​n​t​e​r​i​n​g​ ​d​o​S​t​u​f​f​\​n​"​)​;​
 ​ ​ ​ ​d​o​M​o​r​e​S​t​u​f​f​ ​(​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​d​o​n​e​ ​w​i​t​h​ ​d​o​S​t​u​f​f​\​n​"​)​;​
}​ ​/​/​ ​d​o​S​t​u​f​f​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​;​

 ​ ​ ​ ​i​f​ ​(​ ​(​r​e​s​u​l​t​ ​=​ ​s​e​t​j​m​p​(​h​a​n​d​l​e​r​)​)​ ​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​l​o​n​g​j​u​m​p​ ​c​a​l​l​e​d​,​ ​s​e​t​j​m​p​ ​r​e​t​u​r​n​e​d​ ​a​g​a​i​n​:​ ​%​d​\​n​"​,​ ​r​e​s​u​l​t​)​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​d​o​S​t​u​f​f​ ​(​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​(​E​X​I​T​_​S​U​C​C​E​S​S​)​;​
}​ ​/​/​ ​m​a​i​n​

A sample run:

$​ ​.​/​l​o​n​g​j​m​p​
e​n​t​e​r​i​n​g​ ​d​o​S​t​u​f​f​
 ​ ​ ​ ​e​n​t​e​r​i​n​g​ ​d​o​M​o​r​e​S​t​u​f​f​
 ​ ​ ​ ​ ​ ​ ​ ​e​n​t​e​r​i​n​g​ ​d​o​E​v​e​n​M​o​r​e​S​t​u​f​f​
 ​ ​ ​ ​ ​ ​ ​ ​d​o​n​e​ ​w​i​t​h​ ​d​o​E​v​e​n​M​o​r​e​S​t​u​f​f​
l​o​n​g​j​u​m​p​ ​c​a​l​l​e​d​,​ ​s​e​t​j​m​p​ ​r​e​t​u​r​n​e​d​ ​a​g​a​i​n​:​ ​2​3​

Two of the functions never get to print out their “done with” statements. They just get jumped over.

The interesting piece here is the if statement. When setjmp() is called, it returns zero, so the second branch of the if is taken. When longjmp() is called, that if statement is essentially evaluated again, and execution begins again at that point. The value argument to longjmp() is what is returned from setjmp() the second time it returns.

Any number of setjmp() calls can be active at any point in time as long as they use different memory locations for their jmp_bufs. You can maintain a stack of jmp_bufs so that longjmp knows to jump to the closest setjmp(). This is what Cocoa uses for its “classic” exception handling mechanism (discussed after Signals).

There is one rule to remember when using setjmp() and longjmp(): any local variables in the function that calls setjmp() that might be used after a longjmp() must be declared volatile. That will force the compiler to read the variables from memory each time rather than using processor registers. setjmp() saves some processor state, but it does not save every register. When longjmp() branches back to its matching setjmp(), any garbage in the registers can give you wrong values in variables.

Signals

Signals are like software interrupts: they can be delivered to your program at any time due to a number of well-defined conditions, like when you write outside of your mapped memory pages, you will get sent a SIGBUS (bus error) or a SIGSEGV (segmentation violation) signal. If a subprocess of yours terminates, you will get a SIGCHLD (child stopped) signal. If your controlling terminal goes away, there is SIGHUP (terminal hung up), and if you use the alarm() function, you will get sent SIGALRM when the time expires. The system defines about 31 different signals, many of which deal with job control or specific hardware issues.

A signal is delivered to your program asynchronously whenever it enters the operating system, whether it be via a system call or just regular process scheduling. This means that your code can be interrupted by a signal at pretty much any time.

As you saw above, signals are named with SIG plus an abbreviation of what the signal does. These are defined in <sys/signal.h> if you are curious. The signal manpage has the complete list of signals. The signal function (a simplified form of sigaction() that we will discuss shortly) is used to provide a handler for a signal.

Handling a signal

Use the signal function to register a signal handler.

typedef void (*sig_t) (int);

sig_t signal (int sig, sig_t func);

where sig is the signal number (e.g., SIGHUP) and func is the handler function. If you do not call signal() for a particular signal, the system default handler is used. Depending on the signal, the default handler will either ignore the signal or terminate the process. Check the signal(2) manpage for details on which is which.

Example 5.3 shows a program that registers signal handlers for three signals. The program will catch the signals and either print out that the signal that was caught (SIGHUP and SIGUSR1) or exit (SIGUSR2). By the way, SIGUSR1 and SIGUSR2 are signals that your program can use for its own purposes. The OS will not send you those signals unless explicitly told to.

Example 5.3. catch.m

/​/​ ​c​a​t​c​h​.​m​ ​-​-​ ​c​a​t​c​h​ ​s​o​m​e​ ​s​i​g​n​a​l​s​

/​/​ ​g​c​c​ ​-​g​ ​-​s​t​d​=​c​9​9​ ​-​W​a​l​l​ ​-​o​ ​c​a​t​c​h​ ​c​a​t​c​h​.​m​

#​i​m​p​o​r​t​ ​<​s​i​g​n​a​l​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​i​g​n​a​l​ ​f​u​n​c​t​i​o​n​s​ ​a​n​d​ ​t​y​p​e​s​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​p​r​i​n​t​f​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​l​e​n​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​l​e​e​p​

s​t​a​t​i​c​ ​v​o​i​d​ ​w​r​i​t​e​S​t​r​i​n​g​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​s​t​r​i​n​g​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​l​e​n​g​t​h​ ​=​ ​s​t​r​l​e​n​ ​(​s​t​r​i​n​g​)​;​
 ​ ​ ​ ​w​r​i​t​e​ ​(​S​T​D​O​U​T​_​F​I​L​E​N​O​,​ ​s​t​r​i​n​g​,​ ​l​e​n​g​t​h​)​;​
}​ ​/​/​ ​w​r​i​t​e​S​t​r​i​n​g​

v​o​i​d​ ​h​a​n​d​l​e​H​U​P​ ​(​i​n​t​ ​s​i​g​n​o​)​ ​{​
 ​ ​ ​ ​w​r​i​t​e​S​t​r​i​n​g​ ​(​"​g​o​t​ ​a​ ​H​U​P​!​\​n​"​)​;​
}​ ​/​/​ ​h​a​n​d​l​e​H​U​P​

v​o​i​d​ ​h​a​n​d​l​e​U​s​r​1​U​s​r​2​ ​(​i​n​t​ ​s​i​g​n​o​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​s​i​g​n​o​ ​=​=​ ​S​I​G​U​S​R​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​w​r​i​t​e​S​t​r​i​n​g​ ​(​"​g​o​t​ ​a​ ​S​I​G​U​S​R​1​\​n​"​)​;​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​s​i​g​n​o​ ​=​=​ ​S​I​G​U​S​R​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​w​r​i​t​e​S​t​r​i​n​g​ ​(​"​g​o​t​ ​a​ ​S​I​G​U​S​R​2​.​ ​e​x​i​t​i​n​g​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​e​x​i​t​ ​(​E​X​I​T​_​S​U​C​C​E​S​S​)​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​h​a​n​d​l​e​U​s​r​1​U​s​r​2​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​/​/​ ​R​e​g​i​s​t​e​r​ ​t​h​e​ ​s​i​g​n​a​l​ ​h​a​n​d​l​e​r​s​

 ​ ​ ​ ​(​v​o​i​d​)​ ​s​i​g​n​a​l​ ​(​S​I​G​H​U​P​,​ ​h​a​n​d​l​e​H​U​P​)​;​
 ​ ​ ​ ​(​v​o​i​d​)​ ​s​i​g​n​a​l​ ​(​S​I​G​U​S​R​1​,​ ​h​a​n​d​l​e​U​s​r​1​U​s​r​2​)​;​
 ​ ​ ​ ​(​v​o​i​d​)​ ​s​i​g​n​a​l​ ​(​S​I​G​U​S​R​2​,​ ​h​a​n​d​l​e​U​s​r​1​U​s​r​2​)​;​

 ​ ​ ​ ​/​/​ ​N​o​w​ ​f​o​r​ ​o​u​r​ ​"​r​e​a​l​ ​w​o​r​k​"​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​5​0​0​0​0​0​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​i​ ​i​s​ ​%​d​\​n​"​,​ ​i​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​l​e​e​p​ ​(​1​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​/​/​ ​m​a​i​n​

Here is a sample run. It can be done with two terminals: one to see the output and the other to run the kill command, which sends signals to programs. Or it can be performed in one terminal because catch is put into the background for easy access to its process ID.

T​e​r​m​i​n​a​l​ ​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​T​e​r​m​i​n​a​l​ ​2​
$​ ​.​/​c​a​t​c​h​ ​&​
[​1​]​ ​7​4​2​9​ ​ ​(​t​h​i​s​ ​i​s​ ​t​h​e​ ​p​r​o​c​e​s​s​ ​I​D​)​
1​
2​
3​
4​
5​
6​
7​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​i​l​l​ ​-​H​U​P​ ​7​4​2​9​
g​o​t​ ​a​ ​H​U​P​!​
8​
9​
1​0​
1​1​
1​2​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​i​l​l​ ​-​U​S​R​1​ ​7​4​2​9​
g​o​t​ ​a​ ​S​I​G​U​S​R​1​
1​3​
1​4​
1​5​
1​6​
1​7​
1​8​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​i​l​l​ ​-​U​S​R​2​ ​7​4​2​9​
g​o​t​ ​a​ ​S​I​G​U​S​R​2​,​ ​e​x​i​t​i​n​g​
[​1​]​ ​ ​D​o​n​e​ ​ ​.​/​c​a​t​c​h​

A signal handler can handle more than one signal. Also, the return value of signal() is the previously registered function. If you are adding a signal handler to a library, or know that more than one handler will be registered for a signal, you should hang on to that return value and call that when your handler is invoked. If you want to register the same function for a bunch of signals, you will need to call signal() a bunch of times.

To ignore a signal, use the constant SIG_IGN instead of a function address. To restore the default behavior, use SIG_DFL. You cannot ignore or block SIGKILL or SIGSTOP. These give system administrators the ability to stop or kill any process that has run amok.

Use the raise() system call to send yourself a signal.

int raise (int sig);

sig is the number of the signal to raise. Your program can terminate itself by raising SIGKILL.

Blocking signals

Sometimes it is inconvenient to have a signal handler called during a critical piece of code. You may want to use a signal to interrupt a long-running process, but you do not want to stop in the middle of a complex data structure change and leave your program’s environment in an inconsistent state. The signal mask can help you protect these critical sections.

Every running program has a signal mask associated with it. This is a bitmask that specifies which signals are blocked from delivery, as shown in Figure 5.1. The kernel tracks which blocked signals have been sent to the application so it can deliver them when they become unblocked, but it does not track the number of times a signal has been sent. A blocked signal will only be delivered to a process after it has been unblocked.

Figure 5.1  Blocking signals

You use sigprocmask() to control the signal mask:

int sigprocmask (int how, const sigset_t *set, sigset_t *oset);

how is one of

SIG_BLOCK | Add the given signals to the program’s signal mask (union).
SIG_UNBLOCK | Remove the signals from the program’s signal mask (intersection).
SIG_SETMASK | Replace the program’s signal mask with the new one.

sigset_t is an abstract type that represents the signal mask. set is the set of signals you want to add or remove, and oset is the original set (which is handy for feeding back into sigprocmask with SIG_SETMASK). You manipulate sigset_t with these functions (defined in man sigsetops(3)):

int sigemptyset (sigset_t *set);

Clears a set to all zeros (no signals).

int sigfillset (sigset_t *set);

Fill it with all ones (all signals).

int sigaddset (sigset_t *set, int signo);

Add a specific signal to the set.

int sigdelset (sigset_t *set, int signo);

Remove a specific signal from the set.

int sigismember (const sigset_t *set, int signo);

Test a signal’s membership in the set.

Example 5.4 is a variation of the catch program above, but instances in which i is not a multiple of five are considered a critical section.

Example 5.4. catchblock.m

/​/​ ​c​a​t​c​h​b​l​o​c​k​.​m​ ​-​-​ ​C​a​t​c​h​ ​a​n​d​ ​b​l​o​c​k​ ​s​o​m​e​ ​s​i​g​n​a​l​s​

/​/​ ​g​c​c​ ​-​g​ ​-​s​t​d​=​c​9​9​ ​-​W​a​l​l​ ​-​o​ ​c​a​t​c​h​b​l​o​c​k​ ​c​a​t​c​h​b​l​o​c​k​.​m​

#​i​m​p​o​r​t​ ​<​s​i​g​n​a​l​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​i​g​n​a​l​ ​f​u​n​c​t​i​o​n​s​ ​a​n​d​ ​t​y​p​e​s​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​p​r​i​n​t​f​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​l​e​n​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​l​e​e​p​

s​t​a​t​i​c​ ​v​o​i​d​ ​w​r​i​t​e​S​t​r​i​n​g​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​s​t​r​i​n​g​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​l​e​n​g​t​h​ ​=​ ​s​t​r​l​e​n​ ​(​s​t​r​i​n​g​)​;​
 ​ ​ ​ ​w​r​i​t​e​ ​(​S​T​D​O​U​T​_​F​I​L​E​N​O​,​ ​s​t​r​i​n​g​,​ ​l​e​n​g​t​h​)​;​
}​ ​/​/​ ​w​r​i​t​e​S​t​r​i​n​g​

v​o​i​d​ ​h​a​n​d​l​e​H​U​P​ ​(​i​n​t​ ​s​i​g​n​o​)​ ​{​
 ​ ​ ​ ​w​r​i​t​e​S​t​r​i​n​g​ ​(​"​g​o​t​ ​a​ ​H​U​P​!​\​n​"​)​;​
}​ ​/​/​ ​h​a​n​d​l​e​H​U​P​

v​o​i​d​ ​h​a​n​d​l​e​U​s​r​1​U​s​r​2​ ​(​i​n​t​ ​s​i​g​n​o​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​s​i​g​n​o​ ​=​=​ ​S​I​G​U​S​R​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​w​r​i​t​e​S​t​r​i​n​g​ ​(​"​g​o​t​ ​a​ ​S​I​G​U​S​R​1​\​n​"​)​;​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​s​i​g​n​o​ ​=​=​ ​S​I​G​U​S​R​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​w​r​i​t​e​S​t​r​i​n​g​ ​(​"​g​o​t​ ​a​ ​S​I​G​U​S​R​2​.​ ​e​x​i​t​i​n​g​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​e​x​i​t​ ​(​E​X​I​T​_​S​U​C​C​E​S​S​)​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​h​a​n​d​l​e​U​s​r​1​U​s​r​2​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​/​/​ ​R​e​g​i​s​t​e​r​ ​o​u​r​ ​s​i​g​n​a​l​ ​h​a​n​d​l​e​r​s​
 ​ ​ ​ ​(​v​o​i​d​)​ ​s​i​g​n​a​l​ ​(​S​I​G​H​U​P​,​ ​h​a​n​d​l​e​H​U​P​)​;​
 ​ ​ ​ ​(​v​o​i​d​)​ ​s​i​g​n​a​l​ ​(​S​I​G​U​S​R​1​,​ ​h​a​n​d​l​e​U​s​r​1​U​s​r​2​)​;​
 ​ ​ ​ ​(​v​o​i​d​)​ ​s​i​g​n​a​l​ ​(​S​I​G​U​S​R​2​,​ ​h​a​n​d​l​e​U​s​r​1​U​s​r​2​)​;​

 ​ ​ ​ ​/​/​ ​c​o​n​s​t​r​u​c​t​ ​o​u​r​ ​s​i​g​n​a​l​ ​m​a​s​k​.​ ​ ​W​e​ ​d​o​n​'​t​ ​w​a​n​t​ ​t​o​ ​b​e​ ​b​o​t​h​e​r​e​d​
 ​ ​ ​ ​/​/​ ​b​y​ ​S​I​G​U​S​R​1​ ​o​r​ ​S​I​G​U​S​R​2​ ​i​n​ ​o​u​r​ ​c​r​i​t​i​c​a​l​ ​s​e​c​t​i​o​n​.​
 ​ ​ ​ ​/​/​ ​b​u​t​ ​w​e​'​l​l​ ​l​e​a​v​e​ ​S​I​G​H​U​P​ ​o​u​t​ ​o​f​ ​t​h​e​ ​m​a​s​k​ ​s​o​ ​t​h​a​t​ ​i​t​ ​w​i​l​l​ ​g​e​t​
 ​ ​ ​ ​/​/​ ​d​e​l​i​v​e​r​e​d​

 ​ ​ ​ ​s​i​g​s​e​t​_​t​ ​s​i​g​n​a​l​M​a​s​k​;​
 ​ ​ ​ ​s​i​g​e​m​p​t​y​s​e​t​ ​(​&​s​i​g​n​a​l​M​a​s​k​)​;​
 ​ ​ ​ ​s​i​g​a​d​d​s​e​t​ ​(​&​s​i​g​n​a​l​M​a​s​k​,​ ​S​I​G​U​S​R​1​)​;​
 ​ ​ ​ ​s​i​g​a​d​d​s​e​t​ ​(​&​s​i​g​n​a​l​M​a​s​k​,​ ​S​I​G​U​S​R​2​)​;​

 ​ ​ ​ ​/​/​ ​n​o​w​ ​d​o​ ​o​u​r​ ​R​e​a​l​ ​W​o​r​k​

 ​ ​ ​ ​s​i​g​s​e​t​_​t​ ​o​l​d​S​i​g​n​a​l​M​a​s​k​;​

 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​5​0​0​0​0​0​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​i​ ​i​s​ ​%​d​\​n​"​,​ ​i​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​ ​(​i​ ​%​ ​5​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​b​l​o​c​k​i​n​g​ ​a​t​ ​%​i​\​n​"​,​ ​i​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​g​p​r​o​c​m​a​s​k​ ​(​S​I​G​_​B​L​O​C​K​,​ ​&​s​i​g​n​a​l​M​a​s​k​,​ ​&​o​l​d​S​i​g​n​a​l​M​a​s​k​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​ ​(​i​ ​%​ ​5​)​ ​=​=​ ​4​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​u​n​b​l​o​c​k​i​n​g​ ​a​t​ ​%​i​\​n​"​,​ ​i​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​g​p​r​o​c​m​a​s​k​(​S​I​G​_​S​E​T​M​A​S​K​,​ ​&​o​l​d​S​i​g​n​a​l​M​a​s​k​,​ ​N​U​L​L​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​s​l​e​e​p​ ​(​1​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​/​/​ ​m​a​i​n​

A sample run:

T​e​r​m​i​n​a​l​ ​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​T​e​r​m​i​n​a​l​ ​2​
.​/​c​a​t​c​h​b​l​o​c​k​ ​&​
[​1​]​ ​7​5​3​3​
i​ ​i​s​ ​0​
b​l​o​c​k​i​n​g​ ​a​t​ ​0​
i​ ​i​s​ ​1​
i​ ​i​s​ ​2​
i​ ​i​s​ ​3​
i​ ​i​s​ ​4​
u​n​b​l​o​c​k​i​n​g​ ​a​t​ ​4​
i​ ​i​s​ ​5​
b​l​o​c​k​i​n​g​ ​a​t​ ​5​
i​ ​i​s​ ​6​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​i​l​l​ ​-​H​U​P​ ​7​5​3​3​
g​o​t​ ​a​ ​H​U​P​!​
i​ ​i​s​ ​7​
i​ ​i​s​ ​8​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​i​l​l​ ​-​H​U​P​ ​7​5​3​3​
g​o​t​ ​a​ ​H​U​P​!​
i​ ​i​s​ ​9​
u​n​b​l​o​c​k​i​n​g​ ​a​t​ ​9​
i​ ​i​s​ ​1​0​
b​l​o​c​k​i​n​g​ ​a​t​ ​1​0​
i​ ​i​s​ ​1​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​i​l​l​ ​-​U​S​R​1​ ​7​5​3​3​
i​ ​i​s​ ​1​2​
i​ ​i​s​ ​1​3​
i​ ​i​s​ ​1​4​
u​n​b​l​o​c​k​i​n​g​ ​a​t​ ​1​4​
g​o​t​ ​a​ ​S​I​G​U​S​R​1​
i​ ​i​s​ ​1​5​
b​l​o​c​k​i​n​g​ ​a​t​ ​1​5​
i​ ​i​s​ ​1​6​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​i​l​l​ ​-​U​S​R​1​ ​7​5​3​3​
i​ ​i​s​ ​1​7​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​i​l​l​ ​-​U​S​R​1​ ​7​5​3​3​
i​ ​i​s​ ​1​8​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​i​l​l​ ​-​U​S​R​1​ ​7​5​3​3​
i​ ​i​s​ ​1​9​
u​n​b​l​o​c​k​i​n​g​ ​a​t​ ​1​9​
g​o​t​ ​a​ ​S​I​G​U​S​R​1​
i​ ​i​s​ ​2​0​
b​l​o​c​k​i​n​g​ ​a​t​ ​2​0​
i​ ​i​s​ ​2​1​
i​ ​i​s​ ​2​2​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​i​l​l​ ​-​U​S​R​2​ ​7​5​3​3​
i​ ​i​s​ ​2​3​
i​ ​i​s​ ​2​4​
u​n​b​l​o​c​k​i​n​g​ ​a​t​ ​2​4​
g​o​t​ ​a​ ​S​I​G​U​S​R​2​.​ ​e​x​i​t​i​n​g​

It works as expected: HUPs make it through immediately, and USR1 and USR2 are only handled once at the unblocking no matter how many times they have been sent.

You can use sigpending() to see if a signal of interest is pending:

int sigpending (sigset_t *set);

This returns by reference a mask of the pending signals.

Handling signals with sigaction()

sigaction() is the full-featured way to handle signals:

s​t​r​u​c​t​ ​s​i​g​a​c​t​i​o​n​ ​{​
 ​ ​ ​ ​v​o​i​d​ ​ ​ ​ ​ ​(​*​s​a​_​h​a​n​d​l​e​r​)​(​)​;​
 ​ ​ ​ ​s​i​g​s​e​t​_​t​ ​s​a​_​m​a​s​k​;​
 ​ ​ ​ ​i​n​t​ ​ ​ ​ ​ ​ ​s​a​_​f​l​a​g​s​;​
}​;​

int sigaction (int sig, const struct sigaction *act,
              struct sigaction *oact);

Instead of just passing in a handler function, you pass in a structure containing the handler function, the set of signals that should be added to the process signal mask, and some flags. Usually you just set them to zero. The sigaction() manpage describes them, mainly used for some SIGCHLD signals or for controlling which stack is used when signals are handled.

Like signal(), the previous setting is returned, this time in the oact parameter if it is non-NULL.

Signal issues

Reentrancy

There are a number of difficult programming issues involved with signals which sometimes makes them more difficult to deal with than they’re worth. The first is reentrancy, and the second concerns race conditions.

A reentrant function is one that will work if there are two execution streams active in it at one time. This can happen even without threads. For example, consider this code:

.​.​.​
p​t​r​ ​=​ ​m​a​l​l​o​c​ ​(​5​0​)​;​
.​.​.​

The program is in the middle of calling malloc(), and malloc() is messing with its internal data structures and is in an inconsistent state. Then a signal happens. Because signals happen asynchronously, your program is interrupted, and the signal handler runs:

.​.​.​
t​e​m​p​P​t​r​ ​=​ ​m​a​l​l​o​c​ ​(​2​0​)​;​
.​.​.​

Because malloc() is in the middle of its previous work, the program will most likely crash.

In your signal handlers, you should only use reentrant functions. The sigaction() manpage has a list of functions that are either reentrant or are not interruptable by signals.

In general, these are safe:

longjmp()

Reentrant versions of functions, like strtok_r()

Program terminators like abort() and exit()

Unbuffered I/O (read(), write(), open(), etc.)

Interrogative functions (getgid(), getpid(), getuid())

Signal functions (sigaction(), sigprocmask())

Any of your own reentrant functions

These are unsafe:

Buffered I/O (printf() and friends). That’s why a custom writeString function was used earlier.

malloc() and free()

Anything using static buffer space, like strtok()

Any of your own non reentrant functions

You do not need to make your signal handler reentrant. When a signal handler is entered, the signal that triggered the handler is automatically added to the process signal mask. The handler will not get triggered again until it returns. Note that if a handler is registered for more than one signal using signal() or sigaction(), you will need to make it reentrant unless the other signals are blocked by the sa_mask member of the structure supplied to sigaction().

You may notice that longjmp() is on the set of safe functions. You are free to longjmp() out of a signal handler to wherever the matching setjmp() was placed. This is a way of handling the interruption of a long-running process.

You may wonder about the process signal mask. If you longjmp() out of a handler, is the signal still blocked? In Mac OS X, longjmp() automatically restores the signal mask when jumping out of a signal handler. If you do not want this behavior (like what exists on some other Unixes), you can use the _setjmp() and _longjmp() functions.

If you’d rather not rely on this behavior, for example you plan to use the code under another Unix-alike, you can use the sigsetjmp()/siglongjmp() pair of functions, which are guaranteed to save or not save and restore the signal mask at your choice:

int sigsetjmp (sigjmp_buf env, int savemask);

void siglongjmp (sigjmp_buf env, int val);

If savemask is non-zero, the signal mask will be saved and restored; if zero, the signal mask will be unaffected by use of siglongjmp().

Example 5.5 is an example of breaking out of a long-running process.

Example 5.5. interrupt.m

/​/​ ​i​n​t​e​r​r​u​p​t​.​m​ ​-​-​ ​s​h​o​w​ ​i​n​t​e​r​r​u​p​t​i​o​n​ ​o​f​ ​a​ ​l​o​n​g​-​r​u​n​n​i​n​g​ ​p​r​o​c​e​s​s​

/​/​ ​c​c​ ​-​g​ ​-​s​t​d​=​c​9​9​ ​-​W​a​l​l​ ​-​o​ ​i​n​t​e​r​r​u​p​t​ ​i​n​t​e​r​r​u​p​t​.​m​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​s​e​t​j​m​p​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​e​t​j​m​p​ ​/​ ​l​o​n​g​j​m​p​
#​i​m​p​o​r​t​ ​<​s​i​g​n​a​l​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​i​g​n​a​l​ ​f​u​n​c​t​i​o​n​s​ ​a​n​d​ ​t​y​p​e​s​
#​i​m​p​o​r​t​ ​<​s​t​d​b​o​o​l​.​h​>​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​b​o​o​l​ ​t​y​p​e​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​l​e​e​p​

s​t​a​t​i​c​ ​j​m​p​_​b​u​f​ ​h​a​n​d​l​e​r​;​

v​o​i​d​ ​h​a​n​d​l​e​S​i​g​n​a​l​ ​(​i​n​t​ ​s​i​g​n​o​)​ ​{​
 ​ ​ ​ ​l​o​n​g​j​m​p​ ​(​h​a​n​d​l​e​r​,​ ​1​)​;​
}​ ​/​/​ ​h​a​n​d​l​e​S​i​g​n​a​l​

v​o​i​d​ ​d​o​L​o​t​s​O​f​W​o​r​k​ ​(​)​ ​{​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​5​0​0​0​0​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​i​ ​i​s​ ​%​d​\​n​"​,​ ​i​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​l​e​e​p​ ​(​1​)​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​d​o​L​o​t​s​O​f​W​o​r​k​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​s​t​r​u​c​t​ ​s​i​g​a​c​t​i​o​n​ ​a​c​t​i​o​n​;​
 ​ ​ ​ ​s​i​g​e​m​p​t​y​s​e​t​ ​(​&​a​c​t​i​o​n​.​s​a​_​m​a​s​k​)​;​
 ​ ​ ​ ​s​i​g​a​d​d​s​e​t​ ​(​&​a​c​t​i​o​n​.​s​a​_​m​a​s​k​,​ ​S​I​G​T​E​R​M​)​;​

 ​ ​ ​ ​a​c​t​i​o​n​.​s​a​_​h​a​n​d​l​e​r​ ​=​ ​h​a​n​d​l​e​S​i​g​n​a​l​;​
 ​ ​ ​ ​a​c​t​i​o​n​.​s​a​_​f​l​a​g​s​ ​=​ ​0​;​

 ​ ​ ​ ​i​f​ ​(​s​i​g​a​c​t​i​o​n​ ​(​S​I​G​U​S​R​1​,​ ​&​a​c​t​i​o​n​,​ ​N​U​L​L​)​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​ ​i​n​ ​s​i​g​a​c​t​i​o​n​:​ ​%​d​ ​/​ ​%​s​\​n​"​,​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​v​o​l​a​t​i​l​e​ ​b​o​o​l​ ​h​a​n​d​l​e​r​S​e​t​ ​=​ ​0​;​
 ​ ​ ​ ​w​h​i​l​e​ ​(​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​!​h​a​n​d​l​e​r​S​e​t​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​s​e​t​j​m​p​ ​(​h​a​n​d​l​e​r​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​e​ ​l​o​n​g​j​m​p​'​d​ ​t​o​ ​h​e​r​e​.​ ​ ​R​e​s​e​t​ ​t​h​e​ ​h​a​n​d​l​e​r​ ​n​e​x​t​ ​t​i​m​e​ ​a​r​o​u​n​d​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​a​n​d​l​e​r​S​e​t​ ​=​ ​0​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​i​n​u​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​a​n​d​l​e​r​S​e​t​ ​=​ ​1​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​(​"​s​t​a​r​t​i​n​g​ ​l​o​t​s​ ​o​f​ ​w​o​r​k​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​d​o​L​o​t​s​O​f​W​o​r​k​ ​(​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

A sample run:

T​e​r​m​i​n​a​l​ ​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​T​e​r​m​i​n​a​l​ ​2​
$​ ​.​/​i​n​t​e​r​r​u​p​t​ ​&​
[​1​]​ ​7​6​2​5​
s​t​a​r​t​i​n​g​ ​l​o​t​s​ ​o​f​ ​w​o​r​k​
i​ ​i​s​ ​0​
i​ ​i​s​ ​1​
i​ ​i​s​ ​2​
i​ ​i​s​ ​3​
i​ ​i​s​ ​4​
i​ ​i​s​ ​5​
i​ ​i​s​ ​6​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​i​l​l​ ​-​U​S​R​1​ ​7​6​2​5​
s​t​a​r​t​i​n​g​ ​l​o​t​s​ ​o​f​ ​w​o​r​k​
i​ ​i​s​ ​0​
i​ ​i​s​ ​1​
i​ ​i​s​ ​2​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​i​l​l​ ​-​U​S​R​1​ ​7​6​2​5​
s​t​a​r​t​i​n​g​ ​l​o​t​s​ ​o​f​ ​w​o​r​k​
i​ ​i​s​ ​0​
i​ ​i​s​ ​1​
i​ ​i​s​ ​2​
i​ ​i​s​ ​3​
i​ ​i​s​ ​4​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​i​l​l​ ​7​6​2​5​

[​1​]​ ​ ​ ​ ​T​e​r​m​i​n​a​t​e​d​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​.​/​i​n​t​e​r​r​u​p​t​

The last kill sends a SIGTERM, which terminates the process if not handled.

Race conditions

The other bugaboo with signals is race conditions (a subject revisited, along with the entire signal model, in Chapter 20: Multithreading). A race condition happens when two different streams of execution hit an ambiguous area of code and the code’s behavior changes depending on the order in which the two streams execute.

A piece of code as simple as

i​ ​=​ ​5​;​
i​ ​=​ ​i​ ​+​ ​7​;​

can be subject to race conditions. Depending on order of operations, you can get different results:

t​h​r​e​a​d​ ​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​h​r​e​a​d​ ​2​
 ​i​ ​=​ ​5​
 ​c​o​p​y​ ​i​ ​t​o​ ​r​e​g​i​s​t​e​r​
 ​a​d​d​ ​7​ ​t​o​ ​5​
 ​s​t​o​r​e​ ​1​2​ ​i​n​t​o​ ​i​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​ ​=​ ​1​2​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​p​y​ ​i​ ​t​o​ ​r​e​g​i​s​t​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​d​d​ ​7​ ​t​o​ ​1​2​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​o​r​e​ ​1​9​ ​i​n​t​o​ ​i​

F​i​n​a​l​ ​v​a​l​u​e​:​ ​1​9​

t​h​r​e​a​d​ ​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​h​r​e​a​d​ ​2​
 ​ ​i​ ​=​ ​5​
 ​ ​c​o​p​y​ ​i​ ​t​o​ ​r​e​g​i​s​t​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​ ​=​ ​5​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​p​y​ ​i​ ​t​o​ ​r​e​g​i​s​t​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​d​d​ ​7​ ​t​o​ ​5​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​o​r​e​ ​1​2​ ​i​n​t​o​ ​i​
 ​ ​a​d​d​ ​7​ ​t​o​ ​5​
 ​ ​s​t​o​r​e​ ​1​2​ ​i​n​t​o​ ​i​

F​i​n​a​l​ ​v​a​l​u​e​:​ ​1​2​

The interrupt.m program has a couple of race conditions in it. The first happens after you register the signal handler but before the call to sigsetjmp() on the sigjmp_buf. If a SIGUSR1 signal happens any time after the sigaction() and before the sigsetjmp(), you will crash by trying to siglongjmp() with an invalid jump buffer.

Likewise, if a SIGUSR1 signal happens between the time that the code returns from sigsetjmp() and you call sigsetjmp() again, you will crash from using an out-of-date sigjmp_buf. You can use sigprocmask() to block the signal during these vulnerable times.

Race conditions are not limited to interleaving C statements. Delivery of a signal could leave a variable half-set, in some otherwise impossible and entirely inconsistent state. This can happen when a type requires more than one machine instruction to update, such as might be the case with long long, double, or a struct.

Aside from calling explicitly async-safe functions, the only guaranteed-safe ways a signal handler can interact with global, static state are to write to a variable of type volatile sig_atomic_t and to read from errno. The volatile sig_atomic_t variable is typically a “signal happened” flag read by a function executing normally. You might find you can get away with ignoring these severe restrictions, but if you ignore them and things go wrong, that could very well be why.

Signal handling is a dangerous and ugly task. The kqueue interface (discussed in Chapter 16: kqueue and FSEvents) provides an easy-to-use and safe way of handling signals. Grand Central Dispatch Chapter 22: Grand Central Dispatch also has a signal-handling mechanism.

Exception Handling in Cocoa

Cocoa provides two exception-handling mechanisms similar to what C++ and Java offer. The older, “classic” way to handle exceptions is to wrap a body of code with a set of macros. The newer way, the “native” way, was introduced in Mac OS X 10.3 and adds language support for exceptions.

Classic exception handling

To handle exceptions, wrap your code in an NS_DURING clause. Any exceptional conditions will raise an exception, which can then be caught by an NS_HANDLER clause.

The way you work it is:

NS_DURING
    ... code that might throw an exception
NS_HANDLER
    ... code to examine the exception and possibly handle it
NS_ENDHANDLER

If any code between NS_DURING and NS_HANDLER raises an exception, execution immediately resumes with the first instruction after NS_HANDLER, as shown in Figure 5.2

Figure 5.2  Exception flow of control

Handlers can be arbitrarily nested, so you could call a function that is in an NS_DURING handler, and it can set up its own NS_DURING handler. When someone finally raises an exception, flow of control will jump to the closest NS_HANDLER.

The macro for NS_HANDLER declares a local variable called localException that you can query for details about the exception – specifically, its name and the reason it happened. You can also use localException to “rethrow” the exception using [localException raise].

Example 5.6 is a little command-line tool to show exception handling in action. -[NSString characterAtIndex:] will raise an exception if you try to get a character that is beyond the length of the string.

Example 5.6. exception.m

/​/​ ​e​x​c​e​p​t​i​o​n​.​m​ ​-​-​ ​S​h​o​w​ ​s​i​m​p​l​e​ ​e​x​c​e​p​t​i​o​n​ ​h​a​n​d​l​i​n​g​ ​i​n​ ​C​o​c​o​a​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​e​x​c​e​p​t​i​o​n​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​e​x​c​e​p​t​i​o​n​.​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​s​t​r​i​n​g​ ​=​ ​@​"​h​e​l​l​o​"​;​

 ​ ​ ​ ​N​S​_​D​U​R​I​N​G​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​h​a​r​a​c​t​e​r​ ​a​t​ ​i​n​d​e​x​ ​0​:​ ​%​c​"​,​ ​[​s​t​r​i​n​g​ ​c​h​a​r​a​c​t​e​r​A​t​I​n​d​e​x​:​ ​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​h​a​r​a​c​t​e​r​ ​a​t​ ​i​n​d​e​x​ ​1​:​ ​%​c​"​,​ ​[​s​t​r​i​n​g​ ​c​h​a​r​a​c​t​e​r​A​t​I​n​d​e​x​:​ ​1​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​h​a​r​a​c​t​e​r​ ​a​t​ ​i​n​d​e​x​ ​2​0​0​0​:​ ​%​c​"​,​ ​[​s​t​r​i​n​g​ ​c​h​a​r​a​c​t​e​r​A​t​I​n​d​e​x​:​ ​2​0​0​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​h​a​r​a​c​t​e​r​ ​a​t​ ​i​n​d​e​x​ ​2​:​ ​%​c​"​,​ ​[​s​t​r​i​n​g​ ​c​h​a​r​a​c​t​e​r​A​t​I​n​d​e​x​:​ ​2​]​)​;​

 ​ ​ ​ ​N​S​_​H​A​N​D​L​E​R​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​i​n​s​i​d​e​ ​o​f​ ​e​x​c​e​p​t​i​o​n​ ​h​a​n​d​l​e​r​.​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​n​a​m​e​ ​i​s​ ​:​ ​%​@​"​,​ ​[​l​o​c​a​l​E​x​c​e​p​t​i​o​n​ ​n​a​m​e​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​r​e​a​s​o​n​ ​i​s​ ​:​ ​%​@​"​,​ ​[​l​o​c​a​l​E​x​c​e​p​t​i​o​n​ ​r​e​a​s​o​n​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​u​s​e​r​I​n​f​o​ ​d​i​c​t​:​ ​%​@​"​,​ ​[​l​o​c​a​l​E​x​c​e​p​t​i​o​n​ ​u​s​e​r​I​n​f​o​]​)​;​

 ​ ​ ​ ​N​S​_​E​N​D​H​A​N​D​L​E​R​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​/​/​ ​m​a​i​n​

A sample run:

$​ ​.​/​e​x​c​e​p​t​i​o​n​
2​0​1​1​-​0​5​-​2​0​ ​1​3​:​1​8​:​3​7​.​4​4​9​ ​e​x​c​e​p​t​i​o​n​[​8​9​6​2​6​:​9​0​3​]​ ​c​h​a​r​a​c​t​e​r​ ​a​t​ ​i​n​d​e​x​ ​0​:​ ​h​
2​0​1​1​-​0​5​-​2​0​ ​1​3​:​1​8​:​3​7​.​4​5​3​ ​e​x​c​e​p​t​i​o​n​[​8​9​6​2​6​:​9​0​3​]​ ​c​h​a​r​a​c​t​e​r​ ​a​t​ ​i​n​d​e​x​ ​1​:​ ​e​
2​0​1​1​-​0​5​-​2​0​ ​1​3​:​1​8​:​3​7​.​4​5​9​ ​e​x​c​e​p​t​i​o​n​[​8​9​6​2​6​:​9​0​3​]​ ​i​n​s​i​d​e​ ​o​f​ ​e​x​c​e​p​t​i​o​n​ ​h​a​n​d​l​e​r​.​
2​0​1​1​-​0​5​-​2​0​ ​1​3​:​1​8​:​3​7​.​4​6​0​ ​e​x​c​e​p​t​i​o​n​[​8​9​6​2​6​:​9​0​3​]​ ​n​a​m​e​ ​i​s​ ​:​ ​N​S​R​a​n​g​e​E​x​c​e​p​t​i​o​n​
2​0​1​1​-​0​5​-​2​0​ ​1​3​:​1​8​:​3​7​.​4​6​0​ ​e​x​c​e​p​t​i​o​n​[​8​9​6​2​6​:​9​0​3​]​ ​r​e​a​s​o​n​ ​i​s​ ​:​
 ​ ​ ​ ​ ​ ​ ​ ​*​*​*​ ​-​[​N​S​C​F​S​t​r​i​n​g​ ​c​h​a​r​a​c​t​e​r​A​t​I​n​d​e​x​:​]​:​ ​R​a​n​g​e​ ​o​r​ ​i​n​d​e​x​ ​o​u​t​ ​o​f​ ​b​o​u​n​d​s​
2​0​1​1​-​0​5​-​2​0​ ​1​3​:​1​8​:​3​7​.​4​6​0​ ​e​x​c​e​p​t​i​o​n​[​8​9​6​2​6​:​9​0​3​]​ ​u​s​e​r​I​n​f​o​ ​d​i​c​t​:​ ​(​n​u​l​l​)​

The first two characterAtIndex: method calls succeed, and the third raised an exception, terminating the code in the NS_DURING section of code.

Specific Cocoa methods document whether they raise exceptions. Unfortunately there is not a centralized list of all methods that can raise exceptions.

Cocoa classic exception handling is based on setjmp()/longjmp(), so you must use volatile variables if they might be accessed after the longjmp() to your NS_HANDLER. There is no automatic cleanup of allocated objects when exceptions happen (like with stack objects in C++). Also, there are some restrictions in what you can do during the NS_HANDLER portion. Specifically, you should not goto or return out of an exception-handling domain (anywhere between NS_DURING and NS_HANDLER); if you do, the exception handler stack will be left in a bad state. If you want to return from an exception-handling domain, you must use the macros NS_VOIDRETURN and NS_VALUERETURN(value, value_type). Further, setjmp() and longjmp() should not be used if it crosses an NS_DURING statement. (In general, if you are using Cocoa exception handling, you will not need to use setjmp() and longjmp(), but be careful of library code.)

Cocoa exception handling is a heavy-weight operation, so do not use it for normal flow of control. It is better test a string’s length when processing characters rather than falling off the string’s end and depending on an exception to terminate your processing.

Cocoa provides the NSException object. You can allocate your own instances of this object and use them to raise your own exceptions. You can supply your own name and reason strings or use built-in ones.

Example 5.7 is a command-line tool that raises a custom exception:

Example 5.7. raise-classic.m

/​/​ ​r​a​i​s​e​-​c​l​a​s​s​i​c​.​m​ ​-​-​ ​r​a​i​s​e​ ​a​n​ ​e​x​c​e​p​t​i​o​n​ ​i​n​ ​o​l​d​-​s​c​h​o​o​l​ ​C​o​c​o​a​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​o​ ​r​a​i​s​e​-​c​l​a​s​s​i​c​ ​r​a​i​s​e​-​c​l​a​s​s​i​c​.​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​

v​o​i​d​ ​d​o​S​o​m​e​t​h​i​n​g​E​l​s​e​ ​(​)​ ​{​
 ​ ​ ​ ​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​u​s​e​r​I​n​f​o​ ​=​ ​[​N​S​D​i​c​t​i​o​n​a​r​y​ ​d​i​c​t​i​o​n​a​r​y​W​i​t​h​O​b​j​e​c​t​s​A​n​d​K​e​y​s​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​"​h​e​l​l​o​"​,​ ​@​"​t​h​i​n​g​1​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​"​b​o​r​k​"​,​ ​@​"​t​h​i​n​g​2​"​,​ ​n​i​l​]​;​
 ​ ​ ​ ​N​S​E​x​c​e​p​t​i​o​n​ ​*​e​x​c​e​p​t​i​o​n​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​N​S​E​x​c​e​p​t​i​o​n​ ​e​x​c​e​p​t​i​o​n​W​i​t​h​N​a​m​e​:​ ​@​"​G​r​o​o​v​y​E​x​c​e​p​t​i​o​n​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​a​s​o​n​:​ ​@​"​d​o​S​o​m​e​t​h​i​n​g​E​l​s​e​ ​r​a​i​s​e​d​ ​a​ ​G​r​o​o​v​y​E​x​c​e​p​t​i​o​n​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​I​n​f​o​:​ ​u​s​e​r​I​n​f​o​]​;​
 ​ ​ ​ ​[​e​x​c​e​p​t​i​o​n​ ​r​a​i​s​e​]​;​

 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​a​f​t​e​r​ ​t​h​e​ ​r​a​i​s​e​.​ ​ ​T​h​i​s​ ​w​o​n​'​t​ ​b​e​ ​p​r​i​n​t​e​d​.​"​)​;​

}​ ​/​/​ ​d​o​S​o​m​e​t​h​i​n​g​E​l​s​e​

v​o​i​d​ ​d​o​S​o​m​e​t​h​i​n​g​ ​(​)​ ​{​
 ​ ​ ​ ​d​o​S​o​m​e​t​h​i​n​g​E​l​s​e​ ​(​)​;​
}​ ​/​/​ ​d​o​S​o​m​e​t​h​i​n​g​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​N​S​_​D​U​R​I​N​G​
 ​ ​ ​ ​ ​ ​ ​ ​d​o​S​o​m​e​t​h​i​n​g​ ​(​)​;​

 ​ ​ ​ ​N​S​_​H​A​N​D​L​E​R​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​i​n​s​i​d​e​ ​o​f​ ​e​x​c​e​p​t​i​o​n​ ​h​a​n​d​l​e​r​.​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​n​a​m​e​ ​i​s​ ​:​ ​%​@​"​,​ ​[​l​o​c​a​l​E​x​c​e​p​t​i​o​n​ ​n​a​m​e​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​r​e​a​s​o​n​ ​i​s​ ​:​ ​%​@​"​,​ ​[​l​o​c​a​l​E​x​c​e​p​t​i​o​n​ ​r​e​a​s​o​n​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​u​s​e​r​I​n​f​o​ ​d​i​c​t​:​ ​%​@​"​,​ ​[​l​o​c​a​l​E​x​c​e​p​t​i​o​n​ ​u​s​e​r​I​n​f​o​]​)​;​

 ​ ​ ​ ​N​S​_​E​N​D​H​A​N​D​L​E​R​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​/​/​ ​m​a​i​n​

A sample run:

$​ ​.​/​r​a​i​s​e​-​c​l​a​s​s​i​c​
.​.​.​ ​i​n​s​i​d​e​ ​o​f​ ​e​x​c​e​p​t​i​o​n​ ​h​a​n​d​l​e​r​.​
.​.​.​ ​n​a​m​e​ ​i​s​:​ ​G​r​o​o​v​y​E​x​c​e​p​t​i​o​n​
.​.​.​ ​r​e​a​s​o​n​ ​i​s​:​ ​d​o​S​o​m​e​t​h​i​n​g​E​l​s​e​ ​r​a​i​s​e​d​ ​a​ ​G​r​o​o​v​y​E​x​c​e​p​t​i​o​n​
.​.​.​ ​u​s​e​r​I​n​f​o​ ​d​i​c​t​:​ ​ ​{​t​h​i​n​g​1​ ​=​ ​h​e​l​l​o​;​ ​t​h​i​n​g​2​ ​=​ ​b​o​r​k​;​ ​}​

The Cocoa classic exception-handling mechanism is fundamentally string-based. There is no hierarchy of exception classes as there is in Java and C++. When code throws a built-in exception (like NSGenericException), the exception is actually just an NSString that gets put into an NSException object.

You can use gdb to halt execution when exceptions are thrown, just put a breakpoint on -[NSException raise] or objc_exception_throw(). The latter will be triggered with classic and native exceptions, and the former only with classic exceptions.

Native exception handling

Starting with Mac OS X 10.3, native exception handling was added to the Objective-C language. You need to turn it on before you can use it. You can provide gcc the -fobjc-exceptions flag when compiling on the command line or with makefiles. You can turn on native exception handling in Xcode’s build configuration. Enabling native exceptions also enables some native thread-safety tools that will be discussed in Chapter 20: Multithreading.

Example 5.8 shows the syntax for native Objective-C exceptions.

Example 5.8. Objective-C Exception Syntax

@​t​r​y​ ​{​
 ​ ​ ​ ​.​.​.​
 ​ ​ ​ ​@​t​h​r​o​w​ ​e​x​p​r​;​
}​
@​c​a​t​c​h​ ​(​S​o​m​e​C​l​a​s​s​ ​*​e​x​c​e​p​t​i​o​n​)​ ​{​
 ​ ​ ​ ​.​.​.​
 ​ ​ ​ ​@​t​h​r​o​w​ ​e​x​p​r​;​
 ​ ​ ​ ​.​.​.​
 ​ ​ ​ ​@​t​h​r​o​w​
}​
@​c​a​t​c​h​ ​(​A​n​o​t​h​e​r​C​l​a​s​s​ ​*​e​x​c​e​p​t​i​o​n​)​ ​{​
 ​ ​ ​ ​.​.​.​
}​
@​c​a​t​c​h​ ​(​i​d​ ​a​l​l​O​t​h​e​r​s​)​ ​{​
 ​ ​ ​ ​.​.​.​
}​
@​f​i​n​a​l​l​y​ ​{​
 ​ ​ ​ ​.​.​.​
 ​ ​ ​ ​@​t​h​r​o​w​ ​e​x​p​r​;​
}​

You wrap the code that might throw an exception inside of an @try block. If any exceptions are thrown from inside of that block, one of the @catch handlers might be entered. Unlike the classic exceptions, the native exceptions are object-based, so you can catch different classes of exceptions. If you @catch a particular class, any objects that are subclasses of that class will be caught by that block, too. Use id to designate the “catch-all” handler. Any exception that does not match any of the other classes will be caught here. Classes are matched in the order they are listed, so be sure to have any id @catch blocks at the end. Finally, the @finally section is run whether or not an exception was thrown or the @try block completed successfully.

To throw an exception, you @throw an object. Only Objective-C objects can be thrown and caught, so you cannot throw C++ objects or primitive C types. You can @throw an exception at any time. The code inside of an @catch section can throw a brand new exception if it wants, or it can use @throw; (without an object) to rethrow the current exception.

The native Objective-C exception mechanism is binary compatible with the NS_HANDLER idiom, but you can only use the new syntax on Mac OS X 10.3 or later because of support added to the Objective-C runtime.

If you want the debugger to break when an exception is @thrown, put a breakpoint on objc_exception_throw(). You can also have Xcode catch exceptions for you. In Xcode 3, use the Stop on Objective-C Exceptions menu command. In Xcode 4, add an exception breakpoint and choose whether you want to break on Objective-C or C++ exceptions and whether to break on throw or on catch.

Example 5.9 is the raise-classic program ported to use the native exception syntax. It throws an NSException object because it conveniently wraps some useful data, but you could throw any kind of object.

Example 5.9. raise-native.m

/​/​ ​r​a​i​s​e​-​n​a​t​i​v​e​.​m​ ​-​-​ ​R​a​i​s​e​ ​a​n​ ​e​x​c​e​p​t​i​o​n​ ​u​s​i​n​g​ ​n​a​t​i​v​e​ ​O​b​j​e​c​t​i​v​e​-​C​ ​m​e​c​h​a​n​i​s​m​s​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​f​o​b​j​c​-​e​x​c​e​p​t​i​o​n​s​ ​-​o​ ​r​a​i​s​e​-​n​a​t​i​v​e​
/​/​ ​ ​ ​ ​r​a​i​s​e​-​n​a​t​i​v​e​.​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​

v​o​i​d​ ​d​o​S​o​m​e​t​h​i​n​g​E​l​s​e​ ​(​)​ ​{​
 ​ ​ ​ ​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​u​s​e​r​I​n​f​o​ ​=​ ​[​N​S​D​i​c​t​i​o​n​a​r​y​ ​d​i​c​t​i​o​n​a​r​y​W​i​t​h​O​b​j​e​c​t​s​A​n​d​K​e​y​s​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​"​h​e​l​l​o​"​,​ ​@​"​t​h​i​n​g​1​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​"​b​o​r​k​"​,​ ​@​"​t​h​i​n​g​2​"​,​ ​n​i​l​]​;​
 ​ ​ ​ ​N​S​E​x​c​e​p​t​i​o​n​ ​*​e​x​c​e​p​t​i​o​n​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​N​S​E​x​c​e​p​t​i​o​n​ ​e​x​c​e​p​t​i​o​n​W​i​t​h​N​a​m​e​:​ ​@​"​G​r​o​o​v​y​E​x​c​e​p​t​i​o​n​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​a​s​o​n​:​ ​@​"​d​o​S​o​m​e​t​h​i​n​g​E​l​s​e​ ​r​a​i​s​e​d​ ​a​ ​G​r​o​o​v​y​E​x​c​e​p​t​i​o​n​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​I​n​f​o​:​ ​u​s​e​r​I​n​f​o​]​;​
 ​ ​ ​ ​@​t​h​r​o​w​ ​e​x​c​e​p​t​i​o​n​;​

 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​a​f​t​e​r​ ​t​h​e​ ​t​h​r​o​w​.​ ​ ​T​h​i​s​ ​w​o​n​'​t​ ​b​e​ ​p​r​i​n​t​e​d​.​"​)​;​

}​ ​/​/​ ​d​o​S​o​m​e​t​h​i​n​g​E​l​s​e​

v​o​i​d​ ​d​o​S​o​m​e​t​h​i​n​g​ ​(​)​ ​{​
 ​ ​ ​ ​d​o​S​o​m​e​t​h​i​n​g​E​l​s​e​ ​(​)​;​
}​ ​/​/​ ​d​o​S​o​m​e​t​h​i​n​g​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​@​t​r​y​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​d​o​S​o​m​e​t​h​i​n​g​ ​(​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​@​c​a​t​c​h​ ​(​N​S​E​x​c​e​p​t​i​o​n​ ​*​e​x​c​e​p​t​i​o​n​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​i​n​s​i​d​e​ ​o​f​ ​e​x​c​e​p​t​i​o​n​ ​h​a​n​d​l​e​r​.​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​n​a​m​e​ ​i​s​ ​:​ ​%​@​"​,​ ​[​e​x​c​e​p​t​i​o​n​ ​n​a​m​e​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​r​e​a​s​o​n​ ​i​s​ ​:​ ​%​@​"​,​ ​[​e​x​c​e​p​t​i​o​n​ ​r​e​a​s​o​n​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​u​s​e​r​I​n​f​o​ ​d​i​c​t​:​ ​%​@​"​,​ ​[​e​x​c​e​p​t​i​o​n​ ​u​s​e​r​I​n​f​o​]​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​@​f​i​n​a​l​l​y​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

And the results are the same as raise-classic:

$​ ​.​/​r​a​i​s​e​-​n​a​t​i​v​e​
.​.​.​ ​[​2​1​5​4​0​]​ ​i​n​s​i​d​e​ ​o​f​ ​e​x​c​e​p​t​i​o​n​ ​h​a​n​d​l​e​r​.​
.​.​.​ ​[​2​1​5​4​0​]​ ​n​a​m​e​ ​i​s​:​ ​G​r​o​o​v​y​E​x​c​e​p​t​i​o​n​
.​.​.​ ​[​2​1​5​4​0​]​ ​r​e​a​s​o​n​ ​i​s​:​ ​d​o​S​o​m​e​t​h​i​n​g​E​l​s​e​ ​r​a​i​s​e​d​ ​a​ ​G​r​o​o​v​y​E​x​c​e​p​t​i​o​n​
.​.​.​ ​[​2​1​5​4​0​]​ ​u​s​e​r​I​n​f​o​ ​d​i​c​t​:​ ​{​t​h​i​n​g​1​ ​=​ ​h​e​l​l​o​;​ ​t​h​i​n​g​2​ ​=​ ​b​o​r​k​;​ ​}​

In both native and classic exception handling, you must be careful not to deallocate the autoreleased exception object by draining a local autorelease pool prior to rethrowing the exception. Be particularly careful of draining pools in @finally blocks, because these execute before the next exception handler catches the rethrown exception.

Subclassing NSApplication to catch exceptions

In some applications (Xcode, for example), when an exception falls through the stack all the way to the run loop, the user is shown the exception in a panel. (Often, the panel just says something like “Something has gone wrong. You may want to save what you are working on and restart this application.”) They accomplish this by using a custom subclass of NSApplication.

When an exception falls through the stack all the way to the run loop, the instance of NSApplication gets sent the following message:

- (void)reportException: (NSException *) theException

This method simply logs the exception using NSLog(), and the run loop begins again. If you would like to alter this behavior, you must subclass NSApplication and override -reportException:.

If you do this, make sure that you also alter the Info.plist for your application so that it uses your subclass instead of NSApplication:

<​k​e​y​>​N​S​P​r​i​n​c​i​p​a​l​C​l​a​s​s​<​/​k​e​y​>​
<​s​t​r​i​n​g​>​G​r​o​o​v​y​E​x​c​e​p​t​i​o​n​R​e​p​o​r​t​i​n​g​A​p​p​l​i​c​a​t​i​o​n​<​/​s​t​r​i​n​g​>​

Note that this only works if your Objective-C code is in a Cocoa application. If you have written a Command Line Tool, you will call NSSetUncaughtExceptionHandler() and supply it with a pointer to a function with this signature:

volatile void Handler (NSException *e);

The Exception Handling framework’s NSExceptionHandler class provides some default uncaught exception handling that can be helpful during debugging; you should check it out before rolling your own.

You might wonder at the volatile void return type. This is a GNU extension to standard C that indicates that the function does not return, similar to the noreturn attribute you could use to qualify wrappers around exit() and abort().

64-bit Objective-C runtime

The new runtime introduced with Leopard’s 64-bit support and now used on iOS, changes the way exceptions are implemented in Objective-C. Under the classic runtime, exceptions are setjmp/longjmp based. Recall that setjmp saves processor registers and other bits of state. This means that you pay a price for every @try, while the @throw is a very cheap operation.

The C++ exception model instead makes try operations very cheap, while incurring the computationally expensive portion when an exception is actually thrown. Entering and exiting try blocks is a much more common operation than actually throwing an exception, so this is a design win. The new Objective-C runtime adopts the C++ model and actually unifies the two exception mechanisms. You can now throw and catch C++ and Objective-C exceptions with each other’s handling mechanism.

iOS development has its own considerations. iOS devices use the new runtime, including the unified exception model. In general, all exception work is relatively expensive, so you should measure the performance impact of any heavy exception work you might be doing.

NSError

Apple introduced NSError in Mac OS X 10.3 as a way of returning richer error results from methods. Consider reading the contents of a file into an NSData object:

+ (id) dataWithContentsOfFile: (NSString *) path;

If something goes wrong, you get nil back, and there’s not much you can do to figure out what went wrong. The updated version of this method looks like this:

+ (id) dataWithContentsOfFile: (NSString *) path
                      options: (NSDataReadingOptions) readOptionsMask
                        error: (NSError **) errorPtr;

Notice the NSError that is passed in. The method wants a pointer to a pointer. The method will fill in this pointer if the method failed. The return value of errorPtr is undefined if the method succeeded. Therefore, you should never use the value (nil or non-nil) of an NSError pointer to decide if an error happened.

This is the usual usage pattern:

 ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​;​
 ​ ​ ​ ​N​S​D​a​t​a​ ​*​d​a​t​a​ ​=​ ​[​N​S​D​a​t​a​ ​d​a​t​a​W​i​t​h​C​o​n​t​e​n​t​s​O​f​F​i​l​e​:​ ​p​a​t​h​n​a​m​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​p​t​i​o​n​s​:​ ​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​:​ ​&​e​r​r​o​r​]​;​
 ​ ​ ​ ​i​f​ ​(​d​a​t​a​ ​=​=​ ​n​i​l​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​U​s​e​ ​e​r​r​o​r​ ​t​o​ ​f​i​g​u​r​e​ ​o​u​t​ ​w​h​a​t​ ​w​e​n​t​ ​w​r​o​n​g​.​
 ​ ​ ​ ​}​

Logging

In our development careers at one time or another we have all done “caveman debugging,” putting in lots of print statements to see program flow and to see what values our variables have. Using stuff in Chapter 11: Files, Part 1: I/O and Permissions, you can even redirect those print statements to log files.

But there are times when you are logging and it is not debugging related, like server programs keeping an audit trail of connections or printing information that may be of interest to administrators (such as the disk is filling up). Most Unix systems have a daemon running called syslogd, the system logging daemon. System administrators can configure syslogd to log to a file or to send the log information from many machines to a central location (very useful if you have a lot of machines to keep an eye on).

syslog()

One programmatic interface to syslogd, the syslog() function, is:

void syslog (int priority, const char *message, ...);

The message is a printf()-style string. You can use any printf() token in there you want. There is also an added format string, “%m”, to insert the current error message from strerror(). syslog() also adds a trailing newline if one is not already specified in the message string.

The priority argument controls whether the logging will be seen or not. Here are the priority levels from highest to lowest:

LOG_EMERG | A panic condition. This is normally broadcast to all users.
LOG_ALERT | A condition that should be corrected immediately, such as a corrupted system database.
LOG_CRIT | Critical conditions, e.g., hard device errors.
LOG_ERR | Errors.
LOG_WARNING | Warning messages.
LOG_NOTICE | Conditions that are not error conditions, but should possibly be handled specially.
LOG_INFO | Informational messages.
LOG_DEBUG | Messages that contain information normally of use only when debugging a program.

The configuration file for syslogd, /etc/syslogd.conf, contains the controls for setting the threshold where logging will occur. By default, LOG_DEBUG messages are not shown, but everything else is.

You can control some of the syslog behavior of the logging output by using openlog():

void openlog (const char *ident, int logopt, int facility);

ident is the name to use for the program in the log. By default, the executable name is used. logopt is any one of these flags bitwise-OR’d together:

LOG_CONS | If syslog() cannot pass the message to syslogd, it will attempt to write the message to the console (/dev/console).
LOG_NDELAY | Open the connection to syslogd(8) immediately. Normally the open is delayed until the first message is logged. This is useful for programs that need to manage the order in which file descriptors are allocated.
LOG_PERROR | Write the message to standard error output as well as to the system log.
LOG_PID | Log the process ID with each message; this is useful for identifying instantiations of daemons.

The facility parameter tells syslogd that the program is a member of a standard system facility, like being a daemon, or that it is part of the security subsystem. For example, if you were writing a daemon that was part of the mail system, you would call openlog() with a facility of LOG_MAIL. These constants are listed in the man page.

Example 5.10 is a sample that logs to syslog, which by default gets written locally to /var/log/system.log. This is the one of the logs that Console.app looks at.

Example 5.10. syslog.m

/​/​ ​s​y​s​l​o​g​.​m​ ​-​-​ ​u​s​e​ ​t​h​e​ ​s​y​s​l​o​g​ ​f​u​n​c​t​i​o​n​s​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​s​y​s​l​o​g​ ​s​y​s​l​o​g​.​m​

#​i​m​p​o​r​t​ ​<​s​y​s​l​o​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​y​s​l​o​g​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​s​y​s​l​o​g​ ​(​L​O​G​_​W​A​R​N​I​N​G​,​ ​"​T​h​i​s​ ​i​s​ ​a​ ​w​a​r​n​i​n​g​ ​m​e​s​s​a​g​e​.​"​)​;​
 ​ ​ ​ ​e​r​r​n​o​ ​=​ ​E​I​N​V​A​L​;​
 ​ ​ ​ ​s​y​s​l​o​g​ ​(​L​O​G​_​E​R​R​,​ ​"​T​h​i​s​ ​i​s​ ​a​n​ ​e​r​r​o​r​,​ ​%​m​"​)​;​
 ​ ​ ​ ​s​y​s​l​o​g​ ​(​L​O​G​_​E​M​E​R​G​,​ ​"​W​H​O​O​P​!​!​ ​W​H​O​O​P​!​!​"​)​;​

 ​ ​ ​ ​o​p​e​n​l​o​g​ ​(​"​B​N​R​s​y​s​l​o​g​T​e​s​t​"​,​ ​L​O​G​_​P​I​D​ ​|​ ​L​O​G​_​N​D​E​L​A​Y​ ​|​ ​L​O​G​_​C​O​N​S​,​ ​L​O​G​_​D​A​E​M​O​N​)​;​

 ​ ​ ​ ​s​y​s​l​o​g​ ​(​L​O​G​_​D​E​B​U​G​,​ ​"​D​e​b​u​g​ ​m​e​s​s​a​g​e​"​)​;​
 ​ ​ ​ ​s​y​s​l​o​g​ ​(​L​O​G​_​N​O​T​I​C​E​,​ ​"​N​o​t​i​c​e​ ​m​e​s​s​a​g​e​"​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​/​/​ ​m​a​i​n​

A run of this produces in the log:

$​ ​t​a​i​l​ ​-​f​ ​/​p​r​i​v​a​t​e​/​v​a​r​/​l​o​g​/​s​y​s​t​e​m​.​l​o​g​
(​a​n​d​ ​r​u​n​ ​.​/​s​y​s​l​o​g​ ​i​n​ ​a​n​o​t​h​e​r​ ​t​e​r​m​i​n​a​l​)​

(​b​e​e​p​)​ ​B​r​o​a​d​c​a​s​t​ ​M​e​s​s​a​g​e​ ​f​r​o​m​ ​r​o​o​t​@​p​h​e​a​s​a​n​t​b​o​o​k​.​l​o​c​a​l​
 ​ ​ ​ ​ ​ ​ ​ ​(​n​o​ ​t​t​y​)​ ​a​t​ ​1​3​:​3​9​ ​E​D​T​.​.​.​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​
W​H​O​O​P​!​!​ ​W​H​O​O​P​!​!​

M​a​y​ ​2​0​ ​1​3​:​3​9​:​1​8​ ​p​h​e​a​s​a​n​t​b​o​o​k​ ​.​/​s​y​s​l​o​g​[​8​9​6​9​7​]​:​ ​T​h​i​s​ ​i​s​ ​a​ ​w​a​r​n​i​n​g​ ​m​e​s​s​a​g​e​.​
M​a​y​ ​2​0​ ​1​3​:​3​9​:​1​8​ ​p​h​e​a​s​a​n​t​b​o​o​k​ ​.​/​s​y​s​l​o​g​[​8​9​6​9​7​]​:​ ​T​h​i​s​ ​i​s​ ​a​n​ ​e​r​r​o​r​,​ ​I​n​v​a​l​i​d​ ​a​r​g​u​m​e​n​t​
M​a​y​ ​2​0​ ​1​3​:​3​9​:​1​8​ ​p​h​e​a​s​a​n​t​b​o​o​k​ ​.​/​s​y​s​l​o​g​[​8​9​6​9​7​]​:​ ​W​H​O​O​P​!​!​ ​W​H​O​O​P​!​!​
M​a​y​ ​2​0​ ​1​3​:​3​9​:​1​8​ ​p​h​e​a​s​a​n​t​b​o​o​k​ ​B​N​R​s​y​s​l​o​g​T​e​s​t​[​8​9​6​9​7​]​:​ ​N​o​t​i​c​e​ ​m​e​s​s​a​g​e​

Here you can see the LOG_EMERG getting broadcast to all the open terminals and then the various syslog messages. You can call openlog() at anytime. You can see how the LOG_PID option and “BNRSyslogTest” identifier appear.

ASL

The syslog() function is very portable and works on all modern Unix-flavored systems. Apple has introduced its own API that adds some value on top of syslogd. ASL, the Apple System Log facility, provides an alternate API for sending messages to syslogd, as well as a capability for querying syslogd for information about previously logged items.

ASL messages

ASL is based on messages, and these messages are an opaque type called aslmsg. A message is a container for key-value pairs with a number of pre-defined keys that have meaning for launchd. You create a new message using asl_new():

aslmsg asl_new (uint32_t type);

asl_new() creates a new aslmsg and returns it to you. The type parameter can either be ASL_TYPE_MSG to create a new message or ASL_TYPE_QUERY to create a new search query.

When you are done with an aslmsg, release its resources using asl_free(), which takes an aslmsg as its only argument.

Use asl_set() to add keys and values to a message:

int asl_set (aslmsg message, const char *key, const char *value);

message is the aslmsg you are filling out. key and value are null-terminated strings (ASCII or UTF-8). The function returns zero on success, non-zero for failure. There are a number of pre-defined keys that have defaults set, and you are also allowed to add your own keys. The predefined keys are: ASL_KEY_TIME, ASL_KEY_HOST, ASL_KEY_SENDER (defaults to the process name), ASL_KEY_PID, ASL_KEY_UID, ASL_KEY_GID, ASL_KEY_LEVEL, and ASL_KEY_MSG (the text to actually log).

You can get a value out of a message for a specific key with asl_get() and iterate through a message’s keys with asl_key():

const char *asl_get (aslmsg message, const char *key);

Given a message and a key, this returns the value or NULL if there is no value under that key.

const char *asl_key (aslmsg message, uint32_t index);

Given a message, this returns the key at the position indicated by index. The function returns NULL if index falls off the end of the list of keys. You can spin through the keys by starting at 0 and incrementing an index. When the function returns NULL, you have reached the end. You can pass the returned key to asl_get() to retrieve the value.

Once you have constructed a message, you can send it off to syslogd using asl_send():

int asl_send (aslclient client, aslmsg message);

The second argument, message, is the message that has already been constructed. The first argument, client, is an aslclient. If you pass NULL, a default client will be used. Access to a client is single-threaded, so if you want multiple threads to be able to log at the exact same time, you must create additional clients using asl_open(). asl_open() is not described further here; see the asl manpage for more information.

If nothing appears in the log, you may need to set the message level, such as

a​s​l​_​s​e​t​ ​(​m​e​s​s​a​g​e​,​ ​A​S​L​_​K​E​Y​_​L​E​V​E​L​,​ ​"​5​"​)​;​

The argument takes a string, not an ASL_LEVEL_* constant.

ASL supplies two convenience functions, asl_log() and asl_vlog(), so that you don’t have to create a new aslmsg every time you want to log something:

int asl_log (aslclient client, aslmsg message, int level,
             const char *format, ...);

int asl_vlog (aslclient client, aslmsg message, int level,
              const char *format, va_list args);

You can pass NULL for the first argument, client. You only need additional clients in multi-threaded applications. The message argument is a template message that has some key-value pairs already added to it. The template will be merged with default values for the message. You can pass NULL if you don’t have a template message you want to use. asl_log() handles its format string the same way as syslog(), so it includes support for the %m “current error string” format specifier. If you have a va_list handy, you can pass that to asl_vlog().

ASL queries

Using ASL messages seems like a lot of work when you can just use syslog(). But unlike the syslog API, ASL provides a way to query syslogd for information about previously logged entries, turning syslogd into a mini-database of sorts. To query the database, you make a new message of type ASL_TYPE_QUERY and then use the asl_set_query() function to add predicates. You can set multiple predicates, which will be joined using logical ANDs.

int asl_set_query (alsmsg query, const char *key, const char *value,
                   uint32_t operation);

The query parameter is an aslmsg that was created with the ASL_TYPE_QUERY type. key is the key you want to search for, value is the value you want to search for, and operation is the way to search, along with some optional flags.

Here are the different query operations:

ASL_QUERY_OP_EQUAL

Compare values for equality. If the value for key in a logged message is equal to the value parameter, that message will be returned from the query.

ASL_QUERY_OP_GREATER

If the value for key in a logged message is strictly greater than the value parameter, that message will be returned from the query.

ASL_QUERY_OP_GREATER_EQUAL

If the value for key in a logged message is greater than or equal to the value parameter, that message will be returned from the query.

ASL_QUERY_OP_LESS

If the value for key in a logged message is strictly less than the value parameter, that message will be returned from the query.

ASL_QUERY_OP_LESS_EQUAL

If the value for key in a logged message is less than or equal to the value parameter, that message will be returned from the query.

ASL_QUERY_OP_NOT_EQUAL

If the value for key in a logged message is not equal to the value parameter, that message will be returned from the query.

ASL_QUERY_OP_REGEX

If the value for key in a logged message matches the regular expression specified by value parameter, that message will be returned from the query. Regular expression searches use the regex function; see the regex(3) manpage for details. Patterns are compiled using the REG_EXTENDED (use “modern” regular expressions) and REG_NOSUB (compile the regular expression for matching that only reports success or failure, not what was matched) options.

ASL_QUERY_OP_TRUE

Always true. Use this to test for the existence of a key.

There are also a number of modifiers you can bitwise-OR into asl_set_query()’s operation parameter:

ASL_QUERY_OP_CASEFOLD

Compare strings in a case-insensitive manner. This is the only modifier that is checked for the ASL_QUERY_OP_REGEX operation.

ASL_QUERY_OP_PREFIX

Match a leading substring.

ASL_QUERY_OP_SUFFIX

Match a trailing substring

ASL_QUERY_OP_SUBSTRING

Match any substring.

ASL_QUERY_OP_NUMERIC

Convert values to integers using atoi(). By adding this option to one of the relative operations (greater, greater_equal, etc.), you can do numeric comparisons instead of string comparisons. This is very handy when you want a specific range of message levels.

A common error is to forget to include a query operation like ASL_QUERY_OP_EQUAL along with ASL_QUERY_OP_SUBSTRING

After you have constructed the query message, you send it off to syslogd using asl_search():

aslresponse asl_search (aslclient client, aslmsg query);

The client can be NULL unless you are calling this from multiple threads. query is the query message you built up with asl_set_query. The return value, an aslresponse, is an opaque iterator type that you feed to aslresponse_next() until it returns NULL. Release the query’s resources with aslresponse_free() when you’re done with it. Both of these aslresponse functions take an aslresponse returned by asl_search()

Example 5.11 will list all messages that syslogd has recently displayed. It uses a query with ASL_QUERY_OP_TRUE to match everything.

Example 5.11. asl-list.m

/​/​ ​a​s​l​-​l​i​s​t​.​m​ ​-​-​ ​s​h​o​w​ ​w​h​a​t​ ​a​s​l​ ​m​e​s​s​a​g​e​s​ ​h​a​v​e​ ​b​e​e​n​ ​l​o​g​g​e​d​ ​s​o​ ​f​a​r​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​a​s​l​-​l​i​s​t​ ​a​s​l​-​l​i​s​t​.​m​

#​i​m​p​o​r​t​ ​<​a​s​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​A​S​L​ ​A​P​I​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​

v​o​i​d​ ​d​u​m​p​A​s​l​M​s​g​ ​(​a​s​l​m​s​g​ ​m​e​s​s​a​g​e​)​ ​{​
 ​ ​ ​ ​/​/​ ​w​a​l​k​ ​t​h​e​ ​k​e​y​s​ ​a​n​d​ ​v​a​l​u​e​s​ ​i​n​ ​e​a​c​h​ ​m​e​s​s​a​g​e​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​k​e​y​,​ ​*​v​a​l​u​e​;​

 ​ ​ ​ ​u​i​n​t​3​2​_​t​ ​i​ ​=​ ​0​;​
 ​ ​ ​ ​w​h​i​l​e​ ​(​(​k​e​y​ ​=​ ​a​s​l​_​k​e​y​ ​(​m​e​s​s​a​g​e​,​ ​i​)​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​v​a​l​u​e​ ​=​ ​a​s​l​_​g​e​t​ ​(​m​e​s​s​a​g​e​,​ ​k​e​y​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​u​:​ ​%​s​ ​-​>​ ​%​s​\​n​"​,​ ​i​,​ ​k​e​y​,​ ​v​a​l​u​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​+​+​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​d​u​m​p​A​s​l​M​s​g​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​/​/​ ​C​o​n​s​t​r​u​c​t​ ​a​ ​q​u​e​r​y​ ​f​o​r​ ​a​l​l​ ​s​e​n​d​e​r​s​ ​u​s​i​n​g​ ​a​ ​r​e​g​u​l​a​r​ ​e​x​p​r​e​s​s​i​o​n​
 ​ ​ ​ ​/​/​ ​t​h​a​t​ ​m​a​t​c​h​e​s​ ​e​v​e​r​y​t​h​i​n​g​.​
 ​ ​ ​ ​a​s​l​m​s​g​ ​q​u​e​r​y​;​
 ​ ​ ​ ​q​u​e​r​y​ ​=​ ​a​s​l​_​n​e​w​ ​(​A​S​L​_​T​Y​P​E​_​Q​U​E​R​Y​)​;​
 ​ ​ ​ ​a​s​l​_​s​e​t​_​q​u​e​r​y​ ​(​q​u​e​r​y​,​ ​A​S​L​_​K​E​Y​_​S​E​N​D​E​R​,​ ​"​"​,​ ​A​S​L​_​Q​U​E​R​Y​_​O​P​_​T​R​U​E​)​;​

 ​ ​ ​ ​/​/​ ​P​e​r​f​o​r​m​ ​t​h​e​ ​s​e​a​r​c​h​.​
 ​ ​ ​ ​a​s​l​r​e​s​p​o​n​s​e​ ​r​e​s​u​l​t​s​ ​=​ ​a​s​l​_​s​e​a​r​c​h​ ​(​N​U​L​L​,​ ​q​u​e​r​y​)​;​

 ​ ​ ​ ​/​/​ ​w​a​l​k​ ​t​h​e​ ​r​e​t​u​r​n​e​d​ ​m​e​s​s​a​g​e​s​
 ​ ​ ​ ​a​s​l​m​s​g​ ​m​e​s​s​a​g​e​;​
 ​ ​ ​ ​w​h​i​l​e​ ​(​(​m​e​s​s​a​g​e​ ​=​ ​a​s​l​r​e​s​p​o​n​s​e​_​n​e​x​t​(​r​e​s​u​l​t​s​)​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​d​u​m​p​A​s​l​M​s​g​ ​(​m​e​s​s​a​g​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​\​n​"​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​a​s​l​r​e​s​p​o​n​s​e​_​f​r​e​e​ ​(​r​e​s​u​l​t​s​)​;​
 ​ ​ ​ ​a​s​l​_​f​r​e​e​ ​(​q​u​e​r​y​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​/​/​ ​m​a​i​n​

And here is a sample run:

$​ ​.​/​a​s​l​-​l​i​s​t​

0​:​ ​T​i​m​e​ ​-​>​ ​1​2​8​0​8​7​0​9​7​0​
1​:​ ​H​o​s​t​ ​-​>​ ​P​h​e​a​s​a​n​t​b​o​o​k​
2​:​ ​S​e​n​d​e​r​ ​-​>​ ​k​e​r​n​e​l​
3​:​ ​P​I​D​ ​-​>​ ​0​
4​:​ ​U​I​D​ ​-​>​ ​0​
5​:​ ​G​I​D​ ​-​>​ ​0​
6​:​ ​L​e​v​e​l​ ​-​>​ ​3​
7​:​ ​M​e​s​s​a​g​e​ ​-​>​ ​(​n​u​l​l​)​
8​:​ ​A​S​L​M​e​s​s​a​g​e​I​D​ ​-​>​ ​6​5​2​9​7​
9​:​ ​T​i​m​e​N​a​n​o​S​e​c​ ​-​>​ ​0​
1​0​:​ ​R​e​a​d​U​I​D​ ​-​>​ ​0​
1​1​:​ ​R​e​a​d​G​I​D​ ​-​>​ ​8​0​
1​2​:​ ​F​a​c​i​l​i​t​y​ ​-​>​ ​c​o​m​.​a​p​p​l​e​.​s​y​s​t​e​m​.​f​s​
1​3​:​ ​E​r​r​T​y​p​e​ ​-​>​ ​I​O​
1​4​:​ ​E​r​r​N​o​ ​-​>​ ​5​
1​5​:​ ​I​O​T​y​p​e​ ​-​>​ ​R​e​a​d​
1​6​:​ ​P​B​l​k​N​u​m​ ​-​>​ ​9​1​1​2​8​1​
1​7​:​ ​L​B​l​k​N​u​m​ ​-​>​ ​1​2​9​9​5​
1​8​:​ ​F​S​L​o​g​M​s​g​I​D​ ​-​>​ ​1​4​3​8​2​7​1​3​3​3​
1​9​:​ ​F​S​L​o​g​M​s​g​O​r​d​e​r​ ​-​>​ ​F​i​r​s​t​
2​0​:​ ​A​S​L​E​x​p​i​r​e​T​i​m​e​ ​-​>​ ​1​3​1​2​4​9​3​3​7​0​
-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
0​:​ ​T​i​m​e​ ​-​>​ ​1​2​8​0​8​7​0​9​7​0​
1​:​ ​H​o​s​t​ ​-​>​ ​P​h​e​a​s​a​n​t​b​o​o​k​
2​:​ ​S​e​n​d​e​r​ ​-​>​ ​k​e​r​n​e​l​
3​:​ ​P​I​D​ ​-​>​ ​0​
4​:​ ​U​I​D​ ​-​>​ ​0​
5​:​ ​G​I​D​ ​-​>​ ​0​
6​:​ ​L​e​v​e​l​ ​-​>​ ​3​
7​:​ ​M​e​s​s​a​g​e​ ​-​>​ ​(​n​u​l​l​)​
8​:​ ​A​S​L​M​e​s​s​a​g​e​I​D​ ​-​>​ ​6​5​2​9​9​
9​:​ ​T​i​m​e​N​a​n​o​S​e​c​ ​-​>​ ​0​
1​0​:​ ​R​e​a​d​U​I​D​ ​-​>​ ​0​
1​1​:​ ​R​e​a​d​G​I​D​ ​-​>​ ​8​0​
1​2​:​ ​F​a​c​i​l​i​t​y​ ​-​>​ ​c​o​m​.​a​p​p​l​e​.​s​y​s​t​e​m​.​f​s​
1​3​:​ ​D​e​v​N​o​d​e​ ​-​>​ ​/​d​e​v​/​d​i​s​k​2​s​2​
1​4​:​ ​M​o​u​n​t​P​t​ ​-​>​ ​/​V​o​l​u​m​e​s​/​T​i​m​e​ ​M​a​c​h​i​n​e​ ​B​a​c​k​u​p​s​
1​5​:​ ​F​S​L​o​g​M​s​g​I​D​ ​-​>​ ​1​4​3​8​2​7​1​3​3​3​
1​6​:​ ​F​S​L​o​g​M​s​g​O​r​d​e​r​ ​-​>​ ​L​a​s​t​
1​7​:​ ​A​S​L​E​x​p​i​r​e​T​i​m​e​ ​-​>​ ​1​3​1​2​4​9​3​3​7​0​

There actually was a lot more output. This is just a representative sample.

Example 5.12 logs a simple message with asl_log(), and then it builds a template message and uses that for a couple of logs. The template has a custom key in it, “Suit” – presumably because the organization running this program wants to log when someone enters the research lab while wearing a specific kind of protective suit. Two people are logged as having been suited up. After the logs, a query is made to get the log messages for when Alex wears his suit.

Example 5.12. asl-log-n-query.m

/​/​ ​a​s​l​-​l​o​g​-​n​-​q​u​e​r​y​.​m​ ​-​-​ ​d​o​ ​s​o​m​e​ ​l​o​g​s​ ​a​n​d​ ​s​o​m​e​ ​q​u​e​r​i​e​s​

#​i​m​p​o​r​t​ ​<​a​s​l​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​A​S​L​ ​f​u​n​c​t​i​o​n​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​y​s​l​o​g​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​L​O​G​_​ ​c​o​n​s​t​a​n​t​s​
#​i​m​p​o​r​t​ ​<​i​n​t​t​y​p​e​s​.​h​>​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​ ​c​o​n​s​t​a​n​t​ ​P​R​I​u​3​2​

/​/​ ​g​c​c​ ​-​g​ ​-​s​t​d​=​c​9​9​ ​-​W​a​l​l​ ​-​o​ ​a​s​l​-​l​o​g​-​n​-​q​u​e​r​y​ ​a​s​l​-​l​o​g​-​n​-​q​u​e​r​y​.​m​

v​o​i​d​ ​d​u​m​p​A​s​l​M​s​g​(​a​s​l​m​s​g​ ​m​e​s​s​a​g​e​)​ ​{​
 ​ ​ ​ ​/​/​ ​w​a​l​k​ ​t​h​e​ ​k​e​y​s​ ​a​n​d​ ​v​a​l​u​e​s​ ​i​n​ ​e​a​c​h​ ​m​e​s​s​a​g​e​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​k​e​y​,​ ​*​v​a​l​u​e​;​
 ​ ​ ​ ​u​i​n​t​3​2​_​t​ ​i​ ​=​ ​0​;​
 ​ ​ ​ ​w​h​i​l​e​ ​(​(​k​e​y​ ​=​ ​a​s​l​_​k​e​y​(​m​e​s​s​a​g​e​,​ ​i​)​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​v​a​l​u​e​ ​=​ ​a​s​l​_​g​e​t​ ​(​m​e​s​s​a​g​e​,​ ​k​e​y​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​u​:​ ​%​s​ ​=​>​ ​%​s​\​n​"​,​ ​i​,​ ​k​e​y​,​ ​v​a​l​u​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​+​+​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​d​u​m​p​A​s​l​M​s​g​

i​n​t​ ​m​a​i​n​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​/​/​ ​P​e​r​f​o​r​m​ ​a​ ​s​i​m​p​l​e​ ​l​o​g​.​
 ​ ​ ​ ​a​s​l​_​l​o​g​ ​(​N​U​L​L​,​ ​N​U​L​L​,​ ​L​O​G​_​N​O​T​I​C​E​,​ ​"​H​e​l​l​o​ ​h​o​w​ ​a​r​e​ ​%​s​ ​t​o​d​a​y​?​"​,​ ​"​y​o​u​"​)​;​

 ​ ​ ​ ​/​/​ ​M​a​k​e​ ​a​ ​t​e​m​p​l​a​t​e​ ​m​e​s​s​a​g​e​ ​w​i​t​h​ ​o​u​r​ ​c​u​s​t​o​m​ ​t​a​g​s​
 ​ ​ ​ ​a​s​l​m​s​g​ ​t​e​m​p​l​a​t​e​ ​=​ ​a​s​l​_​n​e​w​ ​(​A​S​L​_​T​Y​P​E​_​M​S​G​)​;​
 ​ ​ ​ ​a​s​l​_​s​e​t​ ​(​t​e​m​p​l​a​t​e​,​ ​"​S​u​i​t​"​,​ ​"​(​4​A​)​C​G​S​"​)​;​

 ​ ​ ​ ​/​/​ ​L​o​g​ ​s​o​m​e​ ​m​e​s​s​a​g​e​s​.​
 ​ ​ ​ ​a​s​l​_​l​o​g​ ​(​N​U​L​L​,​ ​t​e​m​p​l​a​t​e​,​ ​L​O​G​_​N​O​T​I​C​E​,​ ​"​L​a​u​r​e​l​ ​h​a​s​ ​s​u​i​t​e​d​ ​u​p​"​)​;​
 ​ ​ ​ ​a​s​l​_​l​o​g​ ​(​N​U​L​L​,​ ​t​e​m​p​l​a​t​e​,​ ​L​O​G​_​N​O​T​I​C​E​,​ ​"​A​l​e​x​ ​h​a​s​ ​s​u​i​t​e​d​ ​u​p​"​)​;​

 ​ ​ ​ ​/​/​ ​D​o​ ​a​ ​q​u​e​r​y​ ​t​o​ ​s​e​e​ ​h​o​w​ ​m​a​n​y​ ​t​i​m​e​s​ ​A​l​e​x​ ​h​a​s​ ​w​o​r​n​ ​h​i​s​ ​(​4​A​)​C​G​S​ ​s​u​i​t​

 ​ ​ ​ ​a​s​l​m​s​g​ ​q​u​e​r​y​ ​=​ ​a​s​l​_​n​e​w​ ​(​A​S​L​_​T​Y​P​E​_​Q​U​E​R​Y​)​;​

 ​ ​ ​ ​a​s​l​_​s​e​t​_​q​u​e​r​y​ ​(​q​u​e​r​y​,​ ​"​S​u​i​t​"​,​ ​"​(​4​A​)​C​G​S​"​,​ ​A​S​L​_​Q​U​E​R​Y​_​O​P​_​E​Q​U​A​L​)​;​
 ​ ​ ​ ​a​s​l​_​s​e​t​_​q​u​e​r​y​ ​(​q​u​e​r​y​,​ ​A​S​L​_​K​E​Y​_​M​S​G​,​ ​"​A​l​e​x​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​A​S​L​_​Q​U​E​R​Y​_​O​P​_​E​Q​U​A​L​ ​|​ ​A​S​L​_​Q​U​E​R​Y​_​O​P​_​P​R​E​F​I​X​)​;​

 ​ ​ ​ ​/​/​ ​P​e​r​f​o​r​m​ ​t​h​e​ ​s​e​a​r​c​h​.​
 ​ ​ ​ ​a​s​l​r​e​s​p​o​n​s​e​ ​r​e​s​u​l​t​s​ ​=​ ​a​s​l​_​s​e​a​r​c​h​(​N​U​L​L​,​ ​q​u​e​r​y​)​;​

 ​ ​ ​ ​/​/​ ​W​a​l​k​ ​t​h​e​ ​r​e​t​u​r​n​e​d​ ​m​e​s​s​a​g​e​s​.​
 ​ ​ ​ ​a​s​l​m​s​g​ ​m​e​s​s​a​g​e​ ​=​ ​N​U​L​L​;​
 ​ ​ ​ ​w​h​i​l​e​ ​(​(​m​e​s​s​a​g​e​ ​=​ ​a​s​l​r​e​s​p​o​n​s​e​_​n​e​x​t​(​r​e​s​u​l​t​s​)​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​d​u​m​p​A​s​l​M​s​g​(​m​e​s​s​a​g​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​\​n​"​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​C​l​e​a​n​u​p​.​
 ​ ​ ​ ​a​s​l​r​e​s​p​o​n​s​e​_​f​r​e​e​ ​(​r​e​s​u​l​t​s​)​;​
 ​ ​ ​ ​a​s​l​_​f​r​e​e​ ​(​q​u​e​r​y​)​;​
 ​ ​ ​ ​a​s​l​_​f​r​e​e​(​ ​t​e​m​p​l​a​t​e​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​/​/​ ​m​a​i​n​

When you run the program, have a tail -f pointed at /var/system.log so you can see the three log messages:

$​ ​t​a​i​l​ ​-​f​ ​/​v​a​r​/​s​y​s​t​e​m​.​l​o​g​
M​a​y​ ​2​0​ ​1​4​:​1​5​:​0​8​ ​p​h​e​a​s​a​n​t​b​o​o​k​ ​a​s​l​-​l​o​g​-​n​-​q​u​e​r​y​[​9​0​2​4​8​]​:​ ​H​e​l​l​o​ ​h​o​w​ ​a​r​e​ ​y​o​u​ ​t​o​d​a​y​?​
M​a​y​ ​2​0​ ​1​4​:​1​5​:​0​8​ ​p​h​e​a​s​a​n​t​b​o​o​k​ ​a​s​l​-​l​o​g​-​n​-​q​u​e​r​y​[​9​0​2​4​8​]​:​ ​L​a​u​r​e​l​ ​h​a​s​ ​s​u​i​t​e​d​ ​u​p​
M​a​y​ ​2​0​ ​1​4​:​1​5​:​0​8​ ​p​h​e​a​s​a​n​t​b​o​o​k​ ​a​s​l​-​l​o​g​-​n​-​q​u​e​r​y​[​9​0​2​4​8​]​:​ ​A​l​e​x​ ​h​a​s​ ​s​u​i​t​e​d​ ​u​p​

Then the program displays the results from its query. It shows an earlier notice from 17:45:45 and the one from 19:20:57. The discrepancy in time stamps between the program’s output and syslogd’s output is due to time zones: the time returned from the query is in UTC (coordinated universal time), while the log file uses the current time zone (Eastern Daylight Time in this case).

$​ ​.​/​a​s​l​-​l​o​g​-​n​-​q​u​e​r​y​

0​:​ ​T​i​m​e​ ​=​>​ ​1​3​0​5​9​1​4​4​1​3​
1​:​ ​H​o​s​t​ ​=​>​ ​p​h​e​a​s​a​n​t​b​o​o​k​
2​:​ ​S​e​n​d​e​r​ ​=​>​ ​a​s​l​-​l​o​g​-​n​-​q​u​e​r​y​
3​:​ ​P​I​D​ ​=​>​ ​8​9​9​5​3​
4​:​ ​U​I​D​ ​=​>​ ​5​0​1​
5​:​ ​G​I​D​ ​=​>​ ​2​0​
6​:​ ​L​e​v​e​l​ ​=​>​ ​5​
7​:​ ​M​e​s​s​a​g​e​ ​=​>​ ​A​l​e​x​ ​h​a​s​ ​s​u​i​t​e​d​ ​u​p​
8​:​ ​A​S​L​M​e​s​s​a​g​e​I​D​ ​=​>​ ​8​6​9​4​5​8​
9​:​ ​T​i​m​e​N​a​n​o​S​e​c​ ​=​>​ ​3​6​0​8​8​5​0​0​0​
1​0​:​ ​F​a​c​i​l​i​t​y​ ​=​>​ ​u​s​e​r​
1​1​:​ ​S​u​i​t​ ​=​>​ ​4​A​C​G​S​
-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
0​:​ ​T​i​m​e​ ​=​>​ ​1​3​0​5​9​1​4​5​2​2​
1​:​ ​H​o​s​t​ ​=​>​ ​p​h​e​a​s​a​n​t​b​o​o​k​
2​:​ ​S​e​n​d​e​r​ ​=​>​ ​a​s​l​-​l​o​g​-​n​-​q​u​e​r​y​
3​:​ ​P​I​D​ ​=​>​ ​8​9​9​7​9​
4​:​ ​U​I​D​ ​=​>​ ​5​0​1​
5​:​ ​G​I​D​ ​=​>​ ​2​0​
6​:​ ​L​e​v​e​l​ ​=​>​ ​5​
7​:​ ​M​e​s​s​a​g​e​ ​=​>​ ​A​l​e​x​ ​h​a​s​ ​s​u​i​t​e​d​ ​u​p​
.​.​.​

ASL also has some features that allow you to change the logging level of applications remotely, allowing you as a user or an administrator to crank up or down the volume of logging from an application. Check out the asl(3) and syslog(1) manpages.

For the More Curious: Assertions

Assertions are a programming technique where tests are added for conditions that cannot happen. The program kills itself when it enters such an impossible state, dumping a core file if it can.

The assert macro

assert (expression)

evaluates the expression. If the expression is zero (false), the process is terminated, a diagnostic message is written to the standard error stream, and the abort() function is called, which terminates the program by raising the signal SIGABRT. Because most programmers use asserts for debugging purposes, you can compile them out of a production program by using the preprocessor flag -DNDEBUG. Be aware of side effects due to code called in the assert() macro. This could cause your program to fail in a non-debug build.

Should you always leave assertions and debugging statements in your code? There are two schools of thought. One idea, usually held by programmers that primarily work on server software, is that programs should terminate immediately if something bad is detected. A server program will be restarted if it exits, and the faster you exit the less chance you have of corrupting the user’s data and sending that corrupted data to permanent storage.

On the other hand, one of the worst things a GUI application can do is suddenly exit, wiping out any work the user has done. In that case, you would not want to immediately terminate the program, but instead put up some kind of “help help i am dying” alert and give the user an opportunity to save any data.

Cocoa also has an assertion mechanism, NSAssertionHandler. There you use macros like NSAssert(test, msg) (when in a method) and NSCAssert(test, msg) (when in a regular old C function) to evaluate a condition. If it evaluates to false, the message is passed to an NSAssertionHandler (one is associated with each thread). When invoked, this object prints an error message and raises an NSInternalInconsitencyException, which can be caught using @catch().

Static assertions

A neat trick is crafting an assertion so that it prevents the file from compiling if the assertion fails. This notifies you of the failed test without having to run the executable! The easiest way to make such a static assertion is to exploit the guarantee that arrays must have a non-negative size. For example, suppose you wanted to assert that sizeof(int) == sizeof(long). You can use a typedef to avoid any cost in space or speed to the program while still checking this predicate statically:

t​y​p​e​d​e​f​ ​c​h​a​r​ ​A​s​s​e​r​t​I​n​t​A​n​d​L​o​n​g​H​a​v​e​S​a​m​e​S​i​z​e​[​(​s​i​z​e​o​f​(​i​n​t​)​ ​=​=​ ​s​i​z​e​o​f​(​l​o​n​g​)​)​ ​?​ ​0​ ​:​ ​-​1​]​;​

There’s no problem compiling for i386 because both int and long are four bytes. But compiling it for x86_64 yields an error:

$​ ​g​c​c​ ​-​a​r​c​h​ ​i​3​8​6​ ​i​s​I​L​P​3​2​.​m​
$​ ​g​c​c​ ​-​a​r​c​h​ ​x​8​6​_​6​4​ ​i​s​I​L​P​3​2​.​m​
i​s​I​L​P​3​2​.​m​:​4​:​ ​e​r​r​o​r​:​ ​s​i​z​e​ ​o​f​ ​a​r​r​a​y​ ​‘​A​s​s​e​r​t​I​n​t​A​n​d​L​o​n​g​H​a​v​e​S​a​m​e​S​i​z​e​’​ ​i​s​ ​n​e​g​a​t​i​v​e​

AssertMacros.h

/usr/include/AssertMacros.h has a collection of useful debugging macros that can be used to control program flow in the face of error conditions. For example, require_noerr takes an error code and a label. In debug builds, if the label is not zero, otherwise known as noErr, it will invoke the DEBUG_ASSERT_MESSAGE macro, which will log the error value, file, line, etc and then jump to the indicated label. You can replace that macro with one that does other work, such as breaking into the debugger, for helping to catch “that can’t possibly happen” circumstances. In production builds, it simply jumps to the label.

Exercises

Fix the race conditions in interrupt.m by using sigprocmask(). Should sigsetjmp() be saving the signal mask or not?

Tweak /etc/syslogd.conf to display the debug message from syslog.m in the system.log file.

6
Libraries

A library is a packaged collection of object files that programs can link against to make use of the features it provides. Traditional Unix has two kinds of libraries: static libraries, where the linker packages the object code into the application, and shared libraries, where the linker just stores a reference to a library and the symbols the application needs. Mac OS X also brings frameworks to the table. Frameworks package shared libraries with other resources, like header files, documentation, and subframeworks.

Static Libraries

Static libraries are the simplest libraries to work with. Many open source projects that you can download will frequently build static libraries, whether for your programs to link against or for internal use to simplify the build system where each major module is put into its own static library. All of these libraries are then linked together to make the final executable program. Figure 6.1 shows that the object code that lives in the shared libraries is physically copied into the final executable.

Figure 6.1  Static libraries

The ar program is what is used to create libraries, or in ar’s terminology, archives. ar can create an archive, add new files to it, and existing files can be extracted, deleted, or replaced. Files are named in the archive by their file name (any files specified with a path just use the file name).

In the online materials for this book, you will find five source files that look like Example 6.1:

Example 6.1. src0.c

/​/​ ​s​r​c​0​.​c​ ​:​ ​a​ ​s​i​m​p​l​e​ ​s​o​u​r​c​e​ ​f​i​l​e​ ​s​o​ ​w​e​ ​c​a​n​ ​f​i​l​l​ ​a​ ​l​i​b​r​a​r​y​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​c​ ​s​r​c​0​.​c​

i​n​t​ ​a​d​d​_​0​ ​(​i​n​t​ ​n​u​m​b​e​r​)​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​n​u​m​b​e​r​ ​+​ ​0​;​
}​ ​/​/​ ​a​d​d​_​0​

These are little functions that do not actually do anything useful. You can compile them all into object files by this command:

$​ ​g​c​c​ ​-​g​ ​-​c​ ​s​r​c​?​.​c​

Recall that the shell will use ? as a one-character wildcard. You can see the source files:

$​ ​l​s​ ​s​r​c​?​.​c​
s​r​c​0​.​c​ ​ ​s​r​c​1​.​c​ ​ ​s​r​c​2​.​c​ ​ ​s​r​c​3​.​c​ ​ ​s​r​c​4​.​c​

and the associated object files:

$​ ​l​s​ ​s​r​c​?​.​o​
s​r​c​0​.​o​ ​ ​s​r​c​1​.​o​ ​ ​s​r​c​2​.​o​ ​ ​s​r​c​3​.​o​ ​ ​s​r​c​4​.​o​

Create the archive:

$​ ​a​r​ ​c​r​l​ ​l​i​b​a​d​d​u​m​.​a​ ​s​r​c​?​.​o​

with the final result looking like Figure 6.2. A static library is basically a collection of object files.

Figure 6.2  Inside a library

The flags are:

c | Create if the archive does not exist.
r | Replace or add the specified files to the archive.
l | The next argument is the name of the library file.

After you create or modify an archive, you may need to run the ranlib command, which builds the table of contents for the archive. The linker needs this table of contents to locate the object files it actually needs to link in. Frequently, ranlib can be used to “fix” broken libraries. If you get strange linker errors (particularly if you’re not using Apple’s development tools) when using a static library, run ranlib on it and see if it that helps things.

$​ ​r​a​n​l​i​b​ ​l​i​b​a​d​d​u​m​.​a​

To actually use a static library in your program, you need to use two compiler flags. -L tells the linker what directory to look in. -l (lowercase ell) tells the linker what files to look for. By convention, library file names are of the form libfoo.a, where foo is some descriptive name for the features the library provides. If you specify -lfoo, the linker knows to look for libfoo.a.

Example 6.2 uses some of the functions from the libaddum.a.

Example 6.2. useadd.m

/​/​ ​u​s​e​a​d​d​.​m​ ​-​-​ ​u​s​e​ ​f​u​n​c​t​i​o​n​s​ ​f​r​o​m​ ​a​ ​l​i​b​r​a​r​y​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​u​s​e​a​d​d​ ​u​s​e​a​d​d​.​m​ ​-​L​.​ ​-​l​a​d​d​u​m​

#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​i​;​

 ​ ​ ​ ​i​ ​=​ ​5​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​i​ ​i​s​ ​%​d​\​n​"​,​ ​i​)​;​

 ​ ​ ​ ​i​ ​=​ ​a​d​d​_​1​ ​(​i​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​i​ ​a​f​t​e​r​ ​a​d​d​_​1​:​ ​%​d​\​n​"​,​ ​i​)​;​

 ​ ​ ​ ​i​ ​=​ ​a​d​d​_​4​ ​(​i​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​i​ ​a​f​t​e​r​ ​a​d​d​_​4​:​ ​%​d​\​n​"​,​ ​i​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​/​/​ ​m​a​i​n​

If you just try to compile like other programs, you will understandably get a complaint about the missing functions:

$​ ​g​c​c​ ​-​g​ ​-​o​ ​u​s​e​a​d​d​ ​u​s​e​a​d​d​.​m​
U​n​d​e​f​i​n​e​d​ ​s​y​m​b​o​l​s​ ​f​o​r​ ​a​r​c​h​i​t​e​c​t​u​r​e​ ​x​8​6​_​6​4​:​
 ​ ​"​_​a​d​d​_​1​"​,​ ​r​e​f​e​r​e​n​c​e​d​ ​f​r​o​m​:​
 ​ ​ ​ ​ ​ ​_​m​a​i​n​ ​i​n​ ​c​c​g​3​l​I​O​O​.​o​
 ​ ​"​_​a​d​d​_​4​"​,​ ​r​e​f​e​r​e​n​c​e​d​ ​f​r​o​m​:​
 ​ ​ ​ ​ ​ ​_​m​a​i​n​ ​i​n​ ​c​c​g​3​l​I​O​O​.​o​
l​d​:​ ​s​y​m​b​o​l​(​s​)​ ​n​o​t​ ​f​o​u​n​d​ ​f​o​r​ ​a​r​c​h​i​t​e​c​t​u​r​e​ ​x​8​6​_​6​4​
c​o​l​l​e​c​t​2​:​ ​l​d​ ​r​e​t​u​r​n​e​d​ ​1​ ​e​x​i​t​ ​s​t​a​t​u​s​

BSD systems frequently prepend an underscore to symbols during linking, and Mac OS X is no different. The missing symbol names are actually “add_1” and “add_4.” Now add the flags to tell the linker where to look and what library to use:

$​ ​g​c​c​ ​-​g​ ​-​o​ ​u​s​e​a​d​d​ ​u​s​e​a​d​d​.​m​ ​-​L​.​ ​-​l​a​d​d​u​m​ ​

A sample run:

$​ ​.​/​u​s​e​a​d​d​
i​ ​i​s​ ​5​
i​ ​a​f​t​e​r​ ​a​d​d​_​1​:​ ​6​
i​ ​a​f​t​e​r​ ​a​d​d​_​4​:​ ​1​0​

Note that the library stuff is specified after the source file name. If it were the other way around:

$​ ​g​c​c​ ​-​g​ ​-​o​ ​u​s​e​a​d​d​ ​-​L​.​ ​-​l​a​d​d​u​m​ ​u​s​e​a​d​d​.​m​
/​u​s​r​/​b​i​n​/​l​d​:​ ​U​n​d​e​f​i​n​e​d​ ​s​y​m​b​o​l​s​:​
_​a​d​d​_​1​
_​a​d​d​_​4​

you would still get the errors because the linker scans files left to right. It looks at the library, sees that nobody so far needs those symbols to link, and so discards the file. It then goes on to resolve the symbols for the useadd program itself. Since it already discarded the library, the linker complains about the missing symbols. Depending on the complexity of your libraries (e.g., circular references), you may need to specify a library more than once.

When you get one of these undefined symbol errors when using libraries provided by other parties, it can be a real hassle figuring out where a symbol lives. The nm command can come in handy, since it shows you information about the symbols that live in applications, libraries, and object files.

Compile the program but generate an object file instead of a program (the -c flag):

$​ ​g​c​c​ ​-​g​ ​-​c​ ​u​s​e​a​d​d​.​m​
$​ ​l​s​ ​-​l​ ​u​s​e​a​d​d​.​o​
-​r​w​-​r​-​-​r​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​4​1​0​8​ ​M​a​y​ ​1​8​ ​1​5​:​0​9​ ​u​s​e​a​d​d​.​o​

And now nm it:

$​ ​n​m​ ​u​s​e​a​d​d​.​o​
0​0​0​0​0​0​0​0​0​0​0​0​0​4​5​0​ ​s​ ​E​H​_​f​r​a​m​e​1​
0​0​0​0​0​0​0​0​0​0​0​0​0​4​1​5​ ​s​ ​L​C​0​
0​0​0​0​0​0​0​0​0​0​0​0​0​4​1​e​ ​s​ ​L​C​1​
0​0​0​0​0​0​0​0​0​0​0​0​0​4​3​1​ ​s​ ​L​C​2​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​U​ ​_​a​d​d​_​1​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​U​ ​_​a​d​d​_​4​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​U​ ​_​e​x​i​t​
0​0​0​0​0​0​0​0​0​0​0​0​0​0​0​0​ ​T​ ​_​m​a​i​n​
0​0​0​0​0​0​0​0​0​0​0​0​0​4​6​8​ ​S​ ​_​m​a​i​n​.​e​h​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​U​ ​_​p​r​i​n​t​f​

The U is for “undefined,” and the T stands for a defined text section symbol. Chapter 7: Memory discusses the text section, which is the area of the program file that contains the actual code that will be executed. You can see the two add functions that were used, printf, plus a little housekeeping.

You can nm libraries too:

$​ ​n​m​ ​l​i​b​a​d​d​u​m​.​a​ ​
l​i​b​a​d​d​u​m​.​a​(​s​r​c​0​.​o​)​:​
0​0​0​0​0​0​0​0​0​0​0​0​0​2​9​0​ ​s​ ​E​H​_​f​r​a​m​e​1​
0​0​0​0​0​0​0​0​0​0​0​0​0​0​0​0​ ​T​ ​_​a​d​d​_​0​
0​0​0​0​0​0​0​0​0​0​0​0​0​2​a​8​ ​S​ ​_​a​d​d​_​0​.​e​h​

l​i​b​a​d​d​u​m​.​a​(​s​r​c​1​.​o​)​:​
0​0​0​0​0​0​0​0​0​0​0​0​0​2​9​0​ ​s​ ​E​H​_​f​r​a​m​e​1​
0​0​0​0​0​0​0​0​0​0​0​0​0​0​0​0​ ​T​ ​_​a​d​d​_​1​
0​0​0​0​0​0​0​0​0​0​0​0​0​2​a​8​ ​S​ ​_​a​d​d​_​1​.​e​h​

l​i​b​a​d​d​u​m​.​a​(​s​r​c​2​.​o​)​:​
0​0​0​0​0​0​0​0​0​0​0​0​0​2​9​0​ ​s​ ​E​H​_​f​r​a​m​e​1​
0​0​0​0​0​0​0​0​0​0​0​0​0​0​0​0​ ​T​ ​_​a​d​d​_​2​
0​0​0​0​0​0​0​0​0​0​0​0​0​2​a​8​ ​S​ ​_​a​d​d​_​2​.​e​h​

l​i​b​a​d​d​u​m​.​a​(​s​r​c​3​.​o​)​:​
0​0​0​0​0​0​0​0​0​0​0​0​0​2​9​0​ ​s​ ​E​H​_​f​r​a​m​e​1​
0​0​0​0​0​0​0​0​0​0​0​0​0​0​0​0​ ​T​ ​_​a​d​d​_​3​
0​0​0​0​0​0​0​0​0​0​0​0​0​2​a​8​ ​S​ ​_​a​d​d​_​3​.​e​h​

l​i​b​a​d​d​u​m​.​a​(​s​r​c​4​.​o​)​:​
0​0​0​0​0​0​0​0​0​0​0​0​0​2​9​0​ ​s​ ​E​H​_​f​r​a​m​e​1​
0​0​0​0​0​0​0​0​0​0​0​0​0​0​0​0​ ​T​ ​_​a​d​d​_​4​
0​0​0​0​0​0​0​0​0​0​0​0​0​2​a​8​ ​S​ ​_​a​d​d​_​4​.​e​h​

This shows each object file that has been put into the archive, as well as what symbols are present.

Using static libraries in Xcode is really easy. Just drag the libfoo.a file into your project. Xcode will automatically link it in.

Shared Libraries

When you use static libraries, the code is linked physically into your executable program. If you have a big library, say 10 megabytes, which is linked into a dozen programs, you will have 120 megabytes of disk space consumed. With today’s huge hard drives, that’s not too big of a deal, but you also have the libraries taking up those megabytes of space in each program’s memory. This is a much bigger problem. Memory is a scarce shared resource, so having duplicate copies of library code each occupying its own pages in memory can put stress on the memory system and cause paging.

Shared libraries were created to address this problem. Instead of copying the code into the programs, just a reference is included. When the program needs a feature out of a shared library, the linker just includes the name of the symbol and a pointer to the library, as shown in Figure 6.3.

Figure 6.3  Shared libraries

When the program is executed, the loader finds the shared libraries, loads them into memory and fixes up the references (resolves the symbols) so that they point to the now-loaded shared library. The shared library code can be loaded into shared pages of memory and shared among many different processes. In the example above, the dozen programs linking to a 10-megabyte shared library will not take up any extra space on disk – plus the library will only appear once in physical RAM and will be shared amongst the dozen processes. Of course, any space for variable data the shared library uses will be duplicated in each process.

Libraries on OS X support two-level namespaces. This means that the name of the library is stored along with the symbol. In the “flat namespace” model, just the symbol was stored, and the loader would search amongst various libraries for the symbol. That caused problems, for instance, if the log() function was defined in a math library and someone else defined a log() function to output text to a logfile. The math library function user might get the file logging function instead. The two-level namespaces handle this case for you so that the two log() functions can coexist. You can sometimes run into difficulty when building code from other Unix platforms. Adding the flag -flat_namespace to the link lines can fix many problems.

To build a shared library, use ld, the linker, rather than ar. To build the adder functions shared library, use the command

$​ ​l​d​ ​-​d​y​l​i​b​ ​-​o​ ​l​i​b​a​d​d​u​m​.​d​y​l​i​b​ ​*​.​o​

And link it into your program:

$​ ​g​c​c​ ​-​g​ ​-​o​ ​u​s​e​a​d​d​ ​u​s​e​a​d​d​.​m​ ​l​i​b​a​d​d​u​m​.​d​y​l​i​b​

As an aside, you can specify a static library:

$​ ​g​c​c​ ​-​g​ ​-​o​ ​u​s​e​a​d​d​ ​u​s​e​a​d​d​.​m​ ​l​i​b​a​d​d​u​m​.​a​

and you can use linker search paths with shared libraries:

$​ ​g​c​c​ ​-​g​ ​-​o​ ​u​s​e​a​d​d​ ​u​s​e​a​d​d​.​m​ ​-​L​.​ ​-​l​a​d​d​u​m​

The linker, when given a choice between a shared library and a static library, will choose the shared library. There are no pure static programs on Mac OS X because everything links to libSystem, which is only available in dynamic form.

When you run the program, the loader searches for the shared libraries to load. It looks in a number of default places (like /usr/lib), and it also looks at the environment variable DYLD_LIBRARY_PATH. Any paths specified there (multiple paths can be separated by colons) are searched in order, looking for the library.

By convention on Mac OS X, shared libraries have an extension of .dylib, for Dynamic Library. On most other Unix systems, the extension is .so, for Shared Object.

nm can also be used to see what dynamic libraries an application links against by using -mg flags. In this case, nm is being run against a Cocoa application:

$​ ​n​m​ ​-​m​g​ ​B​i​g​S​h​o​w​
0​0​0​0​0​0​0​0​ ​(​a​b​s​o​l​u​t​e​)​ ​e​x​t​e​r​n​a​l​ ​.​o​b​j​c​_​c​l​a​s​s​_​n​a​m​e​_​A​p​p​C​o​n​t​r​o​l​l​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​(​u​n​d​e​f​i​n​e​d​ ​[​l​a​z​y​ ​b​o​u​n​d​]​)​ ​e​x​t​e​r​n​a​l​ ​.​o​b​j​c​_​c​l​a​s​s​_​n​a​m​e​_​B​i​g​E​l​e​m​e​n​t​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​f​r​o​m​ ​B​i​g​S​h​o​w​B​a​s​e​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​(​u​n​d​e​f​i​n​e​d​ ​[​l​a​z​y​ ​b​o​u​n​d​]​)​ ​e​x​t​e​r​n​a​l​ ​.​o​b​j​c​_​c​l​a​s​s​_​n​a​m​e​_​N​S​A​r​r​a​y​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​f​r​o​m​ ​C​o​c​o​a​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​(​u​n​d​e​f​i​n​e​d​ ​[​l​a​z​y​ ​b​o​u​n​d​]​)​ ​e​x​t​e​r​n​a​l​ ​.​o​b​j​c​_​c​l​a​s​s​_​n​a​m​e​_​N​S​B​e​z​i​e​r​P​a​t​h​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​f​r​o​m​ ​C​o​c​o​a​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​(​u​n​d​e​f​i​n​e​d​ ​[​l​a​z​y​ ​b​o​u​n​d​]​)​ ​e​x​t​e​r​n​a​l​ ​.​o​b​j​c​_​c​l​a​s​s​_​n​a​m​e​_​N​S​B​u​n​d​l​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​f​r​o​m​ ​C​o​c​o​a​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​(​u​n​d​e​f​i​n​e​d​ ​[​l​a​z​y​ ​b​o​u​n​d​]​)​ ​e​x​t​e​r​n​a​l​ ​.​o​b​j​c​_​c​l​a​s​s​_​n​a​m​e​_​N​S​C​o​l​o​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​f​r​o​m​ ​C​o​c​o​a​)​
.​.​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​(​u​n​d​e​f​i​n​e​d​)​ ​e​x​t​e​r​n​a​l​ ​_​_​o​b​j​c​I​n​i​t​ ​(​f​r​o​m​ ​C​o​c​o​a​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​(​u​n​d​e​f​i​n​e​d​ ​[​l​a​z​y​ ​b​o​u​n​d​]​)​ ​e​x​t​e​r​n​a​l​ ​_​a​b​o​r​t​ ​(​f​r​o​m​ ​l​i​b​S​y​s​t​e​m​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​(​u​n​d​e​f​i​n​e​d​ ​[​l​a​z​y​ ​b​o​u​n​d​]​)​ ​e​x​t​e​r​n​a​l​ ​_​a​t​e​x​i​t​ ​(​f​r​o​m​ ​l​i​b​S​y​s​t​e​m​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​(​u​n​d​e​f​i​n​e​d​ ​[​l​a​z​y​ ​b​o​u​n​d​]​)​ ​e​x​t​e​r​n​a​l​ ​_​c​a​l​l​o​c​ ​(​f​r​o​m​ ​l​i​b​S​y​s​t​e​m​)​
.​.​.​
0​0​0​0​7​0​0​8​ ​(​_​_​D​A​T​A​,​_​_​d​a​t​a​)​ ​[​r​e​f​e​r​e​n​c​e​d​ ​d​y​n​a​m​i​c​a​l​l​y​]​ ​e​x​t​e​r​n​a​l​ ​_​e​n​v​i​r​o​n​
 ​ ​ ​ ​ ​ ​ ​ ​ ​(​u​n​d​e​f​i​n​e​d​)​ ​e​x​t​e​r​n​a​l​ ​_​e​r​r​n​o​ ​(​f​r​o​m​ ​l​i​b​S​y​s​t​e​m​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​(​u​n​d​e​f​i​n​e​d​ ​[​l​a​z​y​ ​b​o​u​n​d​]​)​ ​e​x​t​e​r​n​a​l​ ​_​e​x​i​t​ ​(​f​r​o​m​ ​l​i​b​S​y​s​t​e​m​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​(​u​n​d​e​f​i​n​e​d​ ​[​l​a​z​y​ ​b​o​u​n​d​]​)​ ​e​x​t​e​r​n​a​l​ ​_​f​r​e​e​ ​(​f​r​o​m​ ​l​i​b​S​y​s​t​e​m​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​(​u​n​d​e​f​i​n​e​d​)​ ​e​x​t​e​r​n​a​l​ ​_​m​a​c​h​_​i​n​i​t​_​r​o​u​t​i​n​e​ ​(​f​r​o​m​ ​l​i​b​S​y​s​t​e​m​)​

This shows the symbols from Cocoa that are being used (NSArray, NSBezierPath), some of the standard C library symbols (abort(), calloc()), and some housekeeping calls (_objcInit).

Shared libraries can be loaded on demand after your program has started and are the standard Unix way for building a plug-in architecture to your program.

If you want to see all the shared libraries a program pulls in, run the program from the command line and set the DYLD_PRINT_LIBRARIES environment variable to 1. You can use this to peek into a program and see how it does some stuff. For instance:

$​ ​D​Y​L​D​_​P​R​I​N​T​_​L​I​B​R​A​R​I​E​S​=​1​
$​ ​/​A​p​p​l​i​c​a​t​i​o​n​s​/​i​T​u​n​e​s​.​a​p​p​/​C​o​n​t​e​n​t​s​/​M​a​c​O​S​/​i​T​u​n​e​s​ ​
l​o​a​d​i​n​g​ ​l​i​b​r​a​r​i​e​s​ ​f​o​r​ ​i​m​a​g​e​:​ ​/​A​p​p​l​i​c​a​t​i​o​n​s​/​i​T​u​n​e​s​.​a​p​p​/​
C​o​n​t​e​n​t​s​/​M​a​c​O​S​/​i​T​u​n​e​s​
l​o​a​d​i​n​g​ ​l​i​b​r​a​r​y​:​ ​/​u​s​r​/​l​i​b​/​l​i​b​z​.​1​.​1​.​3​.​d​y​l​i​b​
l​o​a​d​i​n​g​ ​l​i​b​r​a​r​y​:​ ​/​u​s​r​/​l​i​b​/​l​i​b​S​y​s​t​e​m​.​B​.​d​y​l​i​b​
l​o​a​d​i​n​g​ ​l​i​b​r​a​r​y​:​ ​/​S​y​s​t​e​m​/​L​i​b​r​a​r​y​/​F​r​a​m​e​w​o​r​k​s​/​C​a​r​b​o​n​.​f​r​a​m​e​w​o​r​k​/​V​e​r​s​i​o​n​s​/​A​/​C​a​r​b​o​n​
l​o​a​d​i​n​g​ ​l​i​b​r​a​r​y​:​ ​/​S​y​s​t​e​m​/​L​i​b​r​a​r​y​/​F​r​a​m​e​w​o​r​k​s​/​I​O​K​i​t​.​f​r​a​m​e​w​o​r​k​/​V​e​r​s​i​o​n​s​/​A​/​I​O​K​i​t​
.​.​.​
l​o​a​d​i​n​g​ ​l​i​b​r​a​r​y​:​ ​/​S​y​s​t​e​m​/​L​i​b​r​a​r​y​/​Q​u​i​c​k​T​i​m​e​/​\​
 ​ ​Q​u​i​c​k​T​i​m​e​F​i​r​e​w​i​r​e​D​V​.​c​o​m​p​o​n​e​n​t​/​C​o​n​t​e​n​t​s​/​M​a​c​O​S​/​Q​u​i​c​k​T​i​m​e​F​i​r​e​w​i​r​e​D​V​
l​o​a​d​i​n​g​ ​l​i​b​r​a​r​i​e​s​ ​f​o​r​ ​i​m​a​g​e​:​ ​/​S​y​s​t​e​m​/​L​i​b​r​a​r​y​/​Q​u​i​c​k​T​i​m​e​/​\​
 ​ ​Q​u​i​c​k​T​i​m​e​F​i​r​e​w​i​r​e​D​V​.​c​o​m​p​o​n​e​n​t​/​C​o​n​t​e​n​t​s​/​M​a​c​O​S​/​Q​u​i​c​k​T​i​m​e​F​i​r​e​w​i​r​e​D​V​
l​o​a​d​i​n​g​ ​l​i​b​r​a​r​i​e​s​ ​f​o​r​ ​i​m​a​g​e​:​ ​/​S​y​s​t​e​m​/​L​i​b​r​a​r​y​/​E​x​t​e​n​s​i​o​n​s​/​\​
 ​ ​I​O​U​S​B​F​a​m​i​l​y​.​k​e​x​t​/​C​o​n​t​e​n​t​s​/​P​l​u​g​I​n​s​/​I​O​U​S​B​L​i​b​.​b​u​n​d​l​e​/​C​o​n​t​e​n​t​s​/​M​a​c​O​S​/​\​
 ​ ​I​O​U​S​B​L​i​b​
l​o​a​d​i​n​g​ ​l​i​b​r​a​r​y​:​/​S​y​s​t​e​m​/​L​i​b​r​a​r​y​/​F​r​a​m​e​w​o​r​k​s​/​\​
 ​ ​A​p​p​l​i​c​a​t​i​o​n​S​e​r​v​i​c​e​s​.​f​r​a​m​e​w​o​r​k​/​V​e​r​s​i​o​n​s​/​A​/​F​r​a​m​e​w​o​r​k​s​/​\​
 ​ ​C​o​r​e​G​r​a​p​h​i​c​s​.​f​r​a​m​e​w​o​r​k​/​R​e​s​o​u​r​c​e​s​/​l​i​b​C​G​A​T​S​.​A​.​d​y​l​i​b​

In all, over 130 libraries. It’s pretty interesting to see some of the stuff in there, like Speech Synthesis, a cryptography library, and some of the private frameworks, like iPod framework and DesktopServicesPriv.

But I included the header!

A common error programmers coming from non-Unix, non-C systems make is assuming that including a header file is sufficient to use a particular library’s features. Instead of a compiler error, you will get a link error. The header file just tells the compiler information like what data structures look like and signatures of functions and methods. Once the compiler is done generating an object file, it forgets about the header file. The linker never hears about the header, so it tries to link your program but doesn’t find the necessary symbols to link against. This is why you also need to specify the library even though you have already included the header in your source.

Frameworks

Shared libraries are nice from a system implementation point of view, but straight shared libraries are a pretty inconvenient way to package and ship a complete product. When you are providing some kind of software library, like a database access API, you will want to provide not only the shared library that has the executable code, but also the header files that describe the API provided, the documentation, and any additional resources like images or sounds. With plain old shared libraries (on plain old Unix), you’ll need to cook up your own packaging format or use whatever platform-specific delivery mechanisms (like RPMs on Red Hat Linux or the Ubuntu packaging system). Even then, the pieces of your product will probably get split up: libraries into /usr/lib, header files into /usr/include, and sound files and images go into who knows where.

NeXT came up with the framework idea to address these issues. Figure 6.4 shows a framework, a bundle that contains the shared library as well as subdirectories for headers and other resources.

Figure 6.4  A framework

Because the framework has a somewhat complex internal structure, including a directory hierarchy for versions and a set of symbolic links to indicate which version is current, it’s best to just let Xcode do the work.

Figure 6.4 shows a maximally complex framework, including multiple versions. Most frameworks you will use or create will be simpler, having just one version and no subframeworks. But this explains why you will see a directory in the framework bundle called “Versions” with a subdirectory “A” with a symbolic link called “Current” pointing to it.

Here’s how to make a shared library for the adder program in Xcode:

Launch Xcode.

File > New > New Project

Framework & Library > Cocoa Framework

Name the framework. In this case, called it Adder.

Drag in your source files: src0.c, etc.

If you wish, you can remove the Cocoa, Foundation, Core Data, and AppKit frameworks from the External Frameworks and Libraries folder, since this program will not link against them.

Now make a header file for the adder functions:

Example 6.3. adder.h

/​/​ ​a​d​d​e​r​.​h​ ​-​-​ ​h​e​a​d​e​r​ ​f​i​l​e​ ​f​o​r​ ​t​h​e​ ​l​i​t​t​l​e​ ​a​d​d​e​r​ ​f​u​n​c​t​i​o​n​s​ ​w​e​ ​h​a​v​e​

i​n​t​ ​a​d​d​_​0​ ​(​i​n​t​ ​n​u​m​b​e​r​)​;​
i​n​t​ ​a​d​d​_​1​ ​(​i​n​t​ ​n​u​m​b​e​r​)​;​
i​n​t​ ​a​d​d​_​2​ ​(​i​n​t​ ​n​u​m​b​e​r​)​;​
i​n​t​ ​a​d​d​_​3​ ​(​i​n​t​ ​n​u​m​b​e​r​)​;​
i​n​t​ ​a​d​d​_​4​ ​(​i​n​t​ ​n​u​m​b​e​r​)​;​

Because it is a public header file, you need to tell Xcode that this header is public so that it will be added to the Headers directory of the framework.

Drag in the header file. You can drag it in from the Finder, or you can create it in place in Xcode. Your files should look like Figure 6.5

Make the header public by setting the Target Membership to Public in the File Inspector, as shown in Figure 6.6

And then build the framework.

When you are done, the project window should look like Figure 6.5

Figure 6.5  Framework source files

Figure 6.6  Setting the header’s public target membership

By default, Xcode 4 places build artifacts into ~/Library/Developer/Xcode/DerivedData/.... You can find the exact location of your framework by popping up the contextual menu on the Adder.framework product, shown at the bottom of Figure 6.5 and choose Show in Finder.

Now that there is a shared library, build the useadd program:

$​ ​A​D​D​E​R​_​F​R​A​M​E​W​O​R​K​_​P​A​T​H​=​/​U​s​e​r​s​/​m​a​r​k​d​/​L​i​b​r​a​r​y​/​D​e​v​e​l​o​p​e​r​/​X​c​o​d​e​/​D​e​r​i​v​e​d​D​a​t​a​/​.​.​.​/​D​e​b​u​g​
$​ ​g​c​c​ ​-​g​ ​-​o​ ​u​s​e​a​d​d​ ​-​F​$​{​A​D​D​E​R​_​F​R​A​M​E​W​O​R​K​_​P​A​T​H​}​ ​-​f​r​a​m​e​w​o​r​k​ ​A​d​d​e​r​ ​u​s​e​a​d​d​.​m​ ​

Use -F to specify where the linker should search for the framework when linking. It works just like the -L flag for static and shared libraries. If you run it now, you will get an error, though:

d​y​l​d​:​ ​L​i​b​r​a​r​y​ ​n​o​t​ ​l​o​a​d​e​d​:​ ​/​L​i​b​r​a​r​y​/​F​r​a​m​e​w​o​r​k​s​/​A​d​d​e​r​.​f​r​a​m​e​w​o​r​k​/​V​e​r​s​i​o​n​s​/​A​/​A​d​d​e​r​
 ​ ​R​e​f​e​r​e​n​c​e​d​ ​f​r​o​m​:​ ​/​U​s​e​r​s​/​m​a​r​k​d​/​W​r​i​t​i​n​g​/​c​o​r​e​-​o​s​x​/​l​i​b​r​a​r​i​e​s​-​c​h​a​p​/​P​r​o​j​e​c​t​s​/​.​/​u​s​e​a​d​d​
 ​ ​R​e​a​s​o​n​:​ ​i​m​a​g​e​ ​n​o​t​ ​f​o​u​n​d​
T​r​a​c​e​/​B​P​T​ ​t​r​a​p​

This is because the -F flag only affects compile-time behavior, finding the framework so the linker can make sure all the symbols are there. At runtime, if the framework is not embedded in the program’s bundle, the system looks in these directories in this order:

~/Library/Frameworks

/Library/Frameworks

/Network/Library/Frameworks

/System/Library/Frameworks

So copy the Adder.framework into your ~/Library/Frameworks (which you may need to manually create) and re-run the program:

$​ ​.​/​u​s​e​a​d​d​
i​ ​i​s​ ​5​
i​ ​a​f​t​e​r​ ​a​d​d​_​1​:​ ​6​
i​ ​a​f​t​e​r​ ​a​d​d​_​4​:​ ​1​0​

If you will be iterating, building this framework fairly often, you might want to make a symbolic link to the library. That way your useadd will always pick up the latest version as you rebuild the framework.

You can see the program using the new framework if you turn on DYLD_PRINT_LIBRARIES:

$​ ​e​x​p​o​r​t​ ​D​Y​L​D​_​P​R​I​N​T​_​L​I​B​R​A​R​I​E​S​=​1​
$​ ​.​/​u​s​e​a​d​d​
d​y​l​d​:​ ​l​o​a​d​e​d​:​ ​/​U​s​e​r​s​/​m​a​r​k​d​/​W​r​i​t​i​n​g​/​c​o​r​e​-​o​s​x​/​l​i​b​r​a​r​i​e​s​-​c​h​a​p​/​P​r​o​j​e​c​t​s​/​.​/​u​s​e​a​d​d​
d​y​l​d​:​ ​l​o​a​d​e​d​:​ ​/​U​s​e​r​s​/​m​a​r​k​d​/​L​i​b​r​a​r​y​/​F​r​a​m​e​w​o​r​k​s​/​A​d​d​e​r​.​f​r​a​m​e​w​o​r​k​/​V​e​r​s​i​o​n​s​/​A​/​A​d​d​e​r​
d​y​l​d​:​ ​l​o​a​d​e​d​:​ ​/​u​s​r​/​l​i​b​/​l​i​b​S​y​s​t​e​m​.​B​.​d​y​l​i​b​
d​y​l​d​:​ ​l​o​a​d​e​d​:​ ​/​u​s​r​/​l​i​b​/​s​y​s​t​e​m​/​l​i​b​m​a​t​h​C​o​m​m​o​n​.​A​.​d​y​l​i​b​
i​ ​i​s​ ​5​
i​ ​a​f​t​e​r​ ​a​d​d​_​1​:​ ​6​
i​ ​a​f​t​e​r​ ​a​d​d​_​4​:​ ​1​0​

You can see the Adder framework being loaded in the beginning.

Libraries or Frameworks?

So, should you be turning all of your common code into frameworks? In general, no. Frameworks are great if you are supplying libraries to third parties for them to be used as-is. Your users can include the frameworks in their application bundles and happily use your stuff.

Where you get into trouble is following along the line of thought “I have a suite of apps, so I’ll make a bunch of shared frameworks that all of them can use and put them up in /Library/Frameworks.” Along this path lies madness. You need administrator privileges to install and update the frameworks. You will need to use an install package, or else escalate privilege, to install your frameworks.

Once you have your shared frameworks installed, you need to worry about versioning issues. Even though frameworks have some versioning abilities, tool support for creating versioned frameworks and linking to particular versions just isn’t there. So you will end up releasing your suite of apps in lock-step, perhaps releasing some apps before they’re fully baked, and delaying releases of other apps until the rest of the suite catches up.

You will avoid the worst of these problems if you always include your frameworks into your application’s bundle. That does mean that common code and resources will not be shared amongst multiple applications, but in today’s age of huge disks and huge tracts of RAM this is less of an issue. Many shops either use static libraries or just include the necessary source files into their projects. The code will find its resources relative to the main bundle.

Also be aware that you cannot use your own frameworks on iOS. You can only statically link in code, whether via static libraries or including the source directly into your application project.

Writing Plug-ins

Shared libraries can be loaded on demand after the program has started running. They are the mechanism used to add plug-in features: build a shared library and have the program load it. Generic Unix applications can use the dyld functions to load shared libraries and get the addresses of symbols (including function pointers to executable code). Higher-level applications can load bundles at runtime. Cocoa can use the NSBundle class.

Bundles in Cocoa

A bundle is a directory containing some executable code, whether it’s a shared library or an executable program, and the various resources that support the code. Cocoa applications are bundles, as are frameworks, screensavers, iPhoto export plug-ins, and a lot of other stuff. Bundles are how Cocoa handles plug-ins that are loaded after the program has launched. Some programs, like QuickSilver, have a tiny main program, and all of the functionality is implemented via plug-ins.

There are two sides to making an application accept plug-ins: the application doing the loading and the plug-in itself. You need to make each of them agree to some kind of protocol to communicate with each other. There is not any pre-defined protocol for doing this, so you are free to use whatever mechanism you care to. Here is a little command-line tool example that will load plug-ins that return a string, which the main program will print out. Once all the plug-ins have had a chance to print out their stuff, the program exits.

Create three projects in Xcode. First, create a Command Line Tool (or Foundation Tool in Xcode 3) called BundlePrinter. Then create a Cocoa Bundle called SimpleMessage, and another Cocoa Bundle called ComplexMessage.

Add a header file to BundlePrinter that looks like this:

Example 6.4. BundlePrinter.h

/​/​ ​B​u​n​d​l​e​P​r​i​n​t​e​r​.​h​ ​-​-​ ​p​r​o​t​o​c​o​l​ ​f​o​r​ ​B​u​n​d​l​e​P​r​i​n​t​e​r​ ​p​l​u​g​i​n​s​ ​t​o​ ​u​s​e​

@​p​r​o​t​o​c​o​l​ ​B​u​n​d​l​e​P​r​i​n​t​e​r​P​r​o​t​o​c​o​l​

+​ ​(​B​O​O​L​)​a​c​t​i​v​a​t​e​;​
+​ ​(​v​o​i​d​)​d​e​a​c​t​i​v​a​t​e​;​

-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​m​e​s​s​a​g​e​;​

@​e​n​d​ ​/​/​ ​B​u​n​d​l​e​P​r​i​n​t​e​r​P​r​o​t​o​c​o​l​

This is very simple protocol. The class is given an opportunity to do stuff when activated and deactivated. Perhaps the plug-in needs to create and destroy a big image or load an mp3 file. Then there is an instance method for getting a string message to print.

Add this header file to the two bundle projects. Now go ahead and implement the plug-ins to conform to this protocol. Add a SimpleMessage.m file to the SimpleMessage project. Also set the Principal Class for the project to SimpleMessage. Go to the Targets pane, select SimpleMessage target, select the Info tab, and add a Principal Class with the string value SimpleMessage.

Here is what SimpleMessage.m looks like:

Example 6.5. SimpleMessage.m

/​/​ ​S​i​m​p​l​e​M​e​s​s​a​g​e​.​m​ ​-​-​ ​a​ ​s​i​m​p​l​e​ ​p​l​u​g​-​i​n​ ​t​h​a​t​ ​r​e​t​u​r​n​s​ ​a​ ​s​i​m​p​l​e​,​ ​h​a​r​d​-​c​o​d​e​d​ ​m​e​s​s​a​g​e​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​
#​i​m​p​o​r​t​ ​"​B​u​n​d​l​e​P​r​i​n​t​e​r​.​h​"​

@​i​n​t​e​r​f​a​c​e​ ​S​i​m​p​l​e​M​e​s​s​a​g​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​<​B​u​n​d​l​e​P​r​i​n​t​e​r​P​r​o​t​o​c​o​l​>​
@​e​n​d​ ​/​/​ ​S​i​m​p​l​e​M​e​s​s​a​g​e​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​S​i​m​p​l​e​M​e​s​s​a​g​e​

+​ ​(​B​O​O​L​)​ ​a​c​t​i​v​a​t​e​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​S​i​m​p​l​e​M​e​s​s​a​g​e​ ​p​l​u​g​-​i​n​ ​a​c​t​i​v​a​t​e​d​"​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​Y​E​S​;​
}​ ​/​/​ ​a​c​t​i​v​a​t​e​

+​ ​(​v​o​i​d​)​ ​d​e​a​c​t​i​v​a​t​e​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​S​i​m​p​l​e​M​e​s​s​a​g​e​ ​p​l​u​g​-​i​n​ ​d​e​a​c​t​i​v​a​t​e​d​"​)​;​
}​ ​/​/​ ​d​e​a​c​t​i​v​a​t​e​

-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​m​e​s​s​a​g​e​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​(​@​"​T​h​i​s​ ​i​s​ ​a​ ​S​i​m​p​l​e​ ​M​e​s​s​a​g​e​"​)​;​
}​ ​/​/​ ​m​e​s​s​a​g​e​

@​e​n​d​ ​/​/​ ​S​i​m​p​l​e​M​e​s​s​a​g​e​

Build it and fix any errors.

Do the same thing for ComplexMessage (set the ComplexMessage Principal class, and add the ComplexMessage.m source file).

Example 6.6. ComplexMessage.m

/​/​ ​C​o​m​p​l​e​x​M​e​s​s​a​g​e​ ​-​-​ ​a​ ​p​l​u​g​-​i​n​ ​t​h​a​t​ ​r​e​t​u​r​n​s​ ​a​ ​m​e​s​s​a​g​e​ ​u​s​i​n​g​ ​s​o​m​e​ ​s​t​o​r​e​d​ ​s​t​a​t​e​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

#​i​m​p​o​r​t​ ​"​B​u​n​d​l​e​P​r​i​n​t​e​r​.​h​"​

#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​r​a​n​d​o​m​ ​n​u​m​b​e​r​ ​r​o​u​t​i​n​e​s​
#​i​m​p​o​r​t​ ​<​t​i​m​e​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​t​i​m​e​(​)​ ​t​o​ ​s​e​e​d​ ​t​h​e​ ​r​a​n​d​o​m​ ​g​e​n​e​r​a​t​o​r​

@​i​n​t​e​r​f​a​c​e​ ​C​o​m​p​l​e​x​M​e​s​s​a​g​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​<​B​u​n​d​l​e​P​r​i​n​t​e​r​P​r​o​t​o​c​o​l​>​ ​{​
 ​ ​ ​ ​N​S​U​I​n​t​e​g​e​r​ ​_​r​a​n​d​o​m​V​a​l​u​e​;​
}​
@​e​n​d​ ​/​/​ ​C​o​m​p​l​e​x​M​e​s​s​a​g​e​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​C​o​m​p​l​e​x​M​e​s​s​a​g​e​

+​ ​(​B​O​O​L​)​ ​a​c​t​i​v​a​t​e​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​C​o​m​p​l​e​x​M​e​s​s​a​g​e​ ​p​l​u​g​-​i​n​ ​a​c​t​i​v​a​t​e​d​"​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​Y​E​S​;​
}​ ​/​/​ ​a​c​t​i​v​a​t​e​

+​ ​(​v​o​i​d​)​ ​d​e​a​c​t​i​v​a​t​e​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​C​o​m​p​l​e​x​M​e​s​s​a​g​e​ ​p​l​u​g​-​i​n​ ​d​e​a​c​t​i​v​a​t​e​d​"​)​;​
}​ ​/​/​ ​d​e​a​c​t​i​v​a​t​e​

-​ ​(​i​d​)​ ​i​n​i​t​ ​{​
 ​ ​ ​ ​i​f​ ​(​(​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​r​a​n​d​o​m​ ​(​(​u​n​s​i​g​n​e​d​)​t​i​m​e​(​N​U​L​L​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​r​a​n​d​o​m​V​a​l​u​e​ ​=​ ​r​a​n​d​o​m​ ​(​)​ ​%​ ​5​0​0​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​

}​ ​/​/​ ​i​n​i​t​

-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​m​e​s​s​a​g​e​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​ ​@​"​H​e​r​e​ ​i​s​ ​a​ ​r​a​n​d​o​m​ ​n​u​m​b​e​r​:​ ​%​d​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​_​r​a​n​d​o​m​V​a​l​u​e​]​;​
}​ ​/​/​ ​m​e​s​s​a​g​e​e​

@​e​n​d​ ​/​/​ ​C​o​m​p​l​e​x​M​e​s​s​a​g​e​

This is more “complex” because it stores some state at initialization time and uses it later.

Build this and fix any errors.

Now, in the BundlePrinter project, edit main.m so that it looks like this:

Example 6.7. main.m

/​/​ ​m​a​i​n​.​m​ ​-​-​ ​t​h​e​ ​m​a​i​n​ ​B​u​n​d​l​e​P​r​i​n​t​e​r​ ​p​r​o​g​r​a​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​
#​i​m​p​o​r​t​ ​"​B​u​n​d​l​e​P​r​i​n​t​e​r​.​h​"​

N​S​S​t​r​i​n​g​ ​*​p​r​o​c​e​s​s​P​l​u​g​i​n​ ​(​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​)​ ​{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​m​e​s​s​a​g​e​ ​=​ ​n​i​l​;​

 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​p​r​o​c​e​s​s​i​n​g​ ​p​l​u​g​-​i​n​:​ ​%​@​"​,​ ​p​a​t​h​)​;​

 ​ ​ ​ ​/​/​ ​L​o​a​d​ ​t​h​e​ ​b​u​n​d​l​e​
 ​ ​ ​ ​N​S​B​u​n​d​l​e​ ​*​p​l​u​g​i​n​ ​=​ ​[​N​S​B​u​n​d​l​e​ ​b​u​n​d​l​e​W​i​t​h​P​a​t​h​:​ ​p​a​t​h​]​;​

 ​ ​ ​ ​i​f​ ​(​p​l​u​g​i​n​ ​=​=​ ​n​i​l​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​o​u​l​d​ ​n​o​t​ ​l​o​a​d​ ​p​l​u​g​-​i​n​ ​a​t​ ​p​a​t​h​ ​%​@​"​,​ ​p​a​t​h​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​c​l​a​s​s​ ​t​h​e​ ​b​u​n​d​l​e​ ​d​e​c​l​a​r​e​s​ ​a​s​ ​i​t​s​ ​P​r​i​n​c​i​p​a​l​ ​o​n​e​.​
 ​ ​ ​ ​/​/​ ​I​f​ ​t​h​e​r​e​ ​a​r​e​ ​m​u​l​t​i​p​l​e​ ​c​l​a​s​s​e​s​ ​d​e​f​i​n​e​d​ ​i​n​ ​t​h​e​ ​b​u​n​d​l​e​,​ ​w​e​
 ​ ​ ​ ​/​/​ ​w​o​u​l​d​n​'​t​ ​k​n​o​w​ ​w​h​i​c​h​ ​o​n​e​ ​t​o​ ​t​a​l​k​ ​t​o​ ​f​i​r​s​t​
 ​ ​ ​ ​C​l​a​s​s​ ​p​r​i​n​c​i​p​a​l​C​l​a​s​s​ ​=​ ​[​p​l​u​g​i​n​ ​p​r​i​n​c​i​p​a​l​C​l​a​s​s​]​;​

 ​ ​ ​ ​i​f​ ​(​p​r​i​n​c​i​p​a​l​C​l​a​s​s​ ​=​=​ ​n​i​l​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​o​u​l​d​ ​n​o​t​ ​l​o​a​d​ ​p​r​i​n​c​i​p​a​l​ ​c​l​a​s​s​ ​f​o​r​ ​p​l​u​g​-​i​n​ ​a​t​ ​p​a​t​h​ ​%​@​"​,​ ​p​a​t​h​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​m​a​k​e​ ​s​u​r​e​ ​t​h​e​ ​P​r​i​n​c​i​p​a​l​C​l​a​s​s​ ​t​a​r​g​e​t​ ​s​e​t​t​i​n​g​ ​i​s​ ​c​o​r​r​e​c​t​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​D​o​ ​a​ ​l​i​t​t​l​e​ ​s​a​n​i​t​y​ ​c​h​e​c​k​i​n​g​
 ​ ​ ​ ​i​f​ ​(​!​[​p​r​i​n​c​i​p​a​l​C​l​a​s​s​ ​c​o​n​f​o​r​m​s​T​o​P​r​o​t​o​c​o​l​:​ ​@​p​r​o​t​o​c​o​l​(​B​u​n​d​l​e​P​r​i​n​t​e​r​P​r​o​t​o​c​o​l​)​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​p​l​u​g​-​i​n​ ​p​r​i​n​c​i​p​a​l​ ​c​l​a​s​s​ ​m​u​s​t​ ​c​o​n​f​o​r​m​ ​t​o​ ​t​h​e​ ​B​u​n​d​l​e​P​r​i​n​t​e​r​P​r​o​t​o​c​o​l​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​t​e​l​l​ ​t​h​e​ ​p​l​u​g​-​i​n​ ​t​h​a​t​ ​i​t​'​s​ ​b​e​i​n​g​ ​a​c​t​i​v​a​t​e​d​
 ​ ​ ​ ​i​f​ ​(​!​[​p​r​i​n​c​i​p​a​l​C​l​a​s​s​ ​a​c​t​i​v​a​t​e​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​o​u​l​d​ ​n​o​t​ ​a​c​t​i​v​a​t​e​ ​c​l​a​s​s​ ​f​o​r​ ​p​l​u​g​-​i​n​ ​a​t​ ​p​a​t​h​ ​%​@​"​,​ ​p​a​t​h​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​m​a​k​e​ ​a​n​ ​i​n​s​t​a​n​c​e​ ​o​f​ ​t​h​e​ ​p​l​u​g​-​i​n​ ​a​n​d​ ​a​s​k​ ​i​t​ ​f​o​r​ ​a​ ​m​e​s​s​a​g​e​
 ​ ​ ​ ​i​d​ ​p​l​u​g​i​n​I​n​s​t​a​n​c​e​ ​=​ ​[​[​p​r​i​n​c​i​p​a​l​C​l​a​s​s​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​/​/​ ​g​e​t​ ​t​h​e​ ​m​e​s​s​a​g​e​ ​a​n​d​ ​d​i​s​p​o​s​e​ ​w​i​t​h​ ​t​h​e​ ​i​n​s​t​a​n​c​e​
 ​ ​ ​ ​m​e​s​s​a​g​e​ ​=​ ​[​p​l​u​g​i​n​I​n​s​t​a​n​c​e​ ​m​e​s​s​a​g​e​]​;​
 ​ ​ ​ ​[​p​l​u​g​i​n​I​n​s​t​a​n​c​e​ ​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​/​/​ ​o​k​,​ ​w​e​'​r​e​ ​d​o​n​e​ ​w​i​t​h​ ​i​t​
 ​ ​ ​ ​[​p​r​i​n​c​i​p​a​l​C​l​a​s​s​ ​d​e​a​c​t​i​v​a​t​e​]​;​

 ​b​a​i​l​o​u​t​:​
 ​ ​ ​ ​r​e​t​u​r​n​ ​m​e​s​s​a​g​e​;​

}​ ​/​/​ ​p​r​o​c​e​s​s​P​l​u​g​i​n​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​/​/​ ​W​a​l​k​ ​t​h​e​ ​c​u​r​r​e​n​t​ ​d​i​r​e​c​t​o​r​y​ ​l​o​o​k​i​n​g​ ​f​o​r​ ​b​u​n​d​l​e​s​.​
 ​ ​ ​ ​/​/​ ​A​n​ ​a​p​p​l​i​c​a​t​i​o​n​ ​w​o​u​l​d​ ​l​o​o​k​ ​i​n​ ​i​t​s​ ​b​u​n​d​l​e​,​ ​o​r​ ​m​a​y​b​e​ ​a​ ​p​l​u​g​i​n​
 ​ ​ ​ ​/​/​ ​d​i​r​e​c​t​o​r​y​ ​i​n​ ​~​/​L​i​b​r​a​r​y​.​
 ​ ​ ​ ​N​S​F​i​l​e​M​a​n​a​g​e​r​ ​*​m​a​n​a​g​e​r​ ​=​ ​[​N​S​F​i​l​e​M​a​n​a​g​e​r​ ​d​e​f​a​u​l​t​M​a​n​a​g​e​r​]​;​

 ​ ​ ​ ​f​o​r​ ​(​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​ ​i​n​ ​[​m​a​n​a​g​e​r​ ​e​n​u​m​e​r​a​t​o​r​A​t​P​a​t​h​:​ ​@​"​.​"​]​)​ ​{​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​O​n​l​y​ ​l​o​o​k​ ​f​o​r​ ​s​t​u​f​f​ ​t​h​a​t​ ​h​a​s​ ​a​ ​.​b​u​n​d​l​e​ ​e​x​t​e​n​s​i​o​n​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​[​[​p​a​t​h​ ​p​a​t​h​E​x​t​e​n​s​i​o​n​]​ ​i​s​E​q​u​a​l​T​o​S​t​r​i​n​g​:​ ​@​"​b​u​n​d​l​e​"​]​)​ ​{​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​I​n​v​o​k​e​ ​t​h​e​ ​p​l​u​g​i​n​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​m​e​s​s​a​g​e​ ​=​ ​p​r​o​c​e​s​s​P​l​u​g​i​n​ ​(​p​a​t​h​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​m​e​s​s​a​g​e​ ​!​=​ ​n​i​l​)​ ​{​ ​/​/​ ​p​l​u​g​i​n​ ​s​u​c​c​e​e​d​e​d​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​n​m​e​s​s​a​g​e​ ​i​s​:​ ​'​%​s​'​\​n​\​n​"​,​ ​[​m​e​s​s​a​g​e​ ​U​T​F​8​S​t​r​i​n​g​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

main() looks in the directory you invoke the program from and searches for entries that end in .bundle. If it finds one, it attempts to load it and invoke methods on the class it finds there.

Here is a sample run after copying (or symlinking) the SimpleMessage and ComplexMessage bundles into a directory and running the program. In this case, copy them to the BundlePrinter project directory.

$​ ​b​u​i​l​d​/​D​e​b​u​g​/​B​u​n​d​l​e​P​r​i​n​t​e​r​
.​.​.​ ​B​u​n​d​l​e​P​r​i​n​t​e​r​[​4​4​5​8​]​ ​p​r​o​c​e​s​s​i​n​g​ ​p​l​u​g​-​i​n​:​ ​C​o​m​p​l​e​x​M​e​s​s​a​g​e​.​b​u​n​d​l​e​
.​.​.​ ​B​u​n​d​l​e​P​r​i​n​t​e​r​[​4​4​5​8​]​ ​C​o​m​p​l​e​x​M​e​s​s​a​g​e​ ​p​l​u​g​-​i​n​ ​a​c​t​i​v​a​t​e​d​
.​.​.​ ​B​u​n​d​l​e​P​r​i​n​t​e​r​[​4​4​5​8​]​ ​C​o​m​p​l​e​x​M​e​s​s​a​g​e​ ​p​l​u​g​-​i​n​ ​d​e​a​c​t​i​v​a​t​e​d​

m​e​s​s​a​g​e​ ​i​s​:​ ​'​H​e​r​e​ ​i​s​ ​a​ ​r​a​n​d​o​m​ ​n​u​m​b​e​r​:​ ​2​3​4​2​'​

.​.​.​ ​B​u​n​d​l​e​P​r​i​n​t​e​r​[​4​4​5​8​]​ ​p​r​o​c​e​s​s​i​n​g​ ​p​l​u​g​-​i​n​:​ ​S​i​m​p​l​e​M​e​s​s​a​g​e​.​b​u​n​d​l​e​
.​.​.​ ​B​u​n​d​l​e​P​r​i​n​t​e​r​[​4​4​5​8​]​ ​S​i​m​p​l​e​M​e​s​s​a​g​e​ ​p​l​u​g​-​i​n​ ​l​o​a​d​e​d​
.​.​.​ ​B​u​n​d​l​e​P​r​i​n​t​e​r​[​4​4​5​8​]​ ​S​i​m​p​l​e​M​e​s​s​a​g​e​ ​p​l​u​g​-​i​n​ ​u​n​l​o​a​d​e​d​

m​e​s​s​a​g​e​ ​i​s​:​ ​'​T​h​i​s​ ​i​s​ ​a​ ​S​i​m​p​l​e​ ​M​e​s​s​a​g​e​'​

There are a couple of limitations that are glossed over here. One is that you cannot load the same bundle twice. You will get a message from the Objective-C runtime about duplicate classes. The other is you cannot unload an Objective-C bundle. The Objective-C runtime gets its claws into the shared library and refuses to let go.

Shared Libraries and dlopen

If you don’t want to use (or can’t use) NSBundle for loading plug-ins, you can load a shared library instead. Loading a shared library does not give you ready access to resources like NSBundle does.

Mac OS X has a choice of API for manually loading bundles and shared libraries. There is the NSModule API, which includes functions such as NSCreateObjectFileImageFromFile(). Prior editions of this book described this mechanism. It has been deprecated in Mac OS X 10.5, and the documentation manpages have been removed. You can find information in the header located at /usr/include/mach-o/dyld.h.

dlopen()

dlopen() is used to open a shared library or a bundle at a given path:

void* dlopen (const char* path, int mode);

dlopen() opens the library and resolves any symbols it contains. It returns a module handle (an opaque pointer) to the library, which can be used in subsequent calls, or it returns NULL if the library could not be opened. The function dlerror() returns a string describing the error.

The path can be a full path or a relative path. If the path is just a leaf name (having no slash in the path, just a library name), dlopen() uses this algorithm to find the library:

The LD_LIBRARY_PATH environment variable (a colon-separated list of directories) is consulted, and the directories are searched (in order) to find the library.

If the DYLD_LIBRARY_PATH environment variable is set, those directories are searched for the leaf.

If the DYLD_FALLBACK_LIBRARY_PATH environment variable is set, those directories are searched. If this environment variable is not set, $HOME/lib, /usr/local/lib, and then /usr/lib are searched.

Finally, the path is treated like a regular path.

Because Mac OS X has fat binary files, there are no separate 32-bit and 64-bit search paths.

The mode parameter can take one of two options that control the binding of external functions, along with two optional flags. Here, “external” refers to functions not defined in the shared library that was just loaded:

RTLD_NOW | Each external function is bound immediately. You can use this flag to make sure that any undefined symbols are discovered at load-time.
RTLD_LAZY | Each external function is bound the first time it is called. You will usually use this form. It is more efficient because it doesn’t bind functions that might never be called.
RTLD_GLOBAL | This is an optional bitwise-OR-in flag. Symbols exported from the loaded library will be available to any other libraries that are loaded, in addition to being available through calls to dlsym(). This is the default behavior.
RTLD_LOCAL | This is also an optional bitwise-OR-in flag. Symbols exported from the loaded library are generally hidden and only available to dlsym() when using the module handle for this library.

dlsym()

Once you have a module handle from dlopen(), you can use dlsym() to get the address of code or data at the location specified by a given symbol.

void* dlsym (void *module, const char *symbol);

module is a module handle returned by dlopen(). Unlike the dyld family of calls, you do not prepend the symbol with an underscore. If the symbol cannot be found, dlsym() returns NULL, and you can query dlerror() to see what the problem was.

There are two constants you can give to dlsym() instead of a module handle: RTLD_DEFAULT to search through every Mach-O image in the process in the order they were loaded. This can be an expensive operation since it will have to slog through all of the system frameworks. You can also use RTLD_NEXT to search for the symbol in any Mach-O images that were loaded after the one calling dlsym(). So, if you call dlsym() in your main program using RTLD_NEXT, it will look in any libraries loaded after your program started running.

BundlePrinter

bundleprinter uses bundle plug-ins similar to the Cocoa-based BundlePrinter. You can compile a shared library like

$​ ​g​c​c​ ​-​g​ ​-​o​ ​s​i​m​p​l​e​m​e​s​s​a​g​e​.​m​s​g​ ​-​b​u​n​d​l​e​ ​s​i​m​p​l​e​m​e​s​s​a​g​e​.​m​

You can also build it as a shared library:

$​ ​g​c​c​ ​-​g​ ​-​o​ ​s​i​m​p​l​e​m​e​s​s​a​g​e​.​m​s​g​ ​-​d​y​l​i​b​ ​s​i​m​p​l​e​m​e​s​s​a​g​e​.​m​

So what’s the difference between a bundle and a shared library on Mac OS X? Practically, not a huge difference. dlopen() can load both of them and dig around the symbols inside. Bundles can be unloaded, while shared libraries cannot. Bundles that use Objective-C have to be unloaded in strict first-in, last-out order, which effectively makes them unloadable. Bundles can also be linked against executables to satisfy missing symbols. This allows a host application to provide an API that the plug-in can use.

.msg is the file extension used for these “message” plug-ins. Example 6.8 is the driver program, while Example 6.9 and Example 6.10 are the plug-in implementations.

Example 6.8. bundleprinter.m

/​/​ ​b​u​n​d​l​e​p​r​i​n​t​e​r​.​m​ ​-​-​ ​d​y​n​a​m​i​c​a​l​l​y​ ​l​o​a​d​ ​p​l​u​g​i​n​s​ ​a​n​d​ ​i​n​v​o​k​e​ ​f​u​n​c​t​i​o​n​s​ ​o​n​ ​t​h​e​m​,​ ​u​s​i​n​g​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​h​e​ ​d​l​o​p​e​n​(​)​ ​f​a​m​i​l​y​ ​o​f​ ​c​a​l​l​s​.​

/​/​ ​g​c​c​ ​-​g​ ​-​o​ ​b​u​n​d​l​e​p​r​i​n​t​e​r​ ​b​u​n​d​l​e​p​r​i​n​t​e​r​.​m​

#​i​m​p​o​r​t​ ​<​s​y​s​/​d​i​r​e​n​t​.​h​>​ ​ ​/​/​ ​f​o​r​ ​s​t​r​u​c​t​ ​d​i​r​e​n​t​
#​i​m​p​o​r​t​ ​<​d​i​r​e​n​t​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​o​p​e​n​d​i​r​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​d​l​f​c​n​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​d​l​o​p​e​n​(​)​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​/​s​t​r​e​r​r​o​r​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​d​u​p​

/​/​ ​W​e​ ​n​e​e​d​ ​a​ ​t​y​p​e​ ​t​o​ ​c​o​e​r​c​e​ ​a​ ​v​o​i​d​ ​p​o​i​n​t​e​r​ ​t​o​ ​t​h​e​ ​f​u​n​c​t​i​o​n​ ​p​o​i​n​t​e​r​ ​w​e​
/​/​ ​n​e​e​d​ ​t​o​ ​j​u​m​p​ ​t​h​r​o​u​g​h​.​ ​ ​H​a​v​i​n​g​ ​a​ ​t​y​p​e​ ​m​a​k​e​s​ ​t​h​i​n​g​s​ ​a​ ​b​i​t​ ​e​a​s​i​e​r​
/​/​ ​t​o​ ​r​e​a​d​ ​r​a​t​h​e​r​ ​t​h​a​n​ ​d​o​i​n​g​ ​t​h​i​s​ ​i​n​l​i​n​e​.​

t​y​p​e​d​e​f​ ​i​n​t​ ​(​*​B​N​R​M​e​s​s​a​g​e​A​c​t​i​v​a​t​e​F​P​)​ ​(​v​o​i​d​)​;​
t​y​p​e​d​e​f​ ​v​o​i​d​ ​(​*​B​N​R​M​e​s​s​a​g​e​D​e​a​c​t​i​v​a​t​e​F​P​)​ ​(​v​o​i​d​)​;​
t​y​p​e​d​e​f​ ​c​h​a​r​ ​*​ ​(​*​B​N​R​M​e​s​s​a​g​e​M​e​s​s​a​g​e​F​P​)​ ​(​v​o​i​d​)​;​

c​h​a​r​ ​*​p​r​o​c​e​s​s​P​l​u​g​i​n​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​p​a​t​h​)​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​*​m​e​s​s​a​g​e​ ​=​ ​N​U​L​L​;​

 ​ ​ ​ ​v​o​i​d​ ​*​m​o​d​u​l​e​ ​=​ ​d​l​o​p​e​n​ ​(​p​a​t​h​,​ ​R​T​L​D​_​L​A​Z​Y​)​;​

 ​ ​ ​ ​i​f​ ​(​m​o​d​u​l​e​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​c​o​u​l​d​n​'​t​ ​l​o​a​d​ ​p​l​u​g​i​n​ ​a​t​ ​p​a​t​h​ ​%​s​.​ ​ ​e​r​r​o​r​ ​i​s​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​a​t​h​,​ ​d​l​e​r​r​o​r​(​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​B​N​R​M​e​s​s​a​g​e​A​c​t​i​v​a​t​e​F​P​ ​a​c​t​i​v​a​t​o​r​ ​=​ ​d​l​s​y​m​ ​(​m​o​d​u​l​e​,​ ​"​B​N​R​M​e​s​s​a​g​e​A​c​t​i​v​a​t​e​"​)​;​
 ​ ​ ​ ​B​N​R​M​e​s​s​a​g​e​D​e​a​c​t​i​v​a​t​e​F​P​ ​d​e​a​c​t​i​v​a​t​o​r​ ​=​ ​d​l​s​y​m​ ​(​m​o​d​u​l​e​,​ ​"​B​N​R​M​e​s​s​a​g​e​D​e​a​c​t​i​v​a​t​e​"​)​;​
 ​ ​ ​ ​B​N​R​M​e​s​s​a​g​e​M​e​s​s​a​g​e​F​P​ ​m​e​s​s​a​g​a​t​o​r​ ​=​ ​d​l​s​y​m​ ​(​m​o​d​u​l​e​,​ ​"​B​N​R​M​e​s​s​a​g​e​M​e​s​s​a​g​e​"​)​;​

 ​ ​ ​ ​i​f​ ​(​a​c​t​i​v​a​t​o​r​ ​=​=​ ​N​U​L​L​ ​|​|​ ​d​e​a​c​t​i​v​a​t​o​r​ ​=​=​ ​N​U​L​L​ ​|​|​ ​m​e​s​s​a​g​a​t​o​r​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​c​o​u​l​d​ ​n​o​t​ ​f​i​n​d​ ​B​N​R​M​e​s​s​a​g​e​*​ ​s​y​m​b​o​l​ ​(​%​p​ ​%​p​ ​%​p​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​c​t​i​v​a​t​o​r​,​ ​d​e​a​c​t​i​v​a​t​o​r​,​ ​m​e​s​s​a​g​a​t​o​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​(​a​c​t​i​v​a​t​o​r​)​(​)​;​
 ​ ​ ​ ​i​f​ ​(​!​r​e​s​u​l​t​)​ ​{​ ​/​/​ ​t​h​e​ ​m​o​d​u​l​e​ ​d​i​d​n​'​t​ ​c​o​n​s​i​d​e​r​ ​i​t​s​e​l​f​ ​l​o​a​d​e​d​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​m​e​s​s​a​g​e​ ​=​ ​(​m​e​s​s​a​g​a​t​o​r​)​(​)​;​

 ​ ​ ​ ​(​d​e​a​c​t​i​v​a​t​o​r​)​(​)​;​

 ​ ​b​a​i​l​o​u​t​:​
 ​ ​ ​ ​i​f​ ​(​m​o​d​u​l​e​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​(​d​l​c​l​o​s​e​ ​(​m​o​d​u​l​e​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​!​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​d​l​c​l​o​s​e​ ​%​s​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​a​t​h​,​ ​d​l​e​r​r​o​r​(​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​m​e​s​s​a​g​e​;​

}​ ​/​/​ ​p​r​o​c​e​s​s​P​l​u​g​i​n​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​/​/​ ​w​a​l​k​ ​t​h​r​o​u​g​h​ ​t​h​e​ ​c​u​r​r​e​n​t​ ​d​i​r​e​c​t​o​r​y​

 ​ ​ ​ ​D​I​R​ ​*​d​i​r​e​c​t​o​r​y​ ​=​ ​o​p​e​n​d​i​r​ ​(​"​.​"​)​;​

 ​ ​ ​ ​i​f​ ​(​d​i​r​e​c​t​o​r​y​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​c​o​u​l​d​ ​n​o​t​ ​o​p​e​n​ ​c​u​r​r​e​n​t​ ​d​i​r​e​c​t​o​r​y​ ​t​o​ ​l​o​o​k​ ​f​o​r​ ​p​l​u​g​i​n​s​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​:​ ​%​d​ ​(​%​s​)​\​n​"​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​e​x​i​t​ ​(​E​X​I​T​_​F​A​I​L​U​R​E​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​s​t​r​u​c​t​ ​d​i​r​e​n​t​ ​*​e​n​t​r​y​;​
 ​ ​ ​ ​w​h​i​l​e​ ​(​(​e​n​t​r​y​ ​=​ ​r​e​a​d​d​i​r​(​d​i​r​e​c​t​o​r​y​)​)​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​I​f​ ​t​h​i​s​ ​i​s​ ​a​ ​f​i​l​e​ ​o​f​ ​t​y​p​e​ ​.​m​s​g​ ​(​a​n​ ​e​x​t​e​n​s​i​o​n​ ​m​a​d​e​ ​u​p​ ​f​o​r​ ​t​h​i​s​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​s​a​m​p​l​e​)​,​ ​p​r​o​c​e​s​s​ ​i​t​ ​l​i​k​e​ ​a​ ​p​l​u​g​-​i​n​.​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​f​n​m​a​t​c​h​(​"​*​.​m​s​g​"​,​ ​e​n​t​r​y​-​>​d​_​n​a​m​e​,​ ​0​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​*​m​e​s​s​a​g​e​ ​=​ ​p​r​o​c​e​s​s​P​l​u​g​i​n​ ​(​e​n​t​r​y​-​>​d​_​n​a​m​e​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​n​m​e​s​s​a​g​e​ ​i​s​:​ ​'​%​s​'​\​n​\​n​"​,​ ​m​e​s​s​a​g​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​r​e​e​ ​(​m​e​s​s​a​g​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​c​l​o​s​e​d​i​r​ ​(​d​i​r​e​c​t​o​r​y​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

Example 6.9. simplemessage.m

/​/​ ​s​i​m​p​l​e​m​e​s​s​a​g​e​.​m​ ​-​-​ ​r​e​t​u​r​n​ ​a​ ​m​a​l​l​o​c​'​d​ ​b​l​o​c​k​ ​o​f​ ​m​e​m​o​r​y​ ​t​o​ ​a​ ​s​i​m​p​l​e​ ​m​e​s​s​a​g​e​

/​/​ ​g​c​c​ ​-​g​ ​-​o​ ​s​i​m​p​l​e​m​e​s​s​a​g​e​.​m​s​g​ ​-​b​u​n​d​l​e​ ​s​i​m​p​l​e​m​e​s​s​a​g​e​.​m​

#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​d​u​p​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​

i​n​t​ ​B​N​R​M​e​s​s​a​g​e​A​c​t​i​v​a​t​e​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​s​i​m​p​l​e​ ​m​e​s​s​a​g​e​ ​a​c​t​i​v​a​t​e​\​n​"​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​1​;​
}​ ​/​/​ ​B​N​R​M​e​s​s​a​g​e​A​c​t​i​v​a​t​e​

v​o​i​d​ ​B​N​R​M​e​s​s​a​g​e​D​e​a​c​t​i​v​a​t​e​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​s​i​m​p​l​e​ ​m​e​s​s​a​g​e​ ​d​e​a​c​t​i​v​a​t​e​\​n​"​)​;​
}​ ​/​/​ ​B​N​R​M​e​s​s​a​g​e​D​e​a​c​t​i​v​a​t​e​

c​h​a​r​ ​*​B​N​R​M​e​s​s​a​g​e​M​e​s​s​a​g​e​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​(​s​t​r​d​u​p​(​"​T​h​i​s​ ​i​s​ ​a​ ​s​i​m​p​l​e​ ​m​e​s​s​a​g​e​"​)​)​;​
}​ ​/​/​ ​B​N​R​M​e​s​s​a​g​e​M​e​s​s​a​g​e​

Example 6.10. simplemessage.m

/​/​ ​c​o​m​p​l​e​x​m​e​s​s​a​g​e​.​m​ ​-​-​ ​r​e​t​u​r​n​ ​a​ ​m​a​l​l​o​c​'​d​ ​b​l​o​c​k​ ​o​f​ ​m​e​m​o​r​y​ ​t​o​ ​a​ ​c​o​m​p​l​e​x​ ​m​e​s​s​a​g​e​

/​/​ ​g​c​c​ ​-​g​ ​-​o​ ​c​o​m​p​l​e​x​m​e​s​s​a​g​e​.​m​s​g​ ​-​b​u​n​d​l​e​ ​c​o​m​p​l​e​x​m​e​s​s​a​g​e​.​m​

#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​r​a​n​d​o​m​ ​n​u​m​b​e​r​ ​r​o​u​t​i​n​e​s​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​d​u​p​,​ ​a​n​d​ ​s​n​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​t​i​m​e​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​t​i​m​e​(​)​ ​t​o​ ​s​e​e​d​ ​t​h​e​ ​r​a​n​d​o​m​ ​g​e​n​e​r​a​t​o​r​

s​t​a​t​i​c​ ​u​n​s​i​g​n​e​d​ ​g​_​r​a​n​d​o​m​V​a​l​u​e​;​

i​n​t​ ​B​N​R​M​e​s​s​a​g​e​A​c​t​i​v​a​t​e​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​c​o​m​p​l​e​x​ ​m​e​s​s​a​g​e​ ​a​c​t​i​v​a​t​e​\​n​"​)​;​

 ​ ​ ​ ​s​r​a​n​d​o​m​ ​(​(​u​n​s​i​g​n​e​d​)​t​i​m​e​(​N​U​L​L​)​)​;​
 ​ ​ ​ ​g​_​r​a​n​d​o​m​V​a​l​u​e​ ​=​ ​r​a​n​d​o​m​ ​(​)​ ​%​ ​5​0​0​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​1​;​

}​ ​/​/​ ​B​N​R​M​e​s​s​a​g​e​A​c​t​i​v​a​t​e​

v​o​i​d​ ​B​N​R​M​e​s​s​a​g​e​D​e​a​c​t​i​v​a​t​e​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​c​o​m​p​l​e​x​ ​m​e​s​s​a​g​e​ ​d​e​a​c​t​i​v​a​t​e​\​n​"​)​;​
}​ ​/​/​ ​B​N​R​M​e​s​s​a​g​e​D​e​a​c​t​i​v​a​t​e​

c​h​a​r​ ​*​B​N​R​M​e​s​s​a​g​e​M​e​s​s​a​g​e​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​*​m​e​s​s​a​g​e​;​
 ​ ​ ​ ​a​s​p​r​i​n​t​f​ ​(​&​m​e​s​s​a​g​e​,​ ​"​H​e​r​e​ ​i​s​ ​a​ ​r​a​n​d​o​m​ ​n​u​m​b​e​r​:​ ​%​d​"​,​ ​g​_​r​a​n​d​o​m​V​a​l​u​e​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​m​e​s​s​a​g​e​;​
}​ ​/​/​ ​B​N​R​M​e​s​s​a​g​e​M​e​s​s​a​g​e​

And here is a run:

$​ ​.​/​b​u​n​d​l​e​p​r​i​n​t​e​r​
c​o​m​p​l​e​x​ ​m​e​s​s​a​g​e​ ​a​c​t​i​v​a​t​e​
c​o​m​p​l​e​x​ ​m​e​s​s​a​g​e​ ​d​e​a​c​t​i​v​a​t​e​

m​e​s​s​a​g​e​ ​i​s​:​ ​'​H​e​r​e​ ​i​s​ ​a​ ​r​a​n​d​o​m​ ​n​u​m​b​e​r​:​ ​2​3​0​'​

s​i​m​p​l​e​ ​m​e​s​s​a​g​e​ ​a​c​t​i​v​a​t​e​
s​i​m​p​l​e​ ​m​e​s​s​a​g​e​ ​d​e​a​c​t​i​v​a​t​e​

m​e​s​s​a​g​e​ ​i​s​:​ ​'​T​h​i​s​ ​i​s​ ​a​ ​s​i​m​p​l​e​ ​m​e​s​s​a​g​e​'​

For the More Curious: libtool

Earlier in the chapter, the class Unix tools ar and ranlib were used to build static libraries, and gcc was used to build a dynamic library. Mac OS X has a tool, libtool, which is peculiar to the platform, but provides a superset of features over ar and friends. The GNU project also has a libtool, but it is unrelated to the one in Mac OS X.

Make a static library like this and use it:

$​ ​l​i​b​t​o​o​l​ ​-​s​t​a​t​i​c​ ​-​o​ ​l​i​b​a​d​d​u​m​.​a​ ​s​r​c​?​.​o​
$​ ​g​c​c​ ​-​g​ ​-​o​ ​u​s​e​a​d​d​ ​u​s​e​a​d​d​.​m​ ​-​L​.​ ​-​l​a​d​d​u​m​

And dynamic libraries (but not bundles) can be created thusly:

$​ ​l​i​b​t​o​o​l​ ​-​d​y​n​a​m​i​c​ ​-​m​a​c​o​s​x​_​v​e​r​s​i​o​n​_​m​i​n​ ​1​0​.​5​ ​-​o​ ​l​i​b​a​d​d​u​m​.​d​y​l​i​b​ ​s​r​c​?​.​o​
$​ ​g​c​c​ ​-​g​ ​-​o​ ​u​s​e​a​d​d​ ​u​s​e​a​d​d​.​m​ ​-​L​.​ ​-​l​a​d​d​u​m​

For the More Curious: otool

otool is another library-oriented tool. Even though it shares the last four letters with libtool, otool will output a lot of information about an object file, library, or executable. otool has a ton of options, so check out the manpage for the whole suite. Here are a couple of interesting commands.

List the names and version numbers of the shared libs an object file or executable uses:

$​ ​o​t​o​o​l​ ​-​L​ ​/​A​p​p​l​i​c​a​t​i​o​n​s​/​S​a​f​a​r​i​.​a​p​p​/​C​o​n​t​e​n​t​s​/​M​a​c​O​S​/​S​a​f​a​r​i​
/​A​p​p​l​i​c​a​t​i​o​n​s​/​S​a​f​a​r​i​.​a​p​p​/​C​o​n​t​e​n​t​s​/​M​a​c​O​S​/​S​a​f​a​r​i​:​
 ​ ​ ​ ​ ​/​u​s​r​/​l​i​b​/​l​i​b​s​q​l​i​t​e​3​.​d​y​l​i​b​ ​(​c​o​m​p​a​t​i​b​i​l​i​t​y​ ​v​e​r​s​i​o​n​ ​9​.​0​.​0​,​ ​c​u​r​r​e​n​t​ ​v​e​r​s​i​o​n​ ​9​.​6​.​0​)​
 ​ ​ ​ ​ ​/​S​y​s​t​e​m​/​L​i​b​r​a​r​y​/​F​r​a​m​e​w​o​r​k​s​/​P​u​b​S​u​b​.​f​r​a​m​e​w​o​r​k​/​V​e​r​s​i​o​n​s​/​A​/​P​u​b​S​u​b​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​c​o​m​p​a​t​i​b​i​l​i​t​y​ ​v​e​r​s​i​o​n​ ​1​.​0​.​0​,​ ​c​u​r​r​e​n​t​ ​v​e​r​s​i​o​n​ ​1​.​0​.​0​)​
 ​ ​ ​ ​ ​/​S​y​s​t​e​m​/​L​i​b​r​a​r​y​/​P​r​i​v​a​t​e​F​r​a​m​e​w​o​r​k​s​/​C​r​a​s​h​R​e​p​o​r​t​e​r​S​u​p​p​o​r​t​.​f​r​a​m​e​w​o​r​k​/​V​e​r​s​i​o​n​s​/​A​/​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​r​a​s​h​R​e​p​o​r​t​e​r​S​u​p​p​o​r​t​ ​(​c​o​m​p​a​t​i​b​i​l​i​t​y​ ​v​e​r​s​i​o​n​ ​1​.​0​.​0​,​ ​c​u​r​r​e​n​t​ ​v​e​r​s​i​o​n​ ​1​.​0​.​0​)​
 ​ ​ ​ ​ ​/​u​s​r​/​l​i​b​/​l​i​b​x​a​r​.​1​.​d​y​l​i​b​ ​(​c​o​m​p​a​t​i​b​i​l​i​t​y​ ​v​e​r​s​i​o​n​ ​1​.​0​.​0​,​ ​c​u​r​r​e​n​t​ ​v​e​r​s​i​o​n​ ​1​.​0​.​0​)​
 ​ ​ ​ ​ ​/​S​y​s​t​e​m​/​L​i​b​r​a​r​y​/​F​r​a​m​e​w​o​r​k​s​/​C​o​r​e​L​o​c​a​t​i​o​n​.​f​r​a​m​e​w​o​r​k​/​V​e​r​s​i​o​n​s​/​A​/​C​o​r​e​L​o​c​a​t​i​o​n​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​c​o​m​p​a​t​i​b​i​l​i​t​y​ ​v​e​r​s​i​o​n​ ​1​.​0​.​0​,​ ​c​u​r​r​e​n​t​ ​v​e​r​s​i​o​n​ ​1​1​.​0​.​0​)​
.​.​.​

Disassemble the contents of the text section. The -V indicates a symbolic disassembly.

$​ ​o​t​o​o​l​ ​-​V​ ​-​t​ ​u​s​e​a​d​d​
.​.​.​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​8​2​ ​ ​ ​ ​ ​ ​ ​ ​m​o​v​l​ ​ ​ ​ ​0​x​f​c​(​%​r​b​p​)​,​%​e​s​i​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​8​5​ ​ ​ ​ ​ ​ ​ ​ ​l​e​a​q​ ​ ​ ​ ​0​x​0​0​0​0​0​0​a​c​(​%​r​i​p​)​,​%​r​d​i​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​8​c​ ​ ​ ​ ​ ​ ​ ​ ​m​o​v​l​ ​ ​ ​ ​$​0​x​0​0​0​0​0​0​0​0​,​%​e​a​x​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​9​1​ ​ ​ ​ ​ ​ ​ ​ ​c​a​l​l​q​ ​ ​ ​0​x​1​0​0​0​0​0​e​f​a​ ​ ​ ​ ​ ​;​ ​s​y​m​b​o​l​ ​s​t​u​b​ ​f​o​r​:​ ​_​p​r​i​n​t​f​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​9​6​ ​ ​ ​ ​ ​ ​ ​ ​m​o​v​l​ ​ ​ ​ ​0​x​f​c​(​%​r​b​p​)​,​%​e​d​i​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​9​9​ ​ ​ ​ ​ ​ ​ ​ ​m​o​v​l​ ​ ​ ​ ​$​0​x​0​0​0​0​0​0​0​0​,​%​e​a​x​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​9​e​ ​ ​ ​ ​ ​ ​ ​ ​c​a​l​l​q​ ​ ​ ​0​x​1​0​0​0​0​0​e​e​8​ ​ ​ ​ ​ ​;​ ​s​y​m​b​o​l​ ​s​t​u​b​ ​f​o​r​:​ ​_​a​d​d​_​1​
0​0​0​0​0​0​0​1​0​0​0​0​0​e​a​3​ ​ ​ ​ ​ ​ ​ ​ ​m​o​v​l​ ​ ​ ​ ​%​e​a​x​,​0​x​f​c​(​%​r​b​p​)​
.​.​.​

Print out some Objective-C stuff:

$​ ​o​t​o​o​l​ ​-​o​v​ ​B​u​n​d​l​e​P​r​i​n​t​e​r​
C​o​n​t​e​n​t​s​ ​o​f​ ​(​_​_​D​A​T​A​,​_​_​o​b​j​c​_​c​l​a​s​s​r​e​f​s​)​ ​s​e​c​t​i​o​n​
0​0​0​0​0​0​0​1​0​0​0​0​2​1​7​8​ ​0​x​0​
0​0​0​0​0​0​0​1​0​0​0​0​2​1​8​0​ ​0​x​0​
0​0​0​0​0​0​0​1​0​0​0​0​2​1​8​8​ ​0​x​0​
C​o​n​t​e​n​t​s​ ​o​f​ ​(​_​_​D​A​T​A​,​_​_​o​b​j​c​_​p​r​o​t​o​l​i​s​t​)​ ​s​e​c​t​i​o​n​
0​0​0​0​0​0​0​1​0​0​0​0​2​0​6​0​ ​0​x​1​0​0​0​0​2​1​b​0​
C​o​n​t​e​n​t​s​ ​o​f​ ​(​_​_​D​A​T​A​,​_​_​o​b​j​c​_​m​s​g​r​e​f​s​)​ ​s​e​c​t​i​o​n​
 ​ ​i​m​p​ ​0​x​0​
 ​ ​s​e​l​ ​0​x​1​0​0​0​0​1​e​8​0​ ​a​l​l​o​c​
 ​ ​i​m​p​ ​0​x​0​
 ​ ​s​e​l​ ​0​x​1​0​0​0​0​1​e​8​b​ ​r​e​l​e​a​s​e​
 ​ ​i​m​p​ ​0​x​0​
 ​ ​s​e​l​ ​0​x​1​0​0​0​0​1​e​b​6​ ​c​o​u​n​t​B​y​E​n​u​m​e​r​a​t​i​n​g​W​i​t​h​S​t​a​t​e​:​o​b​j​e​c​t​s​:​c​o​u​n​t​:​
 ​ ​i​m​p​ ​0​x​0​
 ​ ​s​e​l​ ​0​x​1​0​0​0​0​1​e​f​6​ ​i​s​E​q​u​a​l​T​o​S​t​r​i​n​g​:​
C​o​n​t​e​n​t​s​ ​o​f​ ​(​_​_​D​A​T​A​,​_​_​o​b​j​c​_​i​m​a​g​e​i​n​f​o​)​ ​s​e​c​t​i​o​n​
 ​ ​v​e​r​s​i​o​n​ ​0​
 ​ ​ ​ ​f​l​a​g​s​ ​0​x​0​

Point it to something big like /Applications/Safari/Contents/MacOS/Safari to see a huge amount of output.

For the More Curious: Runtime Environment Variables

Earlier you saw several environment variables that could be set to achieve different results. There are a number of environment variables you can set. Here’s an interesting subset, and check out the dyld manpage for the complete list. When a list of things is mentioned, it is a colon-separated list of those objects.

The first set of environment variables control where the dynamic linker finds libraries and frameworks when a process is first loaded.

DYLD_FRAMEWORK_PATH

A list of directories that contain frameworks. The dynamic linker searches these directories first.

DYLD_FALLBACK_FRAMEWORK_PATH

A list of directories that contain frameworks. This is used as the default location for frameworks. By default, it includes the Frameworks directory in /Library, /Network, and /System/Library.

DYLD_LIBRARY_PATH

A list of directories that contain libraries. The dynamic linker searches these directories before it looks at the default location for the libraries. Handy for testing new versions of libraries.

DYLD_FALLBACK_LIBRARY_PATH

A list of directories that contain libraries. These are the default locations for libraries. By default, it is set to $(HOME)/lib:/usr/local/bin:/lib:/usr/lib

The second set causes the dynamic linker to emit output when interesting things happen. Some are good nerdy fun, and some can be useful when debugging or tracking down performance problems that happen before main() is called. The environment variables do not have to be set to any particular value, just so long as they are set.

DYLD_PRINT_STATISTICS

Prints out where the dynamic linker spent its time before main() is called.

DYLD_PRINT_INITIALIZERS

Prints when running each initializer in every image.

DYLD_PRINT_APIS

Prints when dynamic linker API is called, such as dlsym().

DYLD_PRINT_SEGMENTS

Print out a line with the name and address range of each Mach-O segment the dynamic linker maps in.

Here is a sample run, showing statistics and API calls with bundleprinter:

$​ ​e​x​p​o​r​t​ ​D​Y​L​D​_​P​R​I​N​T​_​A​P​I​S​=​"​"​
$​ ​e​x​p​o​r​t​ ​D​Y​L​D​_​P​R​I​N​T​_​S​T​A​T​I​S​T​I​C​S​=​"​"​
$​ ​.​/​b​u​n​d​l​e​p​r​i​n​t​e​r​
_​d​y​l​d​_​r​e​g​i​s​t​e​r​_​f​u​n​c​_​f​o​r​_​r​e​m​o​v​e​_​i​m​a​g​e​(​0​x​7​f​f​f​8​2​f​c​6​b​7​6​)​
t​o​t​a​l​ ​t​i​m​e​:​ ​0​.​5​3​ ​m​i​l​l​i​s​e​c​o​n​d​s​ ​(​1​0​0​.​0​%​)​
t​o​t​a​l​ ​i​m​a​g​e​s​ ​l​o​a​d​e​d​:​ ​ ​3​ ​(​2​ ​f​r​o​m​ ​d​y​l​d​ ​s​h​a​r​e​d​ ​c​a​c​h​e​,​ ​0​ ​n​e​e​d​e​d​ ​n​o​ ​f​i​x​u​p​s​)​
t​o​t​a​l​ ​s​e​g​m​e​n​t​s​ ​m​a​p​p​e​d​:​ ​0​,​ ​i​n​t​o​ ​0​ ​p​a​g​e​s​ ​w​i​t​h​ ​0​ ​p​a​g​e​s​ ​p​r​e​-​f​e​t​c​h​e​d​
t​o​t​a​l​ ​i​m​a​g​e​s​ ​l​o​a​d​i​n​g​ ​t​i​m​e​:​ ​0​.​0​3​ ​m​i​l​l​i​s​e​c​o​n​d​s​ ​(​6​.​0​%​)​
t​o​t​a​l​ ​d​t​r​a​c​e​ ​D​O​F​ ​r​e​g​i​s​t​r​a​t​i​o​n​ ​t​i​m​e​:​ ​0​.​0​2​ ​m​i​l​l​i​s​e​c​o​n​d​s​ ​(​5​.​5​%​)​
t​o​t​a​l​ ​r​e​b​a​s​e​ ​f​i​x​u​p​s​:​ ​ ​0​
t​o​t​a​l​ ​r​e​b​a​s​e​ ​f​i​x​u​p​s​ ​t​i​m​e​:​ ​0​.​0​0​ ​m​i​l​l​i​s​e​c​o​n​d​s​ ​(​0​.​7​%​)​
t​o​t​a​l​ ​b​i​n​d​i​n​g​ ​f​i​x​u​p​s​:​ ​2​
t​o​t​a​l​ ​b​i​n​d​i​n​g​ ​f​i​x​u​p​s​ ​t​i​m​e​:​ ​0​.​0​2​ ​m​i​l​l​i​s​e​c​o​n​d​s​ ​(​5​.​1​%​)​
t​o​t​a​l​ ​w​e​a​k​ ​b​i​n​d​i​n​g​ ​f​i​x​u​p​s​ ​t​i​m​e​:​ ​0​.​0​0​ ​m​i​l​l​i​s​e​c​o​n​d​s​ ​(​0​.​1​%​)​
t​o​t​a​l​ ​b​i​n​d​i​n​g​s​ ​l​a​z​i​l​y​ ​f​i​x​e​d​ ​u​p​:​ ​0​ ​o​f​ ​0​
t​o​t​a​l​ ​i​n​i​t​i​a​l​i​z​e​r​ ​t​i​m​e​:​ ​0​.​4​3​ ​m​i​l​l​i​s​e​c​o​n​d​s​ ​(​8​2​.​3​%​)​
t​o​t​a​l​ ​s​y​m​b​o​l​ ​t​r​i​e​ ​s​e​a​r​c​h​e​s​:​ ​ ​ ​ ​2​
t​o​t​a​l​ ​s​y​m​b​o​l​ ​t​a​b​l​e​ ​b​i​n​a​r​y​ ​s​e​a​r​c​h​e​s​:​ ​ ​ ​ ​0​
t​o​t​a​l​ ​i​m​a​g​e​s​ ​d​e​f​i​n​i​n​g​/​u​s​i​n​g​ ​w​e​a​k​ ​s​y​m​b​o​l​s​:​ ​ ​0​/​0​

d​l​o​p​e​n​(​c​o​m​p​l​e​x​m​e​s​s​a​g​e​.​m​s​g​,​ ​0​x​0​0​0​0​0​0​0​1​)​
d​l​s​y​m​(​0​x​1​0​0​1​0​0​2​6​0​,​ ​B​N​R​M​e​s​s​a​g​e​A​c​t​i​v​a​t​e​)​
d​l​s​y​m​(​0​x​1​0​0​1​0​0​2​6​0​,​ ​B​N​R​M​e​s​s​a​g​e​D​e​a​c​t​i​v​a​t​e​)​
d​l​s​y​m​(​0​x​1​0​0​1​0​0​2​6​0​,​ ​B​N​R​M​e​s​s​a​g​e​M​e​s​s​a​g​e​)​

c​o​m​p​l​e​x​ ​m​e​s​s​a​g​e​ ​a​c​t​i​v​a​t​e​
c​o​m​p​l​e​x​ ​m​e​s​s​a​g​e​ ​d​e​a​c​t​i​v​a​t​e​

d​l​c​l​o​s​e​(​0​x​1​0​0​1​0​0​2​6​0​)​

m​e​s​s​a​g​e​ ​i​s​:​ ​'​H​e​r​e​ ​i​s​ ​a​ ​r​a​n​d​o​m​ ​n​u​m​b​e​r​:​ ​9​9​'​

d​l​o​p​e​n​(​s​i​m​p​l​e​m​e​s​s​a​g​e​.​m​s​g​,​ ​0​x​0​0​0​0​0​0​0​1​)​

d​l​s​y​m​(​0​x​1​0​0​1​0​0​2​6​0​,​ ​B​N​R​M​e​s​s​a​g​e​A​c​t​i​v​a​t​e​)​
d​l​s​y​m​(​0​x​1​0​0​1​0​0​2​6​0​,​ ​B​N​R​M​e​s​s​a​g​e​D​e​a​c​t​i​v​a​t​e​)​
d​l​s​y​m​(​0​x​1​0​0​1​0​0​2​6​0​,​ ​B​N​R​M​e​s​s​a​g​e​M​e​s​s​a​g​e​)​

s​i​m​p​l​e​ ​m​e​s​s​a​g​e​ ​a​c​t​i​v​a​t​e​
s​i​m​p​l​e​ ​m​e​s​s​a​g​e​ ​d​e​a​c​t​i​v​a​t​e​

d​l​c​l​o​s​e​(​0​x​1​0​0​1​0​0​2​6​0​)​

m​e​s​s​a​g​e​ ​i​s​:​ ​'​T​h​i​s​ ​i​s​ ​a​ ​s​i​m​p​l​e​ ​m​e​s​s​a​g​e​'​

Exercises

Take the Cocoa plug-in example and include it in a GUI program, putting the plug-in name and the message into an NSTableView. Write some additional plug-ins.

otool has a lot of options and additional flags. Write a Cocoa front-end so you can easily play with the options. You can use NSTask, covered in Chapter 19: Using NSTask to actually run otool from your application.

7
Memory

Virtual Memory

Virtual memory is a way for the computer to fake having more memory than it actually has. A machine might have four gigabytes of RAM, but you can write programs that manipulate data several times that amount. When you overflow the available amount of real memory, portions of the data are saved out to disk and read back in when the program needs it.

The operating system handles the grungy details of keeping data that is currently being worked on physically in memory and moving data that has not been touched in a while out to the disk. It also pulls the data back into memory from disk if the program wants to work with it again.

The operating system divides memory into pages, 4KB chunks of memory that the operating system addresses. Programs are given pages as they request memory. As pages are used, they are kept on a list of recently used pages. As programs request more and more memory from the system, the least recently used pages are written to disk, called “paging” or “swapping,” and the chunk of physical memory is reused. iOS does not have this disk-based swapfile, so you will hit out-of-memory conditions sooner on that platform than with desktop Mac OS X.

Pages can be written out and then read back in at different physical addresses. A 4k page starting at address 0x5000 might be paged out and given to another program. The program that needs the data that was at 0x5000 now needs it again, so the OS reads the page from disk. The chunk of memory at 0x5000 may now be in use by the second program. Oops. To fix this, virtual addressing is used.

Figure 7.1 shows virtual memory in action. Virtual addresses are the memory addresses a program sees, and each program has its own address space. The virtual address gets mapped by the OS (and hardware in the CPU) to the physical address of a chunk of a page of RAM. Program A and program B each have a page of data at 0x8000. In physical memory, A’s might live at physical address 0x15020, and B’s might live at physical address 0x3150, but the address translation lets each program live with the fantasy of having their data at address 0x8000.

Figure 7.1  Virtual memory

The total amount of memory that a program has allocated to it at a particular time is called its virtual set. The amount of memory that is actually located in RAM is called its resident set. The difference between the virtual set and the resident set is stored out on disk in a swap file, or in the files of read-only code segments such as the system frameworks. “Swap” derives its name from the pages that are swapped for each other when paging happens. You can also lock, or wire, memory down so it doesn’t get swapped out. Some pages you absolutely don’t want to be swapped out, such as pages containing decrypted passwords. These pages should be wired down with the mlock() function before decrypting the password.

Pages can have permissions, like read-only, read/write, and execute. This helps keep you from scribbling over your own code and helps prevent security exploits that try to execute code from a page that is not marked executable.

Program Memory Model

Mac OS X processes have a conceptual memory model, where memory is divided into space for the executable code, a stack, a heap, and other bits and pieces, as shown in Figure 7.2:

Figure 7.2  Unix program memory model

Text Segment

The executable program code lives here. At program launch time, the code is mapped into memory from the executable on disk as read-only pages. Since these pages are read-only, they can be easily shared among multiple processes, so the pages only have to appear in physical memory once and can still be shared among multiple users. They can, of course, appear multiple times in the virtual memory of other processes. This is especially for shared libraries that are loaded into each program. Because the data is read-only, the kernel can recycle these pages without having to write them to the swapfile.

Initialized Data Segment

Initialized global and static variables live here – things like float pi = 3.1415 outside of any functions or static int blah = 25 inside of a function. The initialized data is stored in the data segment itself, which is just copied into memory into a read/write page that the program can then modify. Because the kernel is just reading in blocks of data from disk, it is very fast to load and initialize all of the globals. No real explicit initialization happens, just bulk data loads.

Uninitialized Data Segment

This is all the stuff that lives in global space but is not given an explicit initializer, like int bork outside of any functions or static char buffer[5000] inside of a function, and that all gets cleared to zero on program launch. These are not treated like the initialized data segment, which would mean lots of zero blocks in the executable. Only the size of this data segment is stored. On program load, the OS allocates that amount of space and zero-fills it.

This is also referred to as the “bss” segment in manpages and the historical literature. “bss” comes from an assembler instruction that means “block started by symbol.”

Heap

The heap is the area where dynamic (runtime) allocations happen. If you malloc() 40K, that 40K will come from the heap.

Program Stack

The program call stack for the main thread. Local (automatic) variables are stored here, as well as the stack frame for each function call. When a function calls another function, the processor’s registers and other assorted bookkeeping need to be stored before the new function is invoked. These values also need to be restored when the new function exits.

Memory “allocation” using the program stack is very fast. A processor register is used to indicate where the end of the stack is. Reserving space on the stack just involves adding a value to this

address, whether it be four bytes or four thousand. You do not want to store too much stuff on the stack, such as big buffers, because there is a limit on how big the stack can be. Also, threads have limited stack space because each thread has its own chunk of memory to use for a stack. This is discussed in Chapter 20: Multithreading.

Example 7.1 is a program that has 8K of initialized data and a megabyte of uninitialized data:

Example 7.1. dataseg.m

/​/​ ​d​a​t​a​s​e​g​.​m​ ​-​-​ ​s​h​o​w​ ​s​i​z​e​ ​o​f​ ​d​a​t​a​ ​s​e​g​m​e​n​t​s​

#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​/​/​ ​f​o​r​ ​e​x​i​t​(​)​

/​/​ ​g​c​c​ ​-​a​r​c​h​ ​x​8​6​_​6​4​ ​-​g​ ​-​o​ ​d​a​t​a​s​e​g​ ​d​a​t​a​s​e​g​.​m​
/​/​ ​g​c​c​ ​-​a​r​c​h​ ​i​3​8​6​ ​-​g​ ​-​o​ ​d​a​t​a​s​e​g​-​3​2​ ​d​a​t​a​s​e​g​.​m​

/​/​ ​a​b​o​u​t​ ​8​K​ ​d​o​u​b​l​e​s​.​ ​l​i​v​e​s​ ​i​n​ ​t​h​e​ ​i​n​i​t​i​a​l​i​z​e​d​ ​d​a​t​a​ ​s​e​g​m​e​n​t​.​
s​t​a​t​i​c​ ​d​o​u​b​l​e​ ​r​d​o​u​b​l​e​s​[​]​ ​=​ ​{​
 ​ ​ ​ ​0​.​0​,​ ​1​.​0​,​ ​2​.​0​,​ ​3​.​0​,​ ​4​.​0​,​ ​5​.​0​,​ ​6​.​0​,​ ​7​.​0​,​ ​8​.​0​,​ ​9​.​0​,​
 ​ ​ ​ ​1​0​.​0​,​ ​1​1​.​0​,​ ​1​2​.​0​,​ ​1​3​.​0​,​ ​1​4​.​0​,​ ​1​5​.​0​,​ ​1​6​.​0​,​ ​1​7​.​0​,​ ​1​8​.​0​,​ ​1​9​.​0​,​
 ​ ​ ​ ​.​.​.​
 ​ ​ ​ ​1​0​1​7​.​0​,​ ​1​0​1​8​.​0​,​ ​1​0​1​9​.​0​,​ ​1​0​2​0​.​0​,​ ​1​0​2​1​.​0​,​
 ​ ​ ​ ​1​0​2​2​.​0​,​ ​1​0​2​3​.​0​
}​;​

/​/​ ​o​n​e​ ​m​e​g​,​ ​a​l​l​ ​z​e​r​o​s​.​ ​ ​L​i​v​e​s​ ​i​n​ ​t​h​e​ ​u​n​i​n​i​t​i​a​l​z​e​d​ ​d​a​t​a​ ​s​e​g​m​e​n​t​
s​t​a​t​i​c​ ​c​h​a​r​ ​b​u​f​f​e​r​[​1​0​4​8​5​7​6​]​;​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​h​i​!​ ​ ​%​l​d​ ​b​y​t​e​s​ ​o​f​ ​d​o​u​b​l​e​s​,​ ​%​l​d​ ​b​y​t​e​s​ ​o​f​ ​b​u​f​f​e​r​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​o​f​(​r​d​o​u​b​l​e​s​)​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

Here’s a sample run:

$​ ​.​/​d​a​t​a​s​e​g​
h​i​!​ ​ ​8​1​9​2​ ​b​y​t​e​s​ ​o​f​ ​d​o​u​b​l​e​s​,​ ​1​0​4​8​5​7​6​ ​b​y​t​e​s​ ​o​f​ ​b​u​f​f​e​r​

The size command will show the size of numerous segments of programs. Running size on this program yields:

$​ ​s​i​z​e​ ​d​a​t​a​s​e​g​
_​_​T​E​X​T​ ​ ​_​_​D​A​T​A​ ​ ​_​_​O​B​J​C​ ​ ​o​t​h​e​r​s​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​c​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​x​
4​0​9​6​ ​ ​ ​ ​1​0​6​0​8​6​4​ ​0​ ​ ​ ​ ​ ​ ​ ​4​2​9​4​9​7​1​3​9​2​ ​ ​ ​ ​ ​ ​4​2​9​6​0​3​6​3​5​2​ ​ ​ ​ ​ ​ ​1​0​0​1​0​5​0​0​0​

Above is the size output for a 64-bit executable file. This is the size for a 32-bit version:

$​ ​g​c​c​ ​-​a​r​c​h​ ​i​3​8​6​ ​-​g​ ​-​o​ ​d​a​t​a​s​e​g​-​3​2​ ​d​a​t​a​s​e​g​.​m​
$​ ​s​i​z​e​ ​.​/​d​a​t​a​s​e​g​-​3​2​
_​_​T​E​X​T​ ​ ​_​_​D​A​T​A​ ​ ​_​_​O​B​J​C​ ​ ​o​t​h​e​r​s​ ​ ​d​e​c​ ​ ​ ​ ​ ​h​e​x​
4​0​9​6​ ​ ​ ​ ​1​0​6​0​8​6​4​ ​4​0​9​6​ ​ ​ ​ ​8​1​9​2​ ​ ​ ​ ​1​0​7​7​2​4​8​ ​1​0​7​0​0​0​

Here size combines the size of initialized and uninitialized data into the __DATA entry. 1060864 (the data segment size) minus 1048576 (the zero-filled uninitialized data) is 12288, which is 8192 (the 8K of double data) plus 4096 (4K of overhead and bookkeeping).

The 64-bit version, though, has a huge “others” section, weighing in at more than four gigabytes. Use the -m flag to get more details:

$​ ​s​i​z​e​ ​-​m​ ​d​a​t​a​s​e​g​
S​e​g​m​e​n​t​ ​_​_​P​A​G​E​Z​E​R​O​:​ ​4​2​9​4​9​6​7​2​9​6​
.​.​.​

The __PAGEZERO segment is exactly four gigabytes large. The others segment was 4294971392, for a difference of 4096 bytes of bookkeeping. Remember that 64-bit processes have the bottom four gigabytes zeroed out to catch pointer truncation errors. The __PAGEZERO segment is how the loader is told to reserve that space.

Note finally that the application size is small even with those large chunks of zeros in the __PAGEZERO and empty buffer. The megabyte buffer of zeros comes from the uninitialized data segment:

$​ ​l​s​ ​-​l​ ​d​a​t​a​s​e​g​
-​r​w​x​r​-​x​r​-​x​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​1​7​2​5​6​ ​J​u​n​ ​2​1​ ​1​0​:​2​7​ ​d​a​t​a​s​e​g​*​

Memory Lifetime

Initialized and uninitialized data segment variables are around during the entire run time of the program. They will not go away. Memory on the heap is explicitly requested and explicitly released. Memory here can be deallocated, but it’s under program control. Memory on the stack goes away, meaning that it can be reused by someone else as soon as it goes out of scope, even before a function exits, like within brace-delimited blocks within a function. The stack memory behavior can cause errors if you assume that memory will be valid longer than it actually is. A classic error is returning an address on the stack:

c​h​a​r​ ​*​f​r​o​b​u​l​a​t​e​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​5​0​0​0​]​;​

 ​ ​ ​ ​/​/​ ​w​o​r​k​ ​o​n​ ​b​u​f​f​e​r​

 ​ ​ ​ ​r​e​t​u​r​n​ ​b​u​f​f​e​r​;​

}​ ​/​/​ ​f​r​o​b​u​l​a​t​e​

buffer is allocated on the stack. There are five thousand bytes on the stack, and buffer contains the address of the first byte. Once buffer goes out of scope, this memory will be available for other functions to use. Anyone working with the return result of borkulize() is taking a chance that someone will clobber its contents, potentially much later in time after this function exits, leading to bugs that are hard to track down.

Dynamic Memory Allocation

“Dynamic memory” is memory that comes from the heap. The heap of the program starts off at an OS-defined default amount of space available for program consumption. As you allocate memory from the heap, it fills up, and then your program asks for more memory from the OS. Memory that you have released can be reused by your program. Memory allocated and subsequently freed is still charged to your program by the operating system. If you allocate 50 megabytes for temporary workspace and then free it all, your program will still have the 50 megabytes of memory allocated to it. This will eventually be swapped out to disk (on desktop systems) since you might not be using it. The total amount of memory can be considered a high water mark.

The primary functions for allocation and deallocating memory are:

void *malloc (size_t size);

void free (void *ptr);

void *realloc (void *ptr, size_t size);

void *reallocf (void *ptr, size_t size);

These functions give you memory from the heap.

malloc()

malloc() allocates a chunk of memory with the address of the block aligned to the strictest boundary required in the OS. That is, if an 8-byte double had the strictest alignment, malloc() would return addresses that were evenly divisible by 8. Example 7.2 allocates blocks of different sizes and shows the address of the returned memory.

Example 7.2. mallocalign.m

/​/​ ​m​a​l​l​o​c​a​l​i​g​n​.​m​ ​-​-​ ​s​e​e​ ​h​o​w​ ​m​a​l​l​o​c​ ​a​l​i​g​n​s​ ​i​t​s​ ​p​o​i​n​t​e​r​s​

/​/​ ​g​c​c​ ​-​W​a​l​l​ ​-​g​ ​-​o​ ​m​a​l​l​o​c​a​l​i​g​n​ ​m​a​l​l​o​c​a​l​i​g​n​.​m​

#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​/​/​ ​f​o​r​ ​m​a​l​l​o​c​(​)​

v​o​i​d​ ​a​l​l​o​c​p​r​i​n​t​ ​(​s​i​z​e​_​t​ ​s​i​z​e​)​ ​{​
 ​ ​ ​ ​v​o​i​d​ ​*​m​e​m​o​r​y​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​m​a​l​l​o​c​(​%​l​d​)​ ​=​=​ ​%​p​\​n​"​,​ ​s​i​z​e​,​ ​m​e​m​o​r​y​)​;​
 ​ ​ ​ ​/​/​ ​I​n​t​e​n​t​i​o​n​a​l​l​y​ ​l​e​a​k​e​d​ ​s​o​ ​w​e​ ​g​e​t​ ​a​ ​n​e​w​ ​b​l​o​c​k​ ​o​f​ ​m​e​m​o​r​y​
}​ ​/​/​ ​a​l​l​o​c​p​r​i​n​t​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​a​l​l​o​c​p​r​i​n​t​ ​(​1​)​;​
 ​ ​ ​ ​a​l​l​o​c​p​r​i​n​t​ ​(​2​)​;​
 ​ ​ ​ ​a​l​l​o​c​p​r​i​n​t​ ​(​s​i​z​e​o​f​(​d​o​u​b​l​e​)​)​;​
 ​ ​ ​ ​a​l​l​o​c​p​r​i​n​t​ ​(​1​0​2​4​ ​*​ ​1​0​2​4​)​;​
 ​ ​ ​ ​a​l​l​o​c​p​r​i​n​t​ ​(​1​)​;​
 ​ ​ ​ ​a​l​l​o​c​p​r​i​n​t​ ​(​1​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

has a run of

$​ ​.​/​m​a​l​l​o​c​a​l​i​g​n​
m​a​l​l​o​c​(​1​)​ ​=​=​ ​0​x​1​0​0​1​0​0​0​8​0​
m​a​l​l​o​c​(​2​)​ ​=​=​ ​0​x​1​0​0​1​0​0​0​9​0​
m​a​l​l​o​c​(​8​)​ ​=​=​ ​0​x​1​0​0​1​0​0​0​a​0​
m​a​l​l​o​c​(​1​0​4​8​5​7​6​)​ ​=​=​ ​0​x​1​0​0​2​0​0​0​0​0​
m​a​l​l​o​c​(​1​)​ ​=​=​ ​0​x​1​0​0​1​0​0​0​b​0​
m​a​l​l​o​c​(​1​)​ ​=​=​ ​0​x​1​0​0​1​0​0​0​c​0​

These are all addresses evenly divisible by 16. Vector operations, such as Altivec or SSE, work best when aligned on 16-byte boundaries. You can see that the very large malloc starts at an address far away from the small allocations. The memory is being put on its own pages elsewhere in the address space.

You should always use the C sizeof operator to determine how much memory to allocate for specific data structures:

t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​N​o​d​e​ ​{​
 ​ ​ ​ ​i​n​t​ ​b​l​a​h​;​ ​/​/​ ​4​ ​b​y​t​e​s​
 ​ ​ ​ ​i​n​t​ ​b​o​r​k​;​ ​/​/​ ​4​ ​b​y​t​e​s​
}​ ​N​o​d​e​;​

N​o​d​e​ ​*​m​y​n​o​d​e​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​N​o​d​e​)​)​;​ ​ ​/​/​ ​8​ ​b​y​t​e​s​

and for arrays

N​o​d​e​ ​n​o​d​u​l​e​s​[​]​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​N​o​d​e​)​ ​*​ ​1​0​0​)​;​ ​ ​/​/​ ​8​0​0​ ​b​y​t​e​s​

malloc() is entitled to give you a block of memory that is larger than what you ask for, but you are only guaranteed to get as much memory as you ask for.

Example 7.3. mallocsize.m

/​/​ ​m​a​l​l​o​c​s​i​z​e​.​m​ ​-​-​ ​s​e​e​ ​w​h​a​t​ ​k​i​n​d​ ​o​f​ ​b​l​o​c​k​ ​s​i​z​e​s​ ​m​a​l​l​o​c​ ​i​s​ ​a​c​t​u​a​l​l​y​ ​g​i​v​i​n​g​ ​u​s​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​m​a​l​l​o​c​s​i​z​e​ ​m​a​l​l​o​c​s​i​z​e​.​m​

#​i​m​p​o​r​t​ ​<​m​a​l​l​o​c​/​m​a​l​l​o​c​.​h​>​ ​ ​/​/​ ​f​o​r​ ​m​a​l​l​o​c​_​s​i​z​e​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​m​a​l​l​o​c​(​)​

v​o​i​d​ ​a​l​l​o​c​p​r​i​n​t​ ​(​s​i​z​e​_​t​ ​s​i​z​e​)​ ​{​
 ​ ​ ​ ​v​o​i​d​ ​*​m​e​m​o​r​y​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​m​a​l​l​o​c​(​%​l​d​)​ ​h​a​s​ ​a​ ​b​l​o​c​k​ ​s​i​z​e​ ​o​f​ ​%​l​d​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​,​ ​m​a​l​l​o​c​_​s​i​z​e​(​m​e​m​o​r​y​)​)​;​
 ​ ​ ​ ​/​/​ ​I​n​t​e​n​t​i​o​n​a​l​l​y​ ​l​e​a​k​e​d​ ​s​o​ ​w​e​ ​g​e​t​ ​a​ ​n​e​w​ ​b​l​o​c​k​ ​o​f​ ​m​e​m​o​r​y​

}​ ​/​/​ ​a​l​l​o​c​p​r​i​n​t​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​a​l​l​o​c​p​r​i​n​t​ ​(​1​)​;​
 ​ ​ ​ ​a​l​l​o​c​p​r​i​n​t​ ​(​s​i​z​e​o​f​(​d​o​u​b​l​e​)​)​;​
 ​ ​ ​ ​a​l​l​o​c​p​r​i​n​t​ ​(​1​4​)​;​
 ​ ​ ​ ​a​l​l​o​c​p​r​i​n​t​ ​(​1​6​)​;​
 ​ ​ ​ ​a​l​l​o​c​p​r​i​n​t​ ​(​3​2​)​;​
 ​ ​ ​ ​a​l​l​o​c​p​r​i​n​t​ ​(​4​8​)​;​
 ​ ​ ​ ​a​l​l​o​c​p​r​i​n​t​ ​(​6​4​)​;​
 ​ ​ ​ ​a​l​l​o​c​p​r​i​n​t​ ​(​1​0​0​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

Yields:

$​ ​.​/​m​a​l​l​o​c​s​i​z​e​
m​a​l​l​o​c​(​1​)​ ​h​a​s​ ​a​ ​b​l​o​c​k​ ​s​i​z​e​ ​o​f​ ​1​6​
m​a​l​l​o​c​(​8​)​ ​h​a​s​ ​a​ ​b​l​o​c​k​ ​s​i​z​e​ ​o​f​ ​1​6​
m​a​l​l​o​c​(​1​4​)​ ​h​a​s​ ​a​ ​b​l​o​c​k​ ​s​i​z​e​ ​o​f​ ​1​6​
m​a​l​l​o​c​(​1​6​)​ ​h​a​s​ ​a​ ​b​l​o​c​k​ ​s​i​z​e​ ​o​f​ ​1​6​
m​a​l​l​o​c​(​3​2​)​ ​h​a​s​ ​a​ ​b​l​o​c​k​ ​s​i​z​e​ ​o​f​ ​3​2​
m​a​l​l​o​c​(​4​8​)​ ​h​a​s​ ​a​ ​b​l​o​c​k​ ​s​i​z​e​ ​o​f​ ​4​8​
m​a​l​l​o​c​(​6​4​)​ ​h​a​s​ ​a​ ​b​l​o​c​k​ ​s​i​z​e​ ​o​f​ ​6​4​
m​a​l​l​o​c​(​1​0​0​)​ ​h​a​s​ ​a​ ​b​l​o​c​k​ ​s​i​z​e​ ​o​f​ ​1​1​2​

You can see that the actual block size is often larger than what was asked for. Memory allocation algorithms are an interesting area of computer science, and most any operating systems textbook will describe a number of different algorithms for managing dynamic memory. Apple has revved its memory allocation algorithms several times in the past.

Usually, your malloc arena has a bunch of buckets that each contain uniform-sized blocks of memory. The system chooses the smallest block size that will contain the requested amount of memory. Rather than have a whole bunch of 9-byte blocks and a whole bunch of 10-byte blocks and a whole bunch of 11-byte blocks (and so on), it will have larger increments. In the above case, it has 16 bytes, 32 bytes, 48 bytes, and 64 bytes. Powers of two or sums of powers of two.

Even though malloc_size() reports sizes (possibly) larger that what was initially allocated, you should not use it to see “how much memory is allocated to this pointer.” For example, if a function is passed a pointer that had been allocated using malloc(8), but you use malloc_size() on the pointer and find its block size to be 16, don’t subsequently treat the pointer like it had come from malloc(16). Doing that could cause problems. malloc_size() also won’t work for pointers to stack memory. You will still need to pass around sizes of buffers.

In general, you cannot make any assumptions about memory placement with multiple calls to malloc().

E.g., you cannot depend on this:

x​ ​=​ ​m​a​l​l​o​c​ ​(​1​0​)​;​
y​ ​=​ ​m​a​l​l​o​c​ ​(​1​0​)​;​

to look like Figure 7.3 in memory

Figure 7.3  Incorrect memory layout

Depending on this malloc(), it most likely looks like: Figure 7.4

Figure 7.4  How blocks are actually laid out

There is no guarantee that blocks allocated one after the other are anywhere near each other in memory.

Dynamic memory allocation gives no guarantee of locality of reference. (Locality of reference means what is used together is placed near each other in memory, leading to fewer cache misses and less paging activity.) You could allocate two chunks of memory one after the other:

x​ ​=​ ​m​a​l​l​o​c​ ​(​1​0​)​;​
y​ ​=​ ​m​a​l​l​o​c​ ​(​1​0​)​;​

and it is perfectly legal for malloc() to give you a pointer to x from one end of your address space and a pointer to y from way on the other side. If you need locality of reference, you may be better off allocating one large chunk of memory and then doing suballocations yourself out of that buffer.

Finally, malloc() on Mac OS X is always thread safe. Some older Unix variants have a thread-safe malloc() if you link with special thread libraries.

free()

free() tells the system that you are done with a block of memory and that it can be reused by a subsequent call to malloc(). In earlier examples, especially mallocalign.m, the allocated memory was purposely never freed because the same block would keep getting returned, and it is hard to draw conclusions about memory alignment if you get the same starting address for a block of memory each time.

Not freeing allocated memory is termed a “memory leak,” since the memory just kind of pours away and is not available for use any more. There is a discussion about memory leaks and memory leak detection tools later on.

Lastly, make sure you only feed free() addresses you get from malloc() or NULL. You will get unpredictable results (crash) if you give free() addresses of stack buffers or other memory not allocated by malloc(). Some functions call malloc() on your behalf, such as strdup(), and are documented that you need to free() their return values.

realloc()

realloc() resizes a chunk of memory that has been previously allocated. Essentially, realloc() does something like:

v​o​i​d​ ​*​c​h​e​e​s​y​R​e​a​l​l​o​c​ ​(​v​o​i​d​ ​*​p​t​r​,​ ​s​i​z​e​_​t​ ​s​i​z​e​)​ ​{​
 ​ ​ ​ ​v​o​i​d​ ​*​n​e​w​M​e​m​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​)​;​
 ​ ​ ​ ​m​e​m​c​p​y​ ​(​n​e​w​M​e​m​,​ ​p​t​r​,​ ​s​i​z​e​)​;​
 ​ ​ ​ ​f​r​e​e​ ​(​p​t​r​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​n​e​w​M​e​m​;​
}​ ​/​/​ ​c​h​e​e​s​y​R​e​a​l​l​o​c​

realloc()’d blocks can therefore move in the heap, so if you have multiple pointers to the same starting address you may need to update them all after reallocating the block. You could have the pointer live in one place and wrap an API around it, or you could use two memory objects: a smaller one that will not move, like a tree node, and a larger one that the smaller one points to, like the user-editable label for the tree node. The larger object can be reallocated, but only the tree node needs to update the address change.

As you would expect, realloc() has optimizations so that it does not have to do the allocate/copy/free procedure every time a block is reallocated. As noted above, sometimes the block returned from malloc() is actually larger than what you asked for. realloc() can just say, “OK, you can now use the rest of the block.” There are also games realloc() can play. There maybe be a free block in another bucket that is contiguous with the block of memory you want to reallocate. In this case, realloc() gloms that second block onto the first and lets you use the space without changing addresses on you.

But in any case, be sure to assign the return value of realloc() back to your pointer. Otherwise, you can have a lurking problem:

N​o​d​e​ ​*​b​l​a​h​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​N​o​d​e​)​ ​*​ ​2​0​)​;​
.​.​.​
r​e​a​l​l​o​c​ ​(​b​l​a​h​,​ ​s​i​z​e​o​f​(​N​o​d​e​)​ ​*​ ​4​0​)​;​ ​/​/​ ​t​h​i​s​ ​i​s​ ​b​a​d​!​
.​.​.​

Sometimes it will work, sometimes not. Always assign the return value of realloc.

b​l​a​h​ ​=​ ​r​e​a​l​l​o​c​ ​(​b​l​a​h​,​ ​s​i​z​e​o​f​(​N​o​d​e​)​ ​*​ ​4​0​)​;​

Even then, this code could cause problems. If there is not enough room for the new block, realloc() will return NULL, but otherwise it will leave the original memory block untouched. If you do not otherwise hang on to the original pointer, you would leak the block. The reallocf() function will free the memory block if it cannot successfully complete the reallocation.

calloc()

malloc() does not initialize the memory it returns to you, so you’ll probably have a bunch of stale junk in the memory you get. A pretty common idiom is to allocate a chunk of memory and zero it out so that it is safer to use:

v​o​i​d​ ​*​m​e​m​o​r​y​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​N​o​d​e​)​ ​*​ ​5​0​)​;​
m​e​m​s​e​t​ ​(​m​e​m​o​r​y​,​ ​0​,​ ​s​i​z​e​o​f​(​N​o​d​e​)​ ​*​ ​5​0​)​;​

You can also use calloc() to do this in one operation:

 ​ ​ ​ ​ ​v​o​i​d​ ​*​c​a​l​l​o​c​(​s​i​z​e​_​t​ ​n​e​l​e​m​,​ ​s​i​z​e​_​t​ ​e​l​s​i​z​e​)​;​

The arguments to calloc() assume that you are allocating an array. It’s just doing a multiplication behind the scenes. So,

m​e​m​o​r​y​ ​=​ ​c​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​N​o​d​e​)​,​ ​5​0​)​;​

gives identical results to the two-step sequence above. It is better to use calloc() because the OS can do some optimizations behind the scenes, like allowing the kernel to reserve the memory, but not actually allocate it. It can then give you zero-filled pages when it is actually accessed. When you memset() a block of memory, you are writing to every page, causing them all to be marked as dirty.

alloca()

void *alloca(size_t size);

alloca() (for alloc automatic) allocates memory for you on the call stack. This means that allocation is very fast, being just some pointer adjustments. You also do not need to perform an explicit free() on your memory to release it. When the function ends, the stack frame just goes away and with it the chunk of the frame that contains the allocated memory. As with stuff that appears too good to be true, there is always a catch. Don’t go nuts and overflow your stack with lots of local storage, especially if you use recursion or if your code could be run in a threaded environment where stack sizes are much more limited. Also, using alloca() with runtime-calculated allocation sizes can confuse some performance tools.

Even though the manpage for alloca() says, “This is machine dependent, its use is discouraged,” it is still a documented API you can use, and it can be useful at times. Also, C99 lets you declare local-variable arrays with a variable size, just like in C++, which works in a way very similar to alloca().

Memory Ownership Issues

One of the difficulties involved when using dynamic memory is determining who is responsible for freeing a piece of allocated memory when nobody else is using it. This is known as memory ownership. There are many different solutions to this problem, each with their own tradeoffs and benefits, as witnessed by Java and Cocoa garbage collection, C++ RAII (resource acquisition is initialization), and Cocoa’s reference counting.

There are no uniform rules for memory ownership for Unix and C library calls, so you pretty much need to check the manpage for the calls in question (which is usually a good idea anyway). For instance:

getenv() returns a char *, but you do not need to free it since the environment variables are all stored in a global array and getenv() just returns a string contained in that array.

strdup() returns a char *, which you do need to free since it allocates memory on your behalf.

Some calls take buffers, which you can malloc or create on the stack and you are responsible for freeing the memory when you are done. A subset of these calls can be given NULL for the buffer argument, and they will allocate memory on your behalf. getcwd() behaves like this.

Some other calls will give you a reference to memory that they own (usually some kind of global buffer), such as ctime() for converting a Unix time into a character constant.

Finally, there are some APIs that wrap dynamic memory allocation in an API and depend on you to use that API to create and destroy objects, such as opendir() and closedir() for iterating through the contents of directories.

Nodepools

And, of course, you can use any of these techniques for modules and APIs that you create, choosing the one that is most appropriate. It is perfectly fine for you to do your own memory allocation out of a big block, if that gives you better behavior. Example 7.4 shows a common suballocation technique using a memory pool. A memory pool is an allocator for vending identically-sized objects, very handy for things like tree or list nodes.

Example 7.4. nodepool.m

/​/​ ​n​o​d​e​p​o​o​l​.​m​ ​-​-​ ​A​ ​s​i​m​p​l​e​ ​m​e​m​o​r​y​ ​p​o​o​l​ ​f​o​r​ ​v​e​n​d​i​n​g​ ​l​i​k​e​-​s​i​z​e​ ​p​i​e​c​e​s​ ​o​f​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​m​e​m​o​r​y​.​ ​ ​A​n​ ​e​x​a​m​p​l​e​ ​o​f​ ​c​u​s​t​o​m​ ​m​e​m​o​r​y​ ​m​a​n​a​g​e​m​e​n​t​.​

/​/​ ​g​c​c​ ​-​W​a​l​l​ ​-​s​t​d​=​c​9​9​ ​-​g​ ​-​O​s​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​o​ ​n​o​d​e​p​o​o​l​ ​n​o​d​e​p​o​o​l​.​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​

/​/​ ​T​h​e​ ​f​r​e​e​ ​l​i​s​t​ ​t​h​a​t​ ​r​u​n​s​ ​t​h​r​o​u​g​h​ ​a​l​l​ ​t​h​e​ ​b​l​o​c​k​s​
t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​B​W​P​o​o​l​E​l​e​m​e​n​t​ ​{​
 ​ ​ ​ ​s​t​r​u​c​t​ ​B​W​P​o​o​l​E​l​e​m​e​n​t​ ​*​n​e​x​t​;​
}​ ​B​W​P​o​o​l​E​l​e​m​e​n​t​;​

@​i​n​t​e​r​f​a​c​e​ ​B​W​N​o​d​e​P​o​o​l​ ​:​ ​N​S​O​b​j​e​c​t​ ​{​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​c​h​a​r​ ​ ​*​m​e​m​b​l​o​c​k​;​ ​/​/​ ​A​ ​b​i​g​ ​b​l​o​b​ ​o​f​ ​b​y​t​e​s​.​
 ​ ​ ​ ​B​W​P​o​o​l​E​l​e​m​e​n​t​ ​ ​*​f​r​e​e​l​i​s​t​;​ ​/​/​ ​P​o​i​n​t​e​r​ ​t​o​ ​t​h​e​ ​h​e​a​d​ ​o​f​ ​t​h​e​ ​f​r​e​e​l​i​s​t​.​
 ​ ​ ​ ​s​i​z​e​_​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​S​i​z​e​;​
 ​ ​ ​ ​s​i​z​e​_​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​u​n​t​;​
}​

-​ ​(​i​d​)​ ​i​n​i​t​W​i​t​h​N​o​d​e​S​i​z​e​:​ ​(​s​i​z​e​_​t​)​ ​n​o​d​e​S​i​z​e​ ​ ​c​o​u​n​t​:​ ​(​s​i​z​e​_​t​)​ ​c​o​u​n​t​;​
-​ ​(​v​o​i​d​ ​*​)​ ​a​l​l​o​c​N​o​d​e​;​
-​ ​(​v​o​i​d​)​ ​f​r​e​e​N​o​d​e​:​ ​(​v​o​i​d​ ​*​)​ ​n​o​d​e​P​t​r​;​

@​e​n​d​ ​/​/​ ​B​W​N​o​d​e​P​o​o​l​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​B​W​N​o​d​e​P​o​o​l​

-​ ​(​v​o​i​d​)​ ​w​e​a​v​e​F​r​e​e​L​i​s​t​F​r​o​m​:​ ​(​u​n​s​i​g​n​e​d​ ​c​h​a​r​ ​*​)​ ​s​t​a​r​t​A​d​d​r​e​s​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​r​C​o​u​n​t​:​ ​(​s​i​z​e​_​t​)​ ​t​h​e​C​o​u​n​t​ ​{​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​c​h​a​r​ ​*​s​c​a​n​ ​=​ ​s​t​a​r​t​A​d​d​r​e​s​s​;​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​t​h​e​C​o​u​n​t​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​f​r​e​e​l​i​s​t​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​r​e​e​l​i​s​t​ ​=​ ​(​B​W​P​o​o​l​E​l​e​m​e​n​t​ ​*​)​ ​s​c​a​n​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​r​e​e​l​i​s​t​-​>​n​e​x​t​ ​=​ ​N​U​L​L​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​B​W​P​o​o​l​E​l​e​m​e​n​t​ ​*​t​e​m​p​ ​=​ ​(​B​W​P​o​o​l​E​l​e​m​e​n​t​*​)​ ​s​c​a​n​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​e​m​p​-​>​n​e​x​t​ ​=​ ​f​r​e​e​l​i​s​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​r​e​e​l​i​s​t​ ​=​ ​t​e​m​p​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​ ​+​=​ ​n​o​d​e​S​i​z​e​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​w​e​a​v​e​F​r​e​e​L​i​s​t​F​r​o​m​

-​ ​(​i​d​)​ ​i​n​i​t​W​i​t​h​N​o​d​e​S​i​z​e​:​ ​(​s​i​z​e​_​t​)​ ​t​h​e​N​o​d​e​S​i​z​e​ ​ ​c​o​u​n​t​:​ ​(​s​i​z​e​_​t​)​ ​t​h​e​C​o​u​n​t​ ​{​
 ​ ​ ​ ​i​f​ ​(​(​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​S​i​z​e​ ​=​ ​t​h​e​N​o​d​e​S​i​z​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​u​n​t​ ​=​ ​t​h​e​C​o​u​n​t​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​M​a​k​e​ ​s​u​r​e​ ​t​h​e​r​e​'​s​ ​e​n​o​u​g​h​ ​s​p​a​c​e​ ​t​o​ ​s​t​o​r​e​ ​t​h​e​ ​p​o​i​n​t​e​r​s​ ​f​o​r​ ​t​h​e​ ​f​r​e​e​l​i​s​t​.​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​n​o​d​e​S​i​z​e​ ​<​ ​s​i​z​e​o​f​(​B​W​P​o​o​l​E​l​e​m​e​n​t​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​S​i​z​e​ ​=​ ​s​i​z​e​o​f​(​B​W​P​o​o​l​E​l​e​m​e​n​t​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​A​l​l​o​c​a​t​e​ ​m​e​m​o​r​y​ ​f​o​r​ ​t​h​e​ ​b​l​o​c​k​.​
 ​ ​ ​ ​ ​ ​ ​ ​m​e​m​b​l​o​c​k​ ​=​ ​m​a​l​l​o​c​ ​(​n​o​d​e​S​i​z​e​ ​*​ ​c​o​u​n​t​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​a​l​k​ ​t​h​r​o​u​g​h​ ​t​h​e​ ​b​l​o​c​k​ ​b​u​i​l​d​i​n​g​ ​t​h​e​ ​f​r​e​e​l​i​s​t​.​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​w​e​a​v​e​F​r​e​e​L​i​s​t​F​r​o​m​:​ ​m​e​m​b​l​o​c​k​ ​ ​f​o​r​C​o​u​n​t​:​ ​t​h​e​C​o​u​n​t​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​

}​ ​/​/​ ​i​n​i​t​W​i​t​h​N​o​d​e​S​i​z​e​

-​ ​(​v​o​i​d​)​ ​d​e​a​l​l​o​c​ ​{​
 ​ ​ ​ ​f​r​e​e​ ​(​m​e​m​b​l​o​c​k​)​;​
 ​ ​ ​ ​[​s​u​p​e​r​ ​d​e​a​l​l​o​c​]​;​
}​ ​/​/​ ​d​e​a​l​l​o​c​

-​ ​(​v​o​i​d​ ​*​)​ ​a​l​l​o​c​N​o​d​e​ ​{​
 ​ ​ ​ ​i​f​ ​(​f​r​e​e​l​i​s​t​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​e​'​r​e​ ​o​u​t​ ​o​f​ ​s​p​a​c​e​,​ ​s​o​ ​j​u​s​t​ ​t​h​r​o​w​ ​o​u​r​ ​h​a​n​d​s​ ​u​p​ ​a​n​d​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​s​u​r​r​e​n​d​e​r​ ​f​o​r​ ​n​o​w​.​ ​ ​Y​o​u​ ​c​a​n​ ​g​r​o​w​ ​t​h​e​ ​p​o​o​l​ ​b​y​ ​k​e​e​p​i​n​g​ ​a​n​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​a​r​r​a​y​ ​o​f​ ​m​e​m​b​l​o​c​k​s​ ​a​n​d​ ​c​r​e​a​t​i​n​g​ ​a​ ​n​e​w​ ​o​n​e​ ​w​h​e​n​ ​t​h​e​ ​p​r​e​v​i​o​u​s​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​b​l​o​c​k​ ​f​i​l​l​s​ ​u​p​.​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​o​u​t​ ​o​f​ ​s​p​a​c​e​ ​i​n​ ​n​o​d​e​ ​p​o​o​l​.​ ​ ​G​i​v​i​n​g​ ​u​p​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​b​o​r​t​ ​(​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​t​a​k​e​ ​a​ ​n​e​w​ ​n​o​d​e​ ​o​f​f​ ​o​f​ ​t​h​e​ ​f​r​e​e​l​i​s​t​
 ​ ​ ​ ​v​o​i​d​ ​*​n​e​w​N​o​d​e​ ​=​ ​f​r​e​e​l​i​s​t​;​
 ​ ​ ​ ​f​r​e​e​l​i​s​t​ ​=​ ​f​r​e​e​l​i​s​t​-​>​n​e​x​t​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​n​e​w​N​o​d​e​;​

}​ ​/​/​ ​a​l​l​o​c​N​o​d​e​

-​ ​(​v​o​i​d​)​ ​f​r​e​e​N​o​d​e​:​ ​(​v​o​i​d​ ​*​)​ ​n​o​d​e​P​t​r​ ​{​
 ​ ​ ​ ​/​/​ ​S​t​i​c​k​ ​t​h​e​ ​f​r​e​e​d​ ​n​o​d​e​ ​a​t​ ​t​h​e​ ​h​e​a​d​ ​o​f​ ​t​h​e​ ​f​r​e​e​l​i​s​t​.​
 ​ ​ ​ ​(​(​B​W​P​o​o​l​E​l​e​m​e​n​t​ ​*​)​n​o​d​e​P​t​r​)​-​>​n​e​x​t​ ​=​ ​f​r​e​e​l​i​s​t​;​
 ​ ​ ​ ​f​r​e​e​l​i​s​t​ ​=​ ​n​o​d​e​P​t​r​;​
}​ ​/​/​ ​f​r​e​e​N​o​d​e​

@​e​n​d​ ​/​/​ ​B​W​N​o​d​e​P​o​o​l​

/​/​ ​T​h​e​ ​n​o​d​e​ ​w​e​'​r​e​ ​u​s​i​n​g​ ​t​h​e​ ​n​o​d​e​p​o​o​l​ ​f​o​r​.​

#​d​e​f​i​n​e​ ​N​O​D​E​_​B​U​F​_​S​I​Z​E​ ​1​3​7​

t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​L​i​s​t​N​o​d​e​ ​{​
 ​ ​ ​ ​i​n​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​o​m​e​D​a​t​a​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​L​i​s​t​N​o​d​e​ ​ ​ ​ ​*​n​e​x​t​;​
}​ ​L​i​s​t​N​o​d​e​;​

v​o​i​d​ ​h​a​v​e​F​u​n​W​i​t​h​P​o​o​l​ ​(​i​n​t​ ​n​o​d​e​C​o​u​n​t​)​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​C​r​e​a​t​i​n​g​ ​n​o​d​e​s​ ​w​i​t​h​ ​t​h​e​ ​n​o​d​e​ ​p​o​o​l​"​)​;​

 ​ ​ ​ ​B​W​N​o​d​e​P​o​o​l​ ​*​n​o​d​e​P​o​o​l​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​B​W​N​o​d​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​N​o​d​e​S​i​z​e​:​ ​s​i​z​e​o​f​(​L​i​s​t​N​o​d​e​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​u​n​t​:​ ​n​o​d​e​C​o​u​n​t​]​;​
 ​ ​ ​ ​L​i​s​t​N​o​d​e​ ​*​n​o​d​e​ ​=​ ​N​U​L​L​,​ ​*​p​r​e​v​ ​=​ ​N​U​L​L​;​

 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​n​o​d​e​C​o​u​n​t​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​ ​=​ ​[​n​o​d​e​P​o​o​l​ ​a​l​l​o​c​N​o​d​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​-​>​s​o​m​e​D​a​t​a​ ​=​ ​i​;​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​I​f​ ​y​o​u​ ​w​i​s​h​,​ ​y​o​u​ ​c​a​n​ ​d​o​ ​s​o​m​e​ ​e​x​t​r​a​ ​w​o​r​k​.​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​o​n​s​t​r​u​c​t​ ​a​ ​l​i​n​k​e​d​ ​l​i​s​t​ ​t​h​r​o​u​g​h​ ​t​h​e​ ​n​o​d​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​-​>​n​e​x​t​ ​=​ ​p​r​e​v​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​e​v​ ​=​ ​n​o​d​e​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​C​l​e​a​n​i​n​g​ ​u​p​"​)​;​

 ​ ​ ​ ​/​/​ ​D​e​s​t​r​o​y​ ​a​l​l​ ​t​h​e​ ​n​o​d​e​s​ ​a​t​ ​o​n​c​e​.​ ​ ​I​f​ ​e​a​c​h​ ​n​o​d​e​ ​h​a​s​ ​m​e​m​o​r​y​ ​m​a​n​a​g​e​m​e​n​t​
 ​ ​ ​ ​/​/​ ​o​b​l​i​g​a​t​i​o​n​s​,​ ​y​o​u​ ​w​o​u​l​d​ ​n​e​e​d​ ​t​o​ ​w​a​l​k​ ​t​h​e​ ​l​i​s​t​ ​o​f​ ​n​o​d​e​s​.​
 ​ ​ ​ ​[​n​o​d​e​P​o​o​l​ ​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​D​o​n​e​"​)​;​

}​ ​/​/​ ​h​a​v​e​F​u​n​W​i​t​h​P​o​o​l​

v​o​i​d​ ​h​a​v​e​F​u​n​W​i​t​h​M​a​l​l​o​c​ ​(​i​n​t​ ​n​o​d​e​C​o​u​n​t​)​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​C​r​e​a​t​i​n​g​ ​n​o​d​e​s​ ​w​i​t​h​ ​m​a​l​l​o​c​"​)​;​

 ​ ​ ​ ​L​i​s​t​N​o​d​e​ ​*​n​o​d​e​ ​=​ ​N​U​L​L​,​ ​*​p​r​e​v​ ​=​ ​N​U​L​L​;​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​n​o​d​e​C​o​u​n​t​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​L​i​s​t​N​o​d​e​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​-​>​s​o​m​e​D​a​t​a​ ​=​ ​i​;​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​I​f​ ​y​o​u​ ​w​i​s​h​,​ ​y​o​u​ ​c​a​n​ ​d​o​ ​s​o​m​e​ ​e​x​t​r​a​ ​w​o​r​k​.​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​o​n​s​t​r​u​c​t​ ​a​ ​l​i​n​k​e​d​ ​l​i​s​t​ ​t​h​r​o​u​g​h​ ​t​h​e​ ​n​o​d​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​-​>​n​e​x​t​ ​=​ ​p​r​e​v​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​e​v​ ​=​ ​n​o​d​e​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​L​i​s​t​N​o​d​e​ ​*​h​e​a​d​ ​=​ ​n​o​d​e​;​

 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​C​l​e​a​n​i​n​g​ ​u​p​"​)​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​h​e​a​d​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​L​i​s​t​N​o​d​e​ ​*​n​o​d​e​ ​=​ ​h​e​a​d​;​
 ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​ ​=​ ​h​e​a​d​-​>​n​e​x​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​r​e​e​ ​(​n​o​d​e​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​D​o​n​e​"​)​;​

}​ ​/​/​ ​h​a​v​e​F​u​n​W​i​t​h​M​a​l​l​o​c​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​c​o​u​n​t​;​

 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​!​=​ ​3​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​s​a​g​e​:​ ​%​s​ ​-​p​|​-​m​ ​#​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​ ​ ​ ​ ​ ​ ​ ​e​x​e​r​c​i​s​e​ ​m​e​m​o​r​y​ ​a​l​l​o​c​a​t​i​o​n​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​ ​ ​ ​ ​ ​ ​ ​-​p​ ​t​o​ ​u​s​e​ ​a​ ​m​e​m​o​r​y​ ​p​o​o​l​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​ ​ ​ ​ ​ ​ ​ ​-​m​ ​t​o​ ​u​s​e​ ​m​a​l​l​o​c​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​ ​ ​ ​ ​ ​ ​ ​#​ ​ ​n​u​m​b​e​r​ ​o​f​ ​n​o​d​e​s​ ​t​o​ ​p​l​a​y​ ​w​i​t​h​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​1​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​c​o​u​n​t​ ​=​ ​a​t​o​i​ ​(​a​r​g​v​[​2​]​)​;​

 ​ ​ ​ ​i​f​ ​(​s​t​r​c​m​p​(​a​r​g​v​[​1​]​,​ ​"​-​p​"​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​h​a​v​e​F​u​n​W​i​t​h​P​o​o​l​ ​(​c​o​u​n​t​)​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​h​a​v​e​F​u​n​W​i​t​h​M​a​l​l​o​c​ ​(​c​o​u​n​t​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​

}​ ​/​/​ ​m​a​i​n​

Once the pool is created, allocations and frees are constant time, being just a couple of pointer operations. malloc() usually takes longer due to the complexity of its internal data structures. If your pool nodes don’t reference other objects, you can delete everything from the pool in one operation. Here are some timings (in seconds) of runs of the program on a 2010 model MacBook Pro. Timings come from the time command:

Count | Pool | malloc()
20,000,000 | 0.62 | 2.86
100,000,000 | 3.12 | 14.19

This shows there can be, under some circumstances, benefit to doing your own allocation. Generally, it is better to test first to find out what your bottlenecks are before implementing your own allocator, but it’s nice to have the option when you need it.

You can override the new operator in C++ to use a pool for allocations. In one C++ project I worked on, I had a class that supported chained array subscripts for digging into a compacted data structure, with code performing access like flavor['page']['sect']['styl'][5]. This was very convenient coding-wise, but it caused a lot of temporary objects to be created and destroyed. A pool was put under this class’ operator new, and performance improved by an order of magnitude.

One thing to note in the nodepool.m code is that it doesn’t handle the pool growing case. You cannot just realloc() the memory block because it could move in memory, leaving the freelist pointers dangling, as well as any pointers the objects being allocated might have, such as the linked list pointers. This can be fixed by having an array of memory pointers; when you run out of memory, create a new block, add it to the array, and weave the freelist through the new block.

Operations on the nodepool are not thread-safe. There are exercises to make it thread safe in Chapter 20: Multithreading and Chapter 22: Grand Central Dispatch.

Debugging Memory Problems

Errors in memory management cause a huge number of problems when programming in C and can lead to difficult-to-track-down bugs since the manifestation of a problem can happen long after the actual program error happened.

Common API issues

When malloc() cannot allocate memory, it returns NULL. Many programmers tend to ignore NULL results from malloc(). The conventional wisdom was that if memory really is exhausted, then the system is in some pretty serious trouble and is swapping heavily. In that case, it is just easier to crash and restart. Plus, it can be tedious to check the return value of malloc() all the time. In these days of systems with many gigabytes of RAM, it’s possible for an app to fill up its address space before physical memory is exhausted, so the system will not be swapping.

iOS will let you know when you are running short on memory by giving you low-memory warnings, such as calling the UIApplication delegate method -applicationDidReceiveMemoryWarning: as well as UIViewController’s -viewDidUnload.

The typical Cocoa idiom of allocation and initialization

N​S​A​r​r​a​y​ ​*​a​r​r​a​y​ ​=​ ​[​[​N​S​A​r​r​a​y​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
[​a​r​r​a​y​ ​a​d​d​O​b​j​e​c​t​:​ ​m​y​O​b​j​e​c​t​]​;​

glosses over allocation problems. alloc may return a nil object, and since Objective-C messages to nil are legal, in the face of an allocation problem, this code will propagate this nil object without complaint.

If you are paranoid, and/or want to be robust in low memory conditions for your own allocations, you can put a wrapper around malloc() (say a safeMalloc() or use preprocessor tricks to rename malloc() itself) that on a NULL return from malloc() will attempt to free memory and try the allocation again, and perhaps call abort() when things are in complete dire straits.

You can get garbage collectors for C and C++ to do automatic cleanup of memory, and Objective-C 2.0 has introduced garbage collection for Cocoa.

Another common API issue is not assigning the return value of realloc(). Your program can work fine until the block of memory moves. Then you are pointing to old memory. If that old memory does not get reused right away, things will seem to work fine until the most inconvenient moment, when things will fall apart.

Only free allocated memory once: do not try to free the same pointer twice. That will usually lead to a crash as the malloc() data structures get confused. Also do not try to free memory you got from some other API, unless it explicitly says you can call free() on it. For instance, opendir() allocates a chunk of memory and returns it to you, but do not free() that memory, use closedir().

Lastly, do not access memory you have just freed. The old (stale) data may still be there, but that is something you do not want to depend on, especially in a threaded environment.

Memory corruption

Memory corruption happens when a piece of code writes data into an unexpected location in memory. At best, you will try writing into memory you do not have access to and will crash. At worst, you will slightly corrupt some data structure, which will manifest itself in an error millions of instructions in the future or even on another run of your program. The most common kinds of memory errors in C are buffer overruns and dangling pointers.

Buffer overruns are when you think you have a certain amount of memory at your disposal, but you actually have less than that amount allocated. A classic example is forgetting to account for the trailing zero byte for C string termination.

For instance:

c​h​a​r​ ​*​s​t​r​i​n​g​C​o​p​y​ ​=​ ​m​a​l​l​o​c​ ​(​s​t​r​l​e​n​(​m​y​s​t​r​i​n​g​)​)​;​
s​t​r​c​p​y​ ​(​s​t​r​i​n​g​C​o​p​y​,​ ​m​y​s​t​r​i​n​g​)​;​

You have just written one byte past the end of your allocated block of memory. To correct this, you need to account for that extra byte:

c​h​a​r​ ​*​s​t​r​i​n​g​C​o​p​y​ ​=​ ​m​a​l​l​o​c​ ​(​s​t​r​l​e​n​(​s​t​r​i​n​g​)​ ​+​ ​1​)​;​
s​t​r​c​p​y​ ​(​s​t​r​i​n​g​C​o​p​y​,​ ​s​t​r​i​n​g​)​;​

Ideally, you should use a variant of the call that takes the buffer length:

c​h​a​r​ ​*​s​t​r​i​n​g​C​o​p​y​ ​=​ ​m​a​l​l​o​c​ ​(​s​t​r​l​e​n​(​s​t​r​i​n​g​)​ ​+​ ​1​)​;​
s​t​r​n​c​p​y​ ​(​s​t​r​i​n​g​C​o​p​y​,​ ​s​t​r​i​n​g​,​ ​s​t​r​l​e​n​(​s​t​r​i​n​g​)​ ​+​ ​1​)​;​

Even in the case where you have an insufficient buffer size, it will not write randomly off the end.

Off-by-one errors, also called “obiwans” or “fence-post errors,” can also cause a buffer overrun. For example:

v​o​i​d​ ​d​o​S​t​u​f​f​ ​(​)​ ​{​
 ​ ​ ​ ​L​i​s​t​N​o​d​e​ ​n​o​d​e​s​[​2​0​]​;​
 ​ ​ ​ ​c​h​a​r​ ​s​t​r​i​n​g​B​u​f​f​e​r​[​1​0​2​4​]​;​

 ​ ​ ​ ​i​n​t​ ​i​;​
 ​ ​ ​ ​f​o​r​ ​(​i​ ​=​ ​0​;​ ​i​ ​<​=​ ​2​0​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​s​[​i​]​.​s​t​u​f​f​ ​=​ ​i​;​
 ​ ​ ​ ​ ​ ​ ​ ​.​.​.​
 ​ ​ ​ ​}​
}​ ​/​/​ ​d​o​S​t​u​f​f​

The loop runs from 0 through 20, which will execute the body of the loop 21 times. The last time through, the loop is indexing past the end of the nodes array and (most likely) has just trashed the beginning of the stringBuffer array.

Another nasty side effect of buffer overruns is that malicious data could clobber the stack in such a way that program control will jump to an unexpected place when the function returns. Many cracker exploits work like this.

Dangling pointers are memory addresses stored in pointer variables that no longer have any correlation with the memory they should be pointing to. Uninitialized pointers can cause this, as can forgetting to assign the return value of realloc(). You can get dangling pointers if you do not propagate the address of something when it moves or is changed. For example:

c​h​a​r​ ​*​g​_​u​s​e​r​n​a​m​e​;​

c​o​n​s​t​ ​c​h​a​r​ ​*​g​e​t​U​s​e​r​N​a​m​e​ ​(​)​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​g​_​u​s​e​r​n​a​m​e​;​
}​

v​o​i​d​ ​s​e​t​U​s​e​r​N​a​m​e​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​n​e​w​N​a​m​e​)​ ​{​
 ​ ​ ​ ​f​r​e​e​ ​(​g​_​u​s​e​r​n​a​m​e​)​;​
 ​ ​ ​ ​g​_​u​s​e​r​n​a​m​e​ ​=​ ​s​t​r​d​u​p​ ​(​n​e​w​N​a​m​e​)​;​ ​/​/​ ​p​e​r​f​o​r​m​s​ ​a​ ​m​a​l​l​o​c​
}​

Now consider this scenario:

n​a​m​e​ ​=​ ​g​e​t​U​s​e​r​N​a​m​e​(​)​;​ ​/​/​ ​s​a​y​ ​i​t​ ​i​s​ ​a​d​d​r​e​s​s​ ​0​x​1​0​0​0​,​ ​"​m​a​r​k​d​"​
s​e​t​U​s​e​r​N​a​m​e​ ​(​"​b​o​r​k​"​)​;​ ​/​/​ ​t​h​e​ ​m​e​m​o​r​y​ ​a​t​ ​a​d​d​r​e​s​s​ ​0​x​1​0​0​0​ ​h​a​s​ ​b​e​e​n​ ​f​r​e​e​d​
p​r​i​n​t​f​ ​(​n​a​m​e​)​;​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​u​s​i​n​g​ ​a​ ​d​a​n​g​l​i​n​g​ ​p​o​i​n​t​e​r​ ​n​o​w​

This is fundamentally an error in memory ownership. Does someone own the memory after calling getUserName()? It could be part of getUserName()’s contract that changing the username invalidates previously vended pointers. But still, this is a programming error that will most likely lead to a crash.

The OS X malloc() libraries have built-in tools to help track down some of these conditions. You can control these tools by setting environment variables and then running your program. If you are debugging a GUI app, you can run it from the command line by doing open /path/to/your/AppBundle.app. Xcode 4 will let you control these tools from the GUI.

$​ ​e​x​p​o​r​t​ ​M​a​l​l​o​c​H​e​l​p​=​1​

will display help when your run your program. This is bash syntax. (C Shell users would use setenv MallocHelp 1). When you are done using MallocHelp, you can remove the environment variable by using the unset command. C Shell users would use unsetenv.

MallocGuardEdges

This puts a 4KB page with no permissions before and after large blocks. This will catch buffer overruns before and after the allocated block. The size of a “large block” is undefined, but experimentally, 12K and larger seem to be considered large blocks.

MallocStackLogging and MallocStackLoggingNoCompact

These turn on stack frame recording for memory management calls for later use by tools like malloc_history.The difference is that MallocStackLoggingNoCompact remembers the malloc history even after the block has been released.

MallocScribble

This writes over-freed blocks with a known value, (0x55), which will catch attempts to reuse memory blocks. This is a bad pointer value, an odd address, which will cause addressing errors if it gets dereferenced.

Example 7.5 shows MallocScribble in action.

Example 7.5. mallocscribble.m

/​/​ ​m​a​l​l​o​c​s​c​r​i​b​b​l​e​.​m​ ​-​-​ ​e​x​e​r​c​i​s​e​ ​M​a​l​l​o​c​S​c​r​i​b​b​l​e​
/​/​ ​R​u​n​ ​t​h​i​s​,​ ​t​h​e​n​ ​r​u​n​ ​a​f​t​e​r​ ​s​e​t​t​i​n​g​ ​t​h​e​ ​M​a​l​l​o​c​S​c​r​i​b​b​l​e​ ​e​n​v​i​r​o​n​m​e​n​t​
/​/​ ​v​a​r​i​a​b​l​e​ ​t​o​ ​1​.​

/​/​ ​g​c​c​ ​-​o​ ​m​a​l​l​o​c​s​c​r​i​b​b​l​e​ ​m​a​l​l​o​c​s​c​r​i​b​b​l​e​.​m​

#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​m​a​l​l​o​c​(​)​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​c​p​y​(​)​

t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​T​h​i​n​g​i​e​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​b​l​a​h​[​1​6​]​;​
 ​ ​ ​ ​c​h​a​r​ ​s​t​r​i​n​g​[​3​0​]​;​
}​ ​T​h​i​n​g​i​e​;​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​T​h​i​n​g​i​e​ ​*​t​h​i​n​g​ ​=​ ​m​a​l​l​o​c​ ​(​2​0​)​;​

 ​ ​ ​ ​s​t​r​c​p​y​ ​(​t​h​i​n​g​-​>​s​t​r​i​n​g​,​ ​"​h​e​l​l​o​ ​t​h​e​r​e​"​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​b​e​f​o​r​e​ ​f​r​e​e​:​ ​%​s​\​n​"​,​ ​t​h​i​n​g​-​>​s​t​r​i​n​g​)​;​
 ​ ​ ​ ​f​r​e​e​ ​(​t​h​i​n​g​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​a​f​t​e​r​ ​f​r​e​e​:​ ​%​s​\​n​"​,​ ​t​h​i​n​g​-​>​s​t​r​i​n​g​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​

}​ ​/​/​ ​m​a​i​n​

Without anything set in the environment:

$​ ​.​/​m​a​l​l​o​c​s​c​r​i​b​b​l​e​
b​e​f​o​r​e​ ​f​r​e​e​:​ ​h​e​l​l​o​ ​t​h​e​r​e​
a​f​t​e​r​ ​f​r​e​e​:​ ​h​e​l​l​o​ ​t​h​e​r​e​

And after:

$​ ​e​x​p​o​r​t​ ​M​a​l​l​o​c​S​c​r​i​b​b​l​e​=​1​
$​ ​.​/​m​a​l​l​o​c​s​c​r​i​b​b​l​e​
m​a​l​l​o​c​[​2​0​7​0​1​]​:​ ​e​n​a​b​l​i​n​g​ ​s​c​r​i​b​b​l​i​n​g​ ​t​o​ ​d​e​t​e​c​t​ ​m​o​d​s​ ​t​o​ ​f​r​e​e​ ​b​l​o​c​k​s​
m​a​l​l​o​c​s​c​r​i​b​b​l​e​(​5​2​3​9​)​ ​m​a​l​l​o​c​:​ ​e​n​a​b​l​i​n​g​ ​s​c​r​i​b​b​l​i​n​g​ ​t​o​ ​d​e​t​e​c​t​ ​m​o​d​s​ ​t​o​ ​f​r​e​e​ ​b​l​o​c​k​s​
b​e​f​o​r​e​ ​f​r​e​e​:​ ​h​e​l​l​o​ ​t​h​e​r​e​
a​f​t​e​r​ ​f​r​e​e​:​ ​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​U​

Guard Malloc

Xcode ships with libgmalloc (Guard Malloc), which is an aggressive debugging malloc library geared to catch memory overrun errors.

Each memory allocation is placed on its own virtual memory page when libgmalloc is enabled, with the end of the buffer placed at the end of the page’s memory. The next page is kept unallocated. You will generate a signal if you try to access beyond the end of the buffer. This will immediately catch run-off-the-end errors.

When memory is freed, libgmalloc deallocates its virtual memory, causing subsequent reads or writes to cause a bus error. Because of the extra pressure put on the virtual memory system, your application can run ten to one hundred times slower, so you probably don’t want to run this all of the time. But when you need help tracking down nasty memory corruption problems, this can be a life-saver.

To enable libgmalloc, set the environment variable DYLD_INSERT_LIBRARIES to have the value /usr/lib/libgmalloc.dylib. This will cause the libgmalloc library to be loaded and override the existing definitions of malloc() and free(). You can either set this in your environment before running the program:

$​ ​e​x​p​o​r​t​ ​D​Y​L​D​_​I​N​S​E​R​T​_​L​I​B​R​A​R​I​E​S​=​/​u​s​r​/​l​i​b​/​l​i​b​g​m​a​l​l​o​c​.​d​y​l​i​b​

or set it in gdb before running your program:

(​g​d​b​)​ ​s​e​t​ ​e​n​v​ ​D​Y​L​D​_​I​N​S​E​R​T​_​L​I​B​R​A​R​I​E​S​ ​/​u​s​r​/​l​i​b​/​l​i​b​g​m​a​l​l​o​c​.​d​y​l​i​b​

lldb has a similar syntax:

(​l​l​d​b​)​ ​s​e​t​ ​e​n​v​-​v​a​r​s​ ​D​Y​L​D​_​I​N​S​E​R​T​_​L​I​B​R​A​R​I​E​S​=​/​u​s​r​/​l​i​b​/​l​i​b​g​m​a​l​l​o​c​.​d​y​l​i​b​

The debugger will halt execution of your program when your program reads or writes off of the end of a dynamically allocated chunk of memory. You can then poke around and see what went wrong.

Memory leaks

Another common memory-related error is memory leaks. These are bits of memory that get allocated and never deallocated. Memory leaks frequently happen when you assign a pointer to a new value but do not free the old value:

c​h​a​r​ ​*​s​o​m​e​s​t​r​i​n​g​;​
s​o​m​e​s​t​r​i​n​g​ ​=​ ​s​t​r​d​u​p​ ​(​"​h​e​l​l​o​"​)​;​ ​/​/​ ​p​e​r​f​o​r​m​s​ ​a​ ​m​a​l​l​o​c​(​)​ ​a​n​d​ ​a​ ​s​t​r​i​n​g​ ​c​o​p​y​.​
s​o​m​e​s​t​r​i​n​g​ ​=​ ​s​t​r​d​u​p​ ​(​"​t​h​e​r​e​"​)​;​

The first string, “hello,” has been leaked. This memory can never be freed because the address was not preserved. On desktop systems, a little memory leaked here or there, aside from being a bit sloppy, is not all that bad when you have gobs of RAM coupled with unused memory getting paged back to disk. What are the real killers are leaks that happen often, like inside of a loop, or every time the user does a common operation. Leaking 100 bytes is not too bad. Leaking 100 bytes every time the user presses a key in a word processor can be deadly. Applications in Mac OS X tend to run for long periods of time, so the user may forget about an app, then click on it in the Dock to do something with it. A small memory leak can really add up when your program can be running for weeks between restarts. iOS devices have very constrained memory compared to desktop computers. Any memory leaks can quickly exhaust the available memory and will cause the operating system to kill your application outright.

One easy way to tell if your program is leaking is to run top or Activity Monitor, find your application in the list, and watch the VSIZE column. You probably have a memory leak if that number is continually increasing

OS X comes with the leaks command that will grovel around in your program’s address space and find unreferenced memory. leaks is a good quick check to see if you have any leaks. It walks your program’s address space like a garbage collector, looking for pointers into malloc() blocks. If it can’t find one, leaks will report a leak, along with a dump of some of the bytes near the start of the block. If the leaked chunk of memory is an instance of an Objective-C object, leaks will show the name of the class:

$​ ​l​e​a​k​s​ ​B​o​r​k​G​r​a​p​h​
P​r​o​c​e​s​s​ ​2​1​6​6​9​:​ ​2​6​3​0​6​ ​n​o​d​e​s​ ​m​a​l​l​o​c​e​d​ ​f​o​r​ ​2​0​4​2​ ​K​B​
P​r​o​c​e​s​s​ ​2​1​6​6​9​:​ ​1​5​4​ ​l​e​a​k​s​ ​f​o​r​ ​5​1​2​0​ ​t​o​t​a​l​ ​l​e​a​k​e​d​ ​b​y​t​e​s​.​
L​e​a​k​:​ ​0​x​0​1​1​f​3​c​5​0​ ​ ​s​i​z​e​=​4​0​
 ​ ​0​x​7​7​e​b​2​3​9​b​ ​0​x​0​0​0​0​0​0​2​0​ ​0​x​0​1​1​f​3​c​a​0​ ​0​x​0​0​0​0​0​0​0​1​ ​ ​ ​ ​ ​w​.​#​.​.​.​.​ ​.​.​X​.​.​.​.​.​
 ​ ​0​x​0​0​0​0​0​0​6​5​ ​0​x​0​0​0​0​0​0​6​5​ ​0​x​0​0​0​0​0​0​6​5​ ​0​x​0​0​0​0​0​0​0​0​ ​ ​ ​ ​ ​.​.​.​e​.​.​.​e​.​.​.​e​.​.​.​.​
 ​ ​0​x​0​1​1​b​8​d​e​0​ ​0​x​0​1​1​b​8​e​3​0​ ​0​x​0​0​0​0​0​0​0​0​ ​0​x​0​0​0​0​0​0​0​0​ ​ ​ ​ ​ ​.​.​.​.​.​.​.​0​.​.​.​.​.​.​.​.​
L​e​a​k​:​ ​0​x​0​1​1​f​3​c​a​0​ ​ ​s​i​z​e​=​4​8​ ​ ​ ​ ​ ​ ​ ​s​t​r​i​n​g​ ​'​x​r​v​t​'​
L​e​a​k​:​ ​0​x​0​2​6​3​4​e​b​0​ ​ ​s​i​z​e​=​3​2​ ​ ​ ​ ​ ​ ​ ​i​n​s​t​a​n​c​e​ ​o​f​ ​'​N​S​A​f​f​i​n​e​T​r​a​n​s​f​o​r​m​'​ ​
 ​ ​0​x​a​2​8​7​e​7​a​c​ ​0​x​4​0​c​c​b​6​5​b​ ​0​x​8​0​0​0​0​0​0​0​ ​0​x​8​0​0​0​0​0​0​0​ ​ ​ ​ ​ ​.​.​.​.​@​.​.​[​.​.​.​.​.​.​.​.​
 ​ ​0​x​4​0​c​c​b​6​5​b​ ​0​x​c​2​2​6​5​4​2​a​ ​0​x​c​2​f​8​8​f​c​7​ ​0​x​0​0​0​0​0​0​0​0​ ​ ​ ​ ​ ​@​.​.​[​.​x​T​*​.​.​.​.​.​.​.​.​

Memory leaks in Cocoa

In addition to malloc()-related memory leaks, you can also leak memory in Cocoa programs by not being careful with your retain and release calls. Example 7.6 intentionally leaks some objects.

For instance:

Example 7.6. objectleak.m

/​/​ ​o​b​j​e​c​t​l​e​a​k​.​m​ ​-​-​ ​L​e​a​k​ ​s​o​m​e​ ​C​o​c​o​a​ ​o​b​j​e​c​t​s​

/​/​g​c​c​ ​-​W​a​l​l​ ​-​s​t​d​=​c​9​9​ ​-​g​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​o​ ​o​b​j​e​c​t​l​e​a​k​ ​o​b​j​e​c​t​l​e​a​k​.​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​*​a​r​r​a​y​ ​=​ ​[​[​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​2​0​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​N​u​m​b​e​r​ ​*​n​u​m​b​e​r​ ​=​ ​[​[​N​S​N​u​m​b​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​I​n​t​:​ ​i​]​;​ ​/​/​ ​r​e​t​a​i​n​ ​c​o​u​n​t​ ​o​f​ ​1​
 ​ ​ ​ ​ ​ ​ ​ ​[​n​u​m​b​e​r​ ​r​e​t​a​i​n​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​a​r​r​a​y​ ​a​d​d​O​b​j​e​c​t​:​ ​n​u​m​b​e​r​]​;​ ​/​/​ ​n​u​m​b​e​r​ ​h​a​s​ ​r​e​t​a​i​n​ ​c​o​u​n​t​ ​o​f​ ​2​
 ​ ​ ​ ​}​

 ​ ​ ​ ​[​a​r​r​a​y​ ​r​e​l​e​a​s​e​]​;​ ​/​/​ ​E​a​c​h​ ​o​f​ ​t​h​e​ ​n​u​m​b​e​r​s​ ​h​a​v​e​ ​r​e​t​a​i​n​ ​c​o​u​n​t​s​ ​o​f​ ​1​.​
 ​ ​ ​ ​/​/​ ​T​h​e​r​e​f​o​r​e​ ​w​e​'​v​e​ ​l​e​a​k​e​d​ ​e​a​c​h​ ​o​f​ ​t​h​e​ ​n​u​m​b​e​r​s​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​
 ​ ​ ​ ​s​l​e​e​p​ ​(​5​0​0​0​)​;​
 ​ ​ ​ ​/​/​ ​N​o​w​ ​w​o​u​l​d​ ​b​e​ ​a​ ​g​o​o​d​ ​t​i​m​e​ ​t​o​ ​r​u​n​ ​l​e​a​k​s​.​
 ​ ​ ​ ​e​x​i​t​ ​0​;​

}​ ​/​/​ ​m​a​i​n​

Each of the NSNumber objects still has a retain count of one after the array is released, so they have been leaked.

The leaks tool can show you these leaks:

$​ ​l​e​a​k​s​ ​o​b​j​e​c​t​l​e​a​k​
.​.​.​
P​r​o​c​e​s​s​ ​5​4​0​9​:​ ​9​2​4​ ​n​o​d​e​s​ ​m​a​l​l​o​c​e​d​ ​f​o​r​ ​2​0​8​ ​K​B​
P​r​o​c​e​s​s​ ​5​4​0​9​:​ ​6​ ​l​e​a​k​s​ ​f​o​r​ ​1​9​2​ ​t​o​t​a​l​ ​l​e​a​k​e​d​ ​b​y​t​e​s​.​
L​e​a​k​:​ ​0​x​1​0​0​1​0​d​7​7​0​ ​ ​s​i​z​e​=​3​2​ ​ ​z​o​n​e​:​ ​D​e​f​a​u​l​t​M​a​l​l​o​c​Z​o​n​e​_​0​x​1​0​0​0​0​4​0​0​0​ ​
 ​ ​ ​ ​i​n​s​t​a​n​c​e​ ​o​f​ ​'​N​S​C​F​N​u​m​b​e​r​'​,​ ​t​y​p​e​ ​O​b​j​C​,​ ​i​m​p​l​e​m​e​n​t​e​d​ ​i​n​ ​F​o​u​n​d​a​t​i​o​n​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​0​x​7​0​b​b​e​6​e​8​ ​0​x​0​0​0​0​7​f​f​f​ ​0​x​0​0​0​0​1​6​8​3​ ​0​x​0​0​0​0​0​0​0​2​ ​ ​ ​ ​ ​.​.​.​p​.​.​.​.​.​.​.​.​.​.​.​.​
 ​ ​ ​ ​ ​ ​ ​ ​0​x​0​0​0​0​0​0​0​e​ ​0​x​0​0​0​0​0​0​0​0​ ​0​x​0​0​0​0​0​0​0​0​ ​0​x​0​0​0​0​0​0​0​0​ ​ ​ ​ ​ ​.​.​.​.​.​.​.​.​.​.​.​.​.​.​.​.​
.​.​.​

Other Tools

ps

The ps command, short for “process status,” has some features for keeping tabs on your program’s memory use. Here is one use of ps:

$​ ​p​s​ ​a​u​x​w​ ​|​ ​g​r​e​p​ ​s​o​m​e​t​h​i​n​g​-​i​n​t​e​r​e​s​t​i​n​g​

where something-interesting is the PID or program name of interest. So, something like

$​ ​p​s​ ​a​u​x​w​ ​|​ ​g​r​e​p​ ​F​i​n​d​e​r​

will show some information about the Finder process:

m​a​r​k​d​ ​ ​ ​2​2​9​ ​ ​ ​0​.​0​ ​ ​1​.​9​ ​ ​ ​ ​2​9​1​2​5​8​4​ ​ ​3​3​1​3​6​ ​ ​?​?​ ​ ​S​ ​ ​ ​0​:​4​3​.​1​1​ ​/​S​y​s​t​e​m​/​ ​\​
L​i​b​r​a​r​y​/​C​o​r​e​S​e​r​v​i​c​e​s​/​F​i​n​d​e​r​.​a​p​p​/​C​o​n​t​e​n​t​s​/​M​a​c​O​S​/​F​i​n​d​e​r​ ​-​p​s​n​_​0​_​2​6​2​1​

In order, the columns are:

Owner of the process (markd).

Process ID (229).

CPU currently taken (0.0%).

Real memory in use (1.9%).

Virtual size, the total footprint of the program. The virtual size for 64-bit processes is very large due to the large volume of zero pages.

Resident set size, how much is living in RAM right now. This is 33136K, or about 33 megs.

Controlling terminal (not important).

Process state (more about this in Chapter 18: Multiprocessing).

Total CPU time consumed by the process (43 seconds).

  Command with arguments that started the process.

Relating to memory, the fifth and sixth columns are the most interesting. You can look at just those with the command

p​s​ ​-​a​x​ ​-​o​ ​u​s​e​r​,​p​i​d​,​v​s​z​,​r​s​s​,​c​o​m​m​a​n​d​

which will show the owner, the PID, the virtual size,the resident size, and the command:

$​ ​p​s​ ​-​a​x​ ​-​o​ ​u​s​e​r​,​p​i​d​,​v​s​z​,​r​s​s​,​c​o​m​m​a​n​d​
U​S​E​R​ ​ ​ ​ ​ ​ ​ ​P​I​D​ ​ ​ ​ ​ ​ ​V​S​Z​ ​ ​ ​ ​R​S​S​ ​C​O​M​M​A​N​D​
r​o​o​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​ ​ ​2​4​5​6​6​7​6​ ​ ​ ​ ​5​8​8​ ​/​s​b​i​n​/​l​a​u​n​c​h​d​
r​o​o​t​ ​ ​ ​ ​ ​ ​ ​ ​1​0​ ​ ​2​4​5​9​5​9​6​ ​ ​ ​1​6​8​8​ ​/​u​s​r​/​l​i​b​e​x​e​c​/​k​e​x​t​d​
r​o​o​t​ ​ ​ ​ ​ ​ ​ ​ ​1​1​ ​ ​2​4​5​2​6​8​0​ ​ ​ ​3​7​6​0​ ​/​u​s​r​/​s​b​i​n​/​D​i​r​e​c​t​o​r​y​S​e​r​v​i​c​e​
r​o​o​t​ ​ ​ ​ ​ ​ ​ ​ ​1​2​ ​ ​2​4​4​4​5​4​4​ ​ ​ ​ ​3​5​2​ ​/​u​s​r​/​s​b​i​n​/​n​o​t​i​f​y​d​
r​o​o​t​ ​ ​ ​ ​ ​ ​ ​ ​1​3​ ​ ​2​4​4​6​8​0​8​ ​ ​ ​ ​8​0​8​ ​/​u​s​r​/​s​b​i​n​/​d​i​s​k​a​r​b​i​t​r​a​t​i​o​n​d​
r​o​o​t​ ​ ​ ​ ​ ​ ​ ​ ​1​4​ ​ ​2​4​7​4​5​4​4​ ​ ​ ​2​4​4​0​ ​/​u​s​r​/​l​i​b​e​x​e​c​/​c​o​n​f​i​g​d​
r​o​o​t​ ​ ​ ​ ​ ​ ​ ​ ​1​5​ ​ ​2​4​5​7​0​9​6​ ​ ​ ​ ​4​2​0​ ​/​u​s​r​/​s​b​i​n​/​s​y​s​l​o​g​d​

ps can show a wealth of information about what is running on your system. You can run this repeatedly to see if your program (or any other) seems to be growing without bounds. Check out the manpage for more.

Resource limits

Because Unix is a multi-user system, there are safeguards in the OS to keep processes from dominating, and possibly bringing down, the system by consuming too many resources. There are a number of different resources that are controlled:

RLIMIT_DATA | Maximum size (bytes) of the data segment for a process, that is, the maximum size of the heap plus initialized + uninitialized data segments.
RLIMIT_RSS | Maximum size (bytes) to which a process’ resident set may grow. That is, maximum amount of physical RAM to be given.
RLIMIT_STACK | Maximum size (bytes) of the stack segment. How deep your program stack can get. This does not apply to the stacks that threads use.
RLIMIT_MEMLOCK | Maximum size (bytes) which a process can lock (wire) into memory with the mlock() function.

There are also resources not related to memory:

RLIMIT_FSIZE | Maximum size (bytes) of a file that may be created.
RLIMIT_NOFILE | Maximum number of simultaneously open files.
RLIMIT_NPROC | Maximum number of simultaneous processes for the current user.
RLIMIT_CPU | Maximum amount of CPU time (in seconds).
RLIMIT_CORE | Largest size (in bytes) of core files.

Resource limits are expressed as a soft limit and a hard limit. When the soft limit is exceeded, the program may receive a signal, which is like a software interrupt. (Signals are discussed in depth in Chapter 5: Exceptions, Error Handling, and Signals.) The soft limits are usually lower than hard limits, but you can raise them to the hard limit. You can lower the hard limit, but you can never raise the hard limit unless you are running with superuser privileges.

Example 7.7 shows you the hard and soft limits currently in force:

Example 7.7. limits.m

/​/​ ​l​i​m​i​t​s​.​m​ ​-​-​ ​S​e​e​ ​t​h​e​ ​c​u​r​r​e​n​t​ ​r​e​s​o​u​r​c​e​ ​l​i​m​i​t​s​.​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​l​i​m​i​t​s​ ​l​i​m​i​t​s​.​m​

#​i​m​p​o​r​t​ ​<​s​y​s​/​r​e​s​o​u​r​c​e​.​h​>​ ​/​/​ ​f​o​r​ ​t​h​e​ ​R​L​M​I​T​_​*​ ​c​o​n​s​t​a​n​t​s​
#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​(​)​

t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​L​i​m​i​t​ ​{​
 ​ ​ ​ ​i​n​t​ ​r​e​s​o​u​r​c​e​;​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​n​a​m​e​;​
}​ ​L​i​m​i​t​;​

L​i​m​i​t​ ​l​i​m​i​t​s​[​]​ ​=​ ​{​
 ​ ​ ​ ​{​ ​R​L​I​M​I​T​_​D​A​T​A​,​ ​ ​ ​ ​"​d​a​t​a​ ​s​e​g​m​e​n​t​ ​m​a​x​i​m​u​m​ ​(​b​y​t​e​s​)​"​ ​}​,​
 ​ ​ ​ ​{​ ​R​L​I​M​I​T​_​R​S​S​,​ ​ ​ ​ ​ ​"​r​e​s​i​d​e​n​t​ ​s​i​z​e​ ​m​a​x​i​m​u​m​ ​(​b​y​t​e​s​)​"​ ​}​,​
 ​ ​ ​ ​{​ ​R​L​I​M​I​T​_​S​T​A​C​K​,​ ​ ​ ​"​s​t​a​c​k​ ​s​i​z​e​ ​m​a​x​i​m​u​m​ ​(​b​y​t​e​s​)​"​ ​}​,​
 ​ ​ ​ ​{​ ​R​L​I​M​I​T​_​M​E​M​L​O​C​K​,​ ​"​w​i​r​e​d​ ​m​e​m​o​r​y​ ​m​a​x​i​m​u​m​ ​(​b​y​t​e​s​)​"​ ​}​,​
 ​ ​ ​ ​{​ ​R​L​I​M​I​T​_​F​S​I​Z​E​,​ ​ ​ ​"​f​i​l​e​ ​s​i​z​e​ ​m​a​x​i​m​u​m​ ​(​b​y​t​e​s​)​"​ ​}​,​
 ​ ​ ​ ​{​ ​R​L​I​M​I​T​_​N​O​F​I​L​E​,​ ​ ​"​m​a​x​ ​n​u​m​b​e​r​ ​o​f​ ​s​i​m​u​l​t​a​n​e​o​u​s​l​y​ ​o​p​e​n​ ​f​i​l​e​s​"​ ​}​,​
 ​ ​ ​ ​{​ ​R​L​I​M​I​T​_​N​P​R​O​C​,​ ​ ​ ​"​m​a​x​ ​n​u​m​b​e​r​ ​o​f​ ​s​i​m​u​l​t​a​n​e​o​u​s​ ​p​r​o​c​e​s​s​e​s​"​ ​}​,​
 ​ ​ ​ ​{​ ​R​L​I​M​I​T​_​C​P​U​,​ ​ ​ ​ ​ ​"​c​p​u​ ​t​i​m​e​ ​m​a​x​i​m​u​m​ ​(​s​e​c​o​n​d​s​)​"​ ​}​,​
 ​ ​ ​ ​{​ ​R​L​I​M​I​T​_​C​O​R​E​,​ ​ ​ ​ ​"​c​o​r​e​ ​f​i​l​e​ ​m​a​x​i​m​u​m​ ​(​b​y​t​e​s​)​"​ ​}​
}​;​

/​/​ ​T​u​r​n​ ​t​h​e​ ​r​l​i​m​_​t​ ​v​a​l​u​e​ ​i​n​ ​t​o​ ​a​ ​s​t​r​i​n​g​,​ ​a​l​s​o​ ​t​r​a​n​s​l​a​t​i​n​g​ ​t​h​e​ ​m​a​g​i​c​
/​/​ ​"​i​n​f​i​n​i​t​y​"​ ​v​a​l​u​e​ ​t​o​ ​s​o​m​e​t​h​i​n​g​ ​h​u​m​a​n​ ​r​e​a​d​a​b​l​e​
v​o​i​d​ ​s​t​r​i​n​g​V​a​l​u​e​ ​(​r​l​i​m​_​t​ ​v​a​l​u​e​,​ ​c​h​a​r​ ​*​b​u​f​f​e​r​,​ ​s​i​z​e​_​t​ ​b​u​f​f​e​r​s​i​z​e​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​v​a​l​u​e​ ​=​=​ ​R​L​I​M​_​I​N​F​I​N​I​T​Y​)​ ​s​t​r​c​p​y​ ​(​b​u​f​f​e​r​,​ ​"​i​n​f​i​n​i​t​e​"​)​;​
 ​ ​ ​ ​e​l​s​e​ ​s​n​p​r​i​n​t​f​ ​(​b​u​f​f​e​r​,​ ​b​u​f​f​e​r​s​i​z​e​,​ ​"​%​l​l​d​"​,​ ​v​a​l​u​e​)​;​
}​ ​/​/​ ​s​t​r​i​n​g​V​a​l​u​e​

/​/​ ​R​i​g​h​t​-​j​u​s​t​i​f​y​ ​t​h​e​ ​f​i​r​s​t​ ​e​n​t​r​y​ ​i​n​ ​a​ ​f​i​e​l​d​ ​w​i​d​t​h​ ​o​f​ ​4​5​,​ ​t​h​e​n​ ​d​i​s​p​l​a​y​
/​/​ ​t​w​o​ ​m​o​r​e​ ​s​t​r​i​n​g​s​.​
#​d​e​f​i​n​e​ ​F​O​R​M​A​T​_​S​T​R​I​N​G​ ​"​%​4​5​s​:​ ​%​-​1​0​s​ ​(​%​s​)​\​n​"​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​L​i​m​i​t​ ​*​s​c​a​n​ ​=​ ​l​i​m​i​t​s​;​
 ​ ​ ​ ​L​i​m​i​t​ ​*​s​t​o​p​ ​=​ ​s​c​a​n​ ​+​ ​(​s​i​z​e​o​f​(​l​i​m​i​t​s​)​ ​/​ ​s​i​z​e​o​f​(​*​l​i​m​i​t​s​)​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​F​O​R​M​A​T​_​S​T​R​I​N​G​,​ ​"​l​i​m​i​t​ ​n​a​m​e​"​,​ ​"​s​o​f​t​-​l​i​m​i​t​"​,​ ​"​h​a​r​d​-​l​i​m​i​t​"​)​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​s​c​a​n​ ​<​ ​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​r​l​i​m​i​t​ ​r​l​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​g​e​t​r​l​i​m​i​t​ ​(​s​c​a​n​-​>​r​e​s​o​u​r​c​e​,​ ​&​r​l​)​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​ ​i​n​ ​g​e​t​r​l​i​m​i​t​ ​f​o​r​ ​%​s​:​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​-​>​n​a​m​e​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​s​o​f​t​L​i​m​i​t​[​2​0​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​h​a​r​d​L​i​m​i​t​[​2​0​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​r​i​n​g​V​a​l​u​e​ ​(​r​l​.​r​l​i​m​_​c​u​r​,​ ​s​o​f​t​L​i​m​i​t​,​ ​2​0​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​r​i​n​g​V​a​l​u​e​ ​(​r​l​.​r​l​i​m​_​m​a​x​,​ ​h​a​r​d​L​i​m​i​t​,​ ​2​0​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​F​O​R​M​A​T​_​S​T​R​I​N​G​,​ ​s​c​a​n​-​>​n​a​m​e​,​ ​s​o​f​t​L​i​m​i​t​,​ ​h​a​r​d​L​i​m​i​t​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​+​+​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

Here is the output on a OS X 10.6, MacBook Pro system:

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​l​i​m​i​t​ ​n​a​m​e​:​ ​s​o​f​t​-​l​i​m​i​t​ ​(​h​a​r​d​-​l​i​m​i​t​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​a​t​a​ ​s​e​g​m​e​n​t​ ​m​a​x​i​m​u​m​ ​(​b​y​t​e​s​)​:​ ​i​n​f​i​n​i​t​e​ ​ ​ ​(​i​n​f​i​n​i​t​e​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​s​i​d​e​n​t​ ​s​i​z​e​ ​m​a​x​i​m​u​m​ ​(​b​y​t​e​s​)​:​ ​i​n​f​i​n​i​t​e​ ​ ​ ​(​i​n​f​i​n​i​t​e​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​a​c​k​ ​s​i​z​e​ ​m​a​x​i​m​u​m​ ​(​b​y​t​e​s​)​:​ ​8​7​2​0​0​0​0​ ​ ​ ​ ​(​6​7​1​0​4​7​6​8​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​r​e​d​ ​m​e​m​o​r​y​ ​m​a​x​i​m​u​m​ ​(​b​y​t​e​s​)​:​ ​i​n​f​i​n​i​t​e​ ​ ​ ​(​i​n​f​i​n​i​t​e​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​i​l​e​ ​s​i​z​e​ ​m​a​x​i​m​u​m​ ​(​b​y​t​e​s​)​:​ ​i​n​f​i​n​i​t​e​ ​ ​ ​(​i​n​f​i​n​i​t​e​)​
 ​ ​ ​ ​ ​ ​m​a​x​ ​n​u​m​b​e​r​ ​o​f​ ​s​i​m​u​l​t​a​n​e​o​u​s​l​y​ ​o​p​e​n​ ​f​i​l​e​s​:​ ​2​5​6​ ​ ​ ​ ​ ​ ​ ​ ​(​i​n​f​i​n​i​t​e​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​m​a​x​ ​n​u​m​b​e​r​ ​o​f​ ​s​i​m​u​l​t​a​n​e​o​u​s​ ​p​r​o​c​e​s​s​e​s​:​ ​2​6​6​ ​ ​ ​ ​ ​ ​ ​ ​(​5​3​2​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​p​u​ ​t​i​m​e​ ​m​a​x​i​m​u​m​ ​(​s​e​c​o​n​d​s​)​:​ ​i​n​f​i​n​i​t​e​ ​ ​ ​(​i​n​f​i​n​i​t​e​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​r​e​ ​f​i​l​e​ ​m​a​x​i​m​u​m​ ​(​b​y​t​e​s​)​:​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​i​n​f​i​n​i​t​e​)​

All things considered, the system is pretty kind to us.

You can read the current resource limits by using getrlimit() as shown in the code above, and you can change the resource limits by using setrlimit() and passing it an appropriately filled in struct rlimit.

Example 7.8 is a program that will attempt to open the same file over and over. It is easier to show resource limits with files than trying to overflow the stack.

Example 7.8. openfiles.m

/​/​ ​o​p​e​n​f​i​l​e​s​.​m​ ​-​-​ ​E​x​h​a​u​s​t​ ​o​p​e​n​-​f​i​l​e​s​ ​r​e​s​o​u​r​c​e​ ​l​i​m​i​t​.​

/​/​ ​g​c​c​ ​-​W​a​l​l​ ​-​s​t​d​=​c​9​9​ ​-​o​ ​o​p​e​n​f​i​l​e​s​ ​o​p​e​n​f​i​l​e​s​.​m​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​f​c​n​t​l​.​h​>​ ​ ​/​/​ ​f​o​r​ ​O​_​R​D​O​N​L​Y​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​/​/​ ​f​o​r​ ​f​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​/​/​ ​f​o​r​ ​e​x​i​t​,​ ​o​p​e​n​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​>​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​s​a​g​e​:​ ​ ​%​s​ ​[​o​p​e​n​-​f​i​l​e​-​r​l​i​m​i​t​]​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​1​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​=​=​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​r​l​i​m​i​t​ ​r​l​ ​=​ ​{​ ​.​r​l​i​m​_​c​u​r​ ​=​ ​a​t​o​i​ ​(​a​r​g​v​[​1​]​)​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​.​r​l​i​m​_​m​a​x​ ​=​ ​R​L​I​M​_​I​N​F​I​N​I​T​Y​ ​}​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​s​e​t​r​l​i​m​i​t​(​R​L​I​M​I​T​_​N​O​F​I​L​E​,​ ​&​r​l​)​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​ ​i​n​ ​s​e​t​r​l​i​m​i​t​ ​f​o​r​ ​R​L​I​M​_​N​O​F​I​L​E​:​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​x​i​t​ ​(​1​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​2​6​0​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​f​d​ ​=​ ​o​p​e​n​ ​(​"​/​u​s​r​/​i​n​c​l​u​d​e​/​s​t​d​i​o​.​h​"​,​ ​O​_​R​D​O​N​L​Y​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​d​:​ ​f​d​ ​i​s​ ​%​d​\​n​"​,​ ​i​,​ ​f​d​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​f​d​ ​<​ ​0​)​ ​b​r​e​a​k​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​

}​ ​/​/​ ​m​a​i​n​

When run, this happens:

$​ ​.​/​o​p​e​n​f​i​l​e​s​
0​:​ ​f​d​ ​i​s​ ​3​
1​:​ ​f​d​ ​i​s​ ​4​
.​.​.​
2​5​1​:​ ​f​d​ ​i​s​ ​2​5​4​
2​5​2​:​ ​f​d​ ​i​s​ ​2​5​5​
2​5​3​:​ ​f​d​ ​i​s​ ​-​1​

Notice that fd starts becoming -1, the error code return value from open(), because it cannot open the file. fd maxes out at 255, which correlates with the result of limits.m (Example 7.7), which said that the maximum number of simultaneously open files was 256. There are already the 3 files opened for us, stdin, stdout, stderror, which count as open files against the resource limit.

When run with an argument, the program changes the soft limit. Here are some sample runs:

$​ ​.​/​o​p​e​n​f​i​l​e​s​ ​1​0​
0​:​ ​f​d​ ​i​s​ ​3​
1​:​ ​f​d​ ​i​s​ ​4​
2​:​ ​f​d​ ​i​s​ ​5​
3​:​ ​f​d​ ​i​s​ ​6​
4​:​ ​f​d​ ​i​s​ ​7​
5​:​ ​f​d​ ​i​s​ ​8​
6​:​ ​f​d​ ​i​s​ ​9​
7​:​ ​f​d​ ​i​s​ ​-​1​

$​ ​.​/​o​p​e​n​f​i​l​e​s​ ​1​0​0​0​0​0​0​
0​:​ ​f​d​ ​i​s​ ​3​
1​:​ ​f​d​ ​i​s​ ​4​
2​:​ ​f​d​ ​i​s​ ​5​
.​.​.​
2​5​7​:​ ​f​d​ ​i​s​ ​2​6​0​
2​5​8​:​ ​f​d​ ​i​s​ ​2​6​1​
2​5​9​:​ ​f​d​ ​i​s​ ​2​6​2​

Setting lower resource limits can be useful when you are spawning off other programs, discussed in Chapter 18: Multiprocessing. If you do not trust the other programs, or want to constrain their limits, you can fork, call setrlimit() on yourself in the child process, and then exec the child process, which will run under the reduced limits.

Miscellaneous tools

The heap command lists all the malloc()-allocated buffers in the heap of a program. Give it the PID or name of the program to examine.

heap is interesting when pointed at a Cocoa program because it shows Objective-C classes. Here is stuff from objectleak.m (edited down):

Z​o​n​e​ ​D​e​f​a​u​l​t​M​a​l​l​o​c​Z​o​n​e​_​0​x​7​0​0​0​:​ ​
Z​o​n​e​ ​D​e​f​a​u​l​t​M​a​l​l​o​c​Z​o​n​e​_​0​x​1​0​0​0​0​4​0​0​0​:​ ​O​v​e​r​a​l​l​ ​s​i​z​e​:​ ​9​2​1​5​K​B​;​ ​
 ​ ​ ​ ​ ​9​2​5​ ​n​o​d​e​s​ ​m​a​l​l​o​c​e​d​ ​f​o​r​ ​2​0​8​K​B​ ​(​2​%​ ​o​f​ ​c​a​p​a​c​i​t​y​)​;​
 ​ ​ ​ ​ ​l​a​r​g​e​s​t​ ​u​n​u​s​e​d​:​ ​[​0​x​1​0​0​8​2​6​8​0​0​-​8​0​3​7​K​B​]​

Z​o​n​e​ ​D​e​f​a​u​l​t​M​a​l​l​o​c​Z​o​n​e​_​0​x​7​0​0​0​:​ ​5​5​8​ ​n​o​d​e​s​ ​-​ ​
3​2​K​B​[​4​]​ ​8​K​B​[​2​]​ ​4​K​B​[​2​]​ ​.​.​.​ ​4​8​[​6​6​]​ ​3​2​[​2​5​8​]​ ​1​6​[​2​4​6​]​ ​

F​o​u​n​d​ ​4​9​0​ ​O​b​j​C​ ​c​l​a​s​s​e​s​ ​i​n​ ​p​r​o​c​e​s​s​ ​5​3​0​7​
F​o​u​n​d​ ​4​0​ ​C​F​T​y​p​e​s​ ​i​n​ ​p​r​o​c​e​s​s​ ​5​3​0​7​

-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
Z​o​n​e​ ​D​e​f​a​u​l​t​M​a​l​l​o​c​Z​o​n​e​_​0​x​7​0​0​0​:​ ​5​5​8​ ​n​o​d​e​s​ ​(​9​2​5​6​0​ ​b​y​t​e​s​)​ ​

 ​ ​ ​ ​C​O​U​N​T​ ​ ​ ​ ​ ​B​Y​T​E​S​ ​ ​ ​ ​ ​ ​ ​A​V​G​ ​ ​ ​C​L​A​S​S​_​N​A​M​E​ ​ ​ ​ ​ ​ ​T​Y​P​E​ ​ ​ ​ ​B​I​N​A​R​Y​
 ​ ​ ​ ​=​=​=​=​=​ ​ ​ ​ ​ ​=​=​=​=​=​ ​ ​ ​ ​ ​ ​ ​=​=​=​ ​ ​ ​=​=​=​=​=​=​=​=​=​=​ ​ ​ ​ ​ ​ ​=​=​=​=​ ​ ​ ​ ​=​=​=​=​=​=​
 ​ ​ ​ ​ ​ ​8​0​3​ ​ ​ ​ ​1​5​6​9​9​2​ ​ ​ ​ ​ ​1​9​5​.​5​ ​ ​ ​n​o​n​-​o​b​j​e​c​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​5​1​ ​ ​ ​ ​ ​ ​1​9​3​6​ ​ ​ ​ ​ ​ ​3​8​.​0​ ​ ​ ​N​S​C​F​S​t​r​i​n​g​ ​ ​ ​ ​ ​ ​O​b​j​C​ ​ ​ ​ ​F​o​u​n​d​a​t​i​o​n​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​2​0​ ​ ​ ​ ​ ​ ​ ​6​4​0​ ​ ​ ​ ​ ​ ​3​2​.​0​ ​ ​ ​N​S​C​F​N​u​m​b​e​r​ ​ ​ ​ ​ ​ ​O​b​j​C​ ​ ​ ​ ​F​o​u​n​d​a​t​i​o​n​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​1​0​ ​ ​ ​ ​ ​ ​ ​6​4​0​ ​ ​ ​ ​ ​ ​6​4​.​0​ ​ ​ ​N​S​C​F​D​i​c​t​i​o​n​a​r​y​ ​ ​O​b​j​C​ ​ ​ ​ ​F​o​u​n​d​a​t​i​o​n​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​8​ ​ ​ ​ ​ ​ ​ ​5​1​2​ ​ ​ ​ ​ ​ ​6​4​.​0​ ​ ​ ​C​F​B​a​s​i​c​H​a​s​h​ ​ ​ ​ ​ ​C​F​T​y​p​e​ ​ ​C​o​r​e​F​o​u​n​d​a​t​i​o​n​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​5​ ​ ​ ​ ​ ​ ​ ​5​6​0​ ​ ​ ​ ​ ​1​1​2​.​0​ ​ ​ ​N​S​R​e​c​u​r​s​i​v​e​L​o​c​k​ ​O​b​j​C​ ​ ​ ​ ​F​o​u​n​d​a​t​i​o​n​ ​ ​ ​ ​ ​ ​ ​ ​

We can see that we have a number of strings and numbers allocated.

malloc_history will show you a history of memory activity. This requires that you set MallocStackLogging to 1 in your environment before running.

Example 7.9 is a program that does some memory manipulations and then sleeps:

Example 7.9. mallochistory.m

/​/​ ​m​a​l​l​o​c​h​i​s​t​o​r​y​.​m​ ​-​-​ ​D​o​ ​s​o​m​e​ ​m​e​m​o​r​y​ ​a​l​l​o​c​a​t​i​o​n​ ​t​o​ ​s​h​o​w​ ​o​f​f​ ​m​a​l​l​o​c​_​h​i​s​t​o​r​y​.​
/​/​ ​B​e​ ​s​u​r​e​ ​t​o​ ​s​e​t​ ​t​h​e​ ​e​n​v​i​r​o​n​m​e​n​t​ ​v​a​r​i​a​b​l​e​ ​M​a​l​l​o​c​S​t​a​c​k​L​o​g​g​i​n​g​N​o​C​o​m​p​a​c​t​ ​t​o​ ​1​.​
/​/​ ​T​h​e​n​ ​r​u​n​ ​t​h​i​s​ ​p​r​o​g​r​a​m​,​ ​a​n​d​ ​w​h​i​l​e​
/​/​ ​i​t​ ​s​l​e​e​p​s​ ​a​t​ ​t​h​e​ ​e​n​d​,​ ​r​u​n​ ​'​m​a​l​l​o​c​_​h​i​s​t​o​r​y​ ​p​i​d​ ​-​a​l​l​_​b​y​_​s​i​z​e​'​ ​o​r​
/​/​ ​'​m​a​l​l​o​c​_​h​i​s​t​o​r​y​ ​p​i​d​ ​-​a​l​l​_​b​y​_​c​o​u​n​t​'​

/​/​ ​g​c​c​ ​-​g​ ​-​s​t​d​=​c​9​9​ ​-​W​a​l​l​ ​-​o​ ​m​a​l​l​o​c​h​i​s​t​o​r​y​ ​m​a​l​l​o​c​h​i​s​t​o​r​y​.​m​

#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​g​e​t​p​i​d​(​)​,​ ​s​l​e​e​p​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​m​a​l​l​o​c​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​

v​o​i​d​ ​f​u​n​c​2​ ​(​)​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​*​s​t​u​f​f​;​

 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​3​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​u​f​f​ ​=​ ​m​a​l​l​o​c​ ​(​5​0​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​r​e​e​ ​(​s​t​u​f​f​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​s​t​u​f​f​ ​=​ ​m​a​l​l​o​c​ ​(​5​0​)​;​
 ​ ​ ​ ​/​/​ ​s​o​ ​w​e​ ​c​a​n​ ​u​s​e​ ​t​h​e​ ​m​a​l​l​o​c​_​h​i​s​t​o​r​y​ ​a​d​d​r​e​s​s​ ​f​e​a​t​u​r​e​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​a​d​d​r​e​s​s​ ​o​f​ ​s​t​u​f​f​ ​i​s​ ​%​p​\​n​"​,​ ​s​t​u​f​f​)​;​
 ​ ​ ​ ​/​/​ ​i​n​t​e​n​t​i​o​n​a​l​l​y​ ​l​e​a​k​ ​s​t​u​f​f​
}​ ​/​/​ ​f​u​n​c​2​

v​o​i​d​ ​f​u​n​c​1​ ​(​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​*​n​u​m​b​e​r​s​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​i​n​t​)​ ​*​ ​1​0​0​)​;​
 ​ ​ ​ ​f​u​n​c​2​ ​(​)​;​
 ​ ​ ​ ​/​/​ ​i​n​t​e​n​t​i​o​n​a​l​l​y​ ​l​e​a​k​ ​n​u​m​b​e​r​s​
}​ ​/​/​ ​f​u​n​c​1​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​m​y​ ​p​r​o​c​e​s​s​ ​i​d​ ​i​s​ ​%​d​\​n​"​,​ ​g​e​t​p​i​d​(​)​)​;​
 ​ ​ ​ ​f​u​n​c​1​ ​(​)​;​

 ​ ​ ​ ​s​l​e​e​p​ ​(​6​0​0​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

When run, this program prints:

$​ ​e​x​p​o​r​t​ ​M​a​l​l​o​c​S​t​a​c​k​L​o​g​g​i​n​g​N​o​C​o​m​p​a​c​t​=​1​
$​ ​.​/​m​a​l​l​o​c​h​i​s​t​o​r​y​
m​a​l​l​o​c​h​i​s​t​o​r​y​(​5​8​8​9​)​ ​m​a​l​l​o​c​:​ ​r​e​c​o​r​d​i​n​g​ ​m​a​l​l​o​c​ ​s​t​a​c​k​s​ ​t​o​ ​d​i​s​k​ ​u​s​i​n​g​ ​s​t​a​n​d​a​r​d​ ​r​e​c​o​r​d​e​r​
m​a​l​l​o​c​h​i​s​t​o​r​y​(​5​8​8​9​)​ ​m​a​l​l​o​c​:​ ​s​t​a​c​k​ ​l​o​g​g​i​n​g​ ​c​o​m​p​a​c​t​i​o​n​ ​t​u​r​n​e​d​ ​o​f​f​;​ ​
 ​ ​ ​s​i​z​e​ ​o​f​ ​l​o​g​ ​f​i​l​e​s​ ​o​n​ ​d​i​s​k​ ​c​a​n​ ​i​n​c​r​e​a​s​e​ ​r​a​p​i​d​l​y​
a​d​d​r​e​s​s​ ​o​f​ ​s​t​u​f​f​ ​i​s​ ​0​x​1​0​0​1​0​0​2​e​0​

Then in another terminal, see who has manipulated the block:

$​ ​m​a​l​l​o​c​_​h​i​s​t​o​r​y​ ​5​8​8​9​ ​0​x​1​0​0​1​0​0​2​e​0​

C​a​l​l​ ​[​2​]​ ​[​a​r​g​=​5​0​]​:​ ​t​h​r​e​a​d​_​a​0​4​c​d​f​a​0​ ​|​0​x​1​ ​|​ ​s​t​a​r​t​ ​|​ ​m​a​i​n​ ​|​ ​f​u​n​c​1​ ​
 ​ ​|​ ​f​u​n​c​2​ ​|​ ​m​a​l​l​o​c​ ​|​ ​m​a​l​l​o​c​_​z​o​n​e​_​m​a​l​l​o​c​ ​

You can also see what stuff is currently allocated and who did it. In this case, the stack entries are ordered by size. Stuff that is purely overhead has been removed.

$​ ​m​a​l​l​o​c​_​h​i​s​t​o​r​y​ ​5​8​8​9​ ​-​a​l​l​_​b​y​_​s​i​z​e​

Looks like printf() and friends use a 4K buffer to do their work:

1​ ​c​a​l​l​s​ ​f​o​r​ ​4​0​9​6​ ​b​y​t​e​s​:​ ​t​h​r​e​a​d​_​a​0​4​c​d​f​a​0​ ​|​0​x​1​ ​|​ ​s​t​a​r​t​ ​|​ ​m​a​i​n​ ​|​ ​p​r​i​n​t​f​ ​
 ​ ​ ​|​ ​v​f​p​r​i​n​t​f​_​l​ ​|​ ​_​_​v​f​p​r​i​n​t​f​ ​|​ ​_​_​s​w​s​e​t​u​p​ ​|​ ​_​_​s​m​a​k​e​b​u​f​ ​|​ ​m​a​l​l​o​c​ ​
 ​ ​ ​|​ ​m​a​l​l​o​c​_​z​o​n​e​_​m​a​l​l​o​c​ ​

Here is the numbers array:

1​ ​c​a​l​l​s​ ​f​o​r​ ​4​0​0​ ​b​y​t​e​s​:​ ​t​h​r​e​a​d​_​a​0​4​c​d​f​a​0​ ​|​ ​s​t​a​r​t​ ​|​ ​m​a​i​n​ ​|​ ​f​u​n​c​1​ ​
 ​ ​|​ ​m​a​l​l​o​c​ ​|​ ​m​a​l​l​o​c​_​z​o​n​e​_​m​a​l​l​o​c​ ​

And the final malloc() from func2().

1​ ​c​a​l​l​s​ ​f​o​r​ ​5​0​ ​b​y​t​e​s​:​ ​t​h​r​e​a​d​_​a​0​4​c​d​f​a​0​ ​|​ ​s​t​a​r​t​ ​|​ ​m​a​i​n​ ​|​ ​f​u​n​c​1​ ​
 ​ ​|​ ​f​u​n​c​2​ ​|​ ​m​a​l​l​o​c​ ​|​ ​m​a​l​l​o​c​_​z​o​n​e​_​m​a​l​l​o​c​ ​

You can get info about a block from inside of gdb with the info malloc command:

$​ ​e​x​p​o​r​t​ ​M​a​l​l​o​c​S​t​a​c​k​L​o​g​g​i​n​g​N​o​C​o​m​p​a​c​t​=​1​
$​ ​g​d​b​ ​.​/​m​a​l​l​o​c​h​i​s​t​o​r​y​
(​g​d​b​)​ ​r​u​n​
.​.​.​
a​d​d​r​e​s​s​ ​o​f​ ​s​t​u​f​f​ ​i​s​ ​0​x​1​0​0​1​0​0​2​e​0​
^​C​
(​g​d​b​)​ ​i​n​f​o​ ​m​a​l​l​o​c​ ​0​x​1​0​0​1​0​0​2​e​0​
A​l​l​o​c​:​ ​B​l​o​c​k​ ​a​d​d​r​e​s​s​:​ ​0​x​0​0​0​0​0​0​0​1​0​0​1​0​0​2​e​0​ ​l​e​n​g​t​h​:​ ​5​0​
S​t​a​c​k​ ​-​ ​p​t​h​r​e​a​d​:​ ​0​x​7​f​f​f​7​0​c​8​e​c​2​0​ ​n​u​m​b​e​r​ ​o​f​ ​f​r​a​m​e​s​:​ ​6​
 ​ ​ ​ ​0​:​ ​0​x​7​f​f​f​8​5​6​9​5​f​2​e​ ​i​n​ ​m​a​l​l​o​c​_​z​o​n​e​_​m​a​l​l​o​c​
 ​ ​ ​ ​1​:​ ​0​x​7​f​f​f​8​5​6​9​4​2​0​8​ ​i​n​ ​m​a​l​l​o​c​
 ​ ​ ​ ​2​:​ ​0​x​1​0​0​0​0​0​d​c​3​ ​i​n​ ​f​u​n​c​2​ ​a​t​ ​m​a​l​l​o​c​h​i​s​t​o​r​y​.​m​:​1​9​
 ​ ​ ​ ​3​:​ ​0​x​1​0​0​0​0​0​e​1​e​ ​i​n​ ​f​u​n​c​1​ ​a​t​ ​m​a​l​l​o​c​h​i​s​t​o​r​y​.​m​:​3​9​
 ​ ​ ​ ​4​:​ ​0​x​1​0​0​0​0​0​e​5​1​ ​i​n​ ​m​a​i​n​ ​a​t​ ​m​a​l​l​o​c​h​i​s​t​o​r​y​.​m​:​4​6​
 ​ ​ ​ ​5​:​ ​0​x​1​0​0​0​0​0​d​a​0​ ​i​n​ ​s​t​a​r​t​
D​e​a​l​l​o​c​:​ ​B​l​o​c​k​ ​a​d​d​r​e​s​s​:​ ​0​x​0​0​0​0​0​0​0​1​0​0​1​0​0​2​e​0​
S​t​a​c​k​ ​-​ ​p​t​h​r​e​a​d​:​ ​0​x​7​f​f​f​7​0​c​8​e​c​2​0​ ​n​u​m​b​e​r​ ​o​f​ ​f​r​a​m​e​s​:​ ​5​
 ​ ​ ​ ​0​:​ ​0​x​7​f​f​f​8​5​6​9​8​6​3​e​ ​i​n​ ​f​r​e​e​
 ​ ​ ​ ​1​:​ ​0​x​1​0​0​0​0​0​d​d​0​ ​i​n​ ​f​u​n​c​2​ ​a​t​ ​m​a​l​l​o​c​h​i​s​t​o​r​y​.​m​:​1​8​
 ​ ​ ​ ​2​:​ ​0​x​1​0​0​0​0​0​e​1​e​ ​i​n​ ​f​u​n​c​1​ ​a​t​ ​m​a​l​l​o​c​h​i​s​t​o​r​y​.​m​:​3​9​
 ​ ​ ​ ​3​:​ ​0​x​1​0​0​0​0​0​e​5​1​ ​i​n​ ​m​a​i​n​ ​a​t​ ​m​a​l​l​o​c​h​i​s​t​o​r​y​.​m​:​4​6​
 ​ ​ ​ ​4​:​ ​0​x​1​0​0​0​0​0​d​a​0​ ​i​n​ ​s​t​a​r​t​
.​.​.​

This shows the stack traces, with file and lines when debug information is known, for each malloc history occurrence.

Instruments also has extensive tools for monitoring and analyzing memory allocation.

vm_stat

vm_stat shows some Mach virtual memory statistics. Here it is for my system right now:

M​a​c​h​ ​V​i​r​t​u​a​l​ ​M​e​m​o​r​y​ ​S​t​a​t​i​s​t​i​c​s​:​ ​(​p​a​g​e​ ​s​i​z​e​ ​o​f​ ​4​0​9​6​ ​b​y​t​e​s​)​
M​a​c​h​ ​V​i​r​t​u​a​l​ ​M​e​m​o​r​y​ ​S​t​a​t​i​s​t​i​c​s​:​ ​(​p​a​g​e​ ​s​i​z​e​ ​o​f​ ​4​0​9​6​ ​b​y​t​e​s​)​
P​a​g​e​s​ ​f​r​e​e​:​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​4​3​2​7​2​.​
P​a​g​e​s​ ​a​c​t​i​v​e​:​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​2​5​1​9​1​8​.​
P​a​g​e​s​ ​i​n​a​c​t​i​v​e​:​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​0​7​1​8​9​.​
P​a​g​e​s​ ​s​p​e​c​u​l​a​t​i​v​e​:​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​4​5​7​1​6​5​.​
P​a​g​e​s​ ​w​i​r​e​d​ ​d​o​w​n​:​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​2​3​8​5​4​.​
"​T​r​a​n​s​l​a​t​i​o​n​ ​f​a​u​l​t​s​"​:​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​6​3​2​4​0​1​7​8​9​.​
P​a​g​e​s​ ​c​o​p​y​-​o​n​-​w​r​i​t​e​:​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​2​3​8​1​8​9​2​.​
P​a​g​e​s​ ​z​e​r​o​ ​f​i​l​l​e​d​:​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​5​4​4​1​9​7​4​7​.​
P​a​g​e​s​ ​r​e​a​c​t​i​v​a​t​e​d​:​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​7​8​7​2​3​6​.​
P​a​g​e​i​n​s​:​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​4​7​2​1​4​9​.​
P​a​g​e​o​u​t​s​:​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​8​0​0​6​9​1​.​
O​b​j​e​c​t​ ​c​a​c​h​e​:​ ​8​7​ ​h​i​t​s​ ​o​f​ ​6​4​8​9​5​1​ ​l​o​o​k​u​p​s​ ​(​0​%​ ​h​i​t​ ​r​a​t​e​)​

You could run vm_stat at different points in time to see if your system is swapping (lots of pageins and pageouts). The manpage has information on each of the entries.

Objective-C Garbage Collection

Objective-C 2.0 adds garbage collection to the language as a replacement to the traditional retain / release reference counting scheme. Garbage collection is automatic object memory management. The garbage collector figures out what objects are being referred to by other objects, and the collector will destroy the object if nobody has any interest in it. This is familiar behavior to programmers who have used languages like Java or Python, where memory management “just happens.”

Objective-C garbage collection is opt-in. You can choose to use GC or continue to use reference counting. iOS does not currently support GC, so you must use reference counting there. Apple recommends using GC for new apps, but you can retrofit older applications to use it. Xcode is an example of a large application that has been retrofitted to use GC. Adopting GC limits you to OS X 10.5 or later since it was first introduced in Leopard. It can also be difficult to adopt if your existing application uses a lot of non-Cocoa code, such as Carbon or C++ libraries.

Objective-C GC is designed for Objective-C code and Objective-C objects since that is where most of the code for the platform is being written and optimized. Because Objective-C is a superset of C, we still have pointers, and those can complicate some memory management scenarios.

Apple’s GC is a conservative, generational, and concurrent garbage collector. A conservative garbage collector never copies or moves pointers. Once you allocate memory, it sticks to the address where it first lands. This means you can allocate a buffer and then pass it to C functions and not worry about the memory floating away unexpectedly.

A generational garbage collector assumes that new objects turn to garbage quickly. Objects tend to stick around as they get older. A generational collector concentrates on new objects when looking for garbage and only looks at older objects a fraction of the time rather than scanning every object on every garbage collection cycle. Apple claims that 90% of objects are recovered with 10% of the effort when using the generational model.

A concurrent garbage collector runs on its own thread. It can also be collecting objects while other objects are being allocated. Apple’s collector doesn’t stop all the threads at once when collecting, and it tries not to interrupt any thread for very long. Threads that don’t use any Objective-C code won’t be stopped at all by the collector. There is a thread-local collector for each thread, which also improves GC concurrency.

How to use it

Turning on GC is easy. Just turn on the Objective-C Garbage Collection setting in Xcode’s build settings. Use the Required setting for new projects, which is equivalent to the gcc command-line flag -fobjc-gc-only. This means that all of your code will run in a GC environment, and all frameworks and plug-ins must support garbage collection. The Supported setting is for frameworks that can run in either GC mode or with reference counting. -fobjc-gc is the command-line flag to use for this case.

To verify that garbage collection is enabled, you can ask NSGarbageCollector:

i​f​ ​(​[​N​S​G​a​r​b​a​g​e​C​o​l​l​e​c​t​o​r​ ​d​e​f​a​u​l​t​C​o​l​l​e​c​t​o​r​]​ ​!​=​ ​n​i​l​)​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​G​C​ ​a​c​t​i​v​e​"​)​;​
}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​G​C​ ​n​o​t​ ​a​c​t​i​v​e​"​)​;​
}​

Assignment of pointers becomes memory management once GC is enabled. Example 7.10 contains a class that has a string instance variable. An instance of the class is created and assigned to a stack-local variable. The collector is run, and nothing happens. There is still a valid reference to the object. The local variable is then set to a nil value, and the collector is run again. The collector realizes that there are no more references to the object, calls the -finalize method, which prints out that the object is dying, and then the memory is reclaimed.

Example 7.10. gc-sample.m

/​/​ ​g​c​-​s​a​m​p​l​e​.​m​ ​-​-​ ​A​ ​s​i​m​p​l​e​ ​G​C​ ​a​p​p​ ​t​h​a​t​ ​s​h​o​w​s​ ​i​t​ ​w​o​r​k​i​n​g​.​

/​/​ ​g​c​c​ ​-​f​o​b​j​c​-​g​c​-​o​n​l​y​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​o​ ​g​c​-​s​a​m​p​l​e​ ​g​c​-​s​a​m​p​l​e​.​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​
#​i​m​p​o​r​t​ ​<​o​b​j​c​/​o​b​j​c​-​a​u​t​o​.​h​>​ ​ ​/​/​ ​f​o​r​ ​G​C​ ​r​u​n​t​i​m​e​ ​A​P​I​

@​i​n​t​e​r​f​a​c​e​ ​S​n​o​r​g​l​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​{​
 ​ ​ ​ ​i​n​t​ ​n​u​m​b​e​r​;​
}​
-​ ​(​i​d​)​ ​i​n​i​t​W​i​t​h​N​u​m​b​e​r​:​ ​(​i​n​t​)​ ​n​u​m​;​
@​e​n​d​ ​/​/​ ​S​n​o​r​g​l​e​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​S​n​o​r​g​l​e​

-​ ​(​i​d​)​ ​i​n​i​t​W​i​t​h​N​u​m​b​e​r​:​ ​(​i​n​t​)​ ​n​u​m​ ​{​
 ​ ​ ​ ​i​f​ ​(​(​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​n​u​m​b​e​r​ ​=​ ​n​u​m​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​(​s​e​l​f​)​;​
}​ ​/​/​ ​i​n​i​t​W​i​t​h​N​u​m​b​e​r​

-​ ​(​v​o​i​d​)​f​i​n​a​l​i​z​e​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​f​i​n​a​l​i​z​e​d​ ​%​d​"​,​ ​n​u​m​b​e​r​)​;​
 ​ ​ ​ ​[​s​u​p​e​r​ ​f​i​n​a​l​i​z​e​]​;​
}​ ​/​/​ ​f​i​n​a​l​i​z​e​

@​e​n​d​ ​/​/​ ​S​n​o​r​g​l​e​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​N​S​G​a​r​b​a​g​e​C​o​l​l​e​c​t​o​r​ ​*​g​c​ ​=​ ​[​N​S​G​a​r​b​a​g​e​C​o​l​l​e​c​t​o​r​ ​d​e​f​a​u​l​t​C​o​l​l​e​c​t​o​r​]​;​

 ​ ​ ​ ​i​f​ ​(​g​c​ ​!​=​ ​n​i​l​)​ ​N​S​L​o​g​ ​(​@​"​G​C​ ​a​c​t​i​v​e​"​)​;​

 ​ ​ ​ ​o​b​j​c​_​s​t​a​r​t​C​o​l​l​e​c​t​o​r​T​h​r​e​a​d​ ​(​)​;​

 ​ ​ ​ ​S​n​o​r​g​l​e​ ​*​s​n​1​ ​=​ ​[​[​S​n​o​r​g​l​e​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​N​u​m​b​e​r​:​ ​1​]​;​

 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​o​l​l​e​c​t​ ​w​i​t​h​ ​s​n​o​r​g​l​e​ ​o​b​j​e​c​t​ ​s​t​i​l​l​ ​l​i​v​e​.​"​)​;​
 ​ ​ ​ ​[​g​c​ ​c​o​l​l​e​c​t​E​x​h​a​u​s​t​i​v​e​l​y​]​;​
 ​ ​ ​ ​s​l​e​e​p​ ​(​2​)​;​

 ​ ​ ​ ​s​n​1​ ​=​ ​n​i​l​;​

 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​o​l​l​e​c​t​ ​a​f​t​e​r​ ​r​e​m​o​v​i​n​g​ ​r​e​f​e​r​e​n​c​e​"​)​;​
 ​ ​ ​ ​[​g​c​ ​c​o​l​l​e​c​t​E​x​h​a​u​s​t​i​v​e​l​y​]​;​
 ​ ​ ​ ​s​l​e​e​p​ ​(​2​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​

}​ ​/​/​ ​m​a​i​n​

Most applications you will write won’t need to explicitly invoke the collector or start the collector thread as is done here. NSApplication takes care of those details for you. There’s a little more work required on the part of command-line tools that do not have a user interface.

Here is Example 7.10 when run:

$​ ​.​/​g​c​-​s​a​m​p​l​e​
2​0​1​0​-​0​6​-​2​2​ ​2​3​:​3​3​:​2​3​.​8​6​1​ ​g​c​-​s​a​m​p​l​e​[​6​0​2​3​:​9​0​3​]​ ​G​C​ ​a​c​t​i​v​e​
2​0​1​0​-​0​6​-​2​2​ ​2​3​:​3​3​:​2​3​.​8​6​4​ ​g​c​-​s​a​m​p​l​e​[​6​0​2​3​:​9​0​3​]​ ​c​o​l​l​e​c​t​ ​w​i​t​h​ ​s​n​o​r​g​l​e​ ​o​b​j​e​c​t​ ​s​t​i​l​l​ ​l​i​v​e​.​
2​0​1​0​-​0​6​-​2​2​ ​2​3​:​3​3​:​2​5​.​8​6​4​ ​g​c​-​s​a​m​p​l​e​[​6​0​2​3​:​9​0​3​]​ ​c​o​l​l​e​c​t​ ​a​f​t​e​r​ ​r​e​m​o​v​i​n​g​ ​r​e​f​e​r​e​n​c​e​
2​0​1​0​-​0​6​-​2​2​ ​2​3​:​3​3​:​2​5​.​8​6​5​ ​g​c​-​s​a​m​p​l​e​[​6​0​2​3​:​9​0​3​]​ ​f​i​n​a​l​i​z​e​d​ ​1​

The act of assigning an object to a pointer causes it to stay alive. Removing the reference to the object, whether by assigning the pointer to nil or by pointing it to another object, causes the object to be collected eventually.

Accessor methods become trivial to write in the GC world. Just assign the new value to your instance variable and return the value:

-​ ​(​v​o​i​d​)​ ​s​e​t​B​l​a​h​:​ ​(​B​l​a​h​ ​*​)​ ​n​e​w​B​l​a​h​ ​{​
 ​ ​ ​ ​b​l​a​h​ ​=​ ​n​e​w​B​l​a​h​;​
}​ ​/​/​ ​s​e​t​B​l​a​h​

-​ ​(​B​l​a​h​ ​*​)​ ​b​l​a​h​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​b​l​a​h​;​
}​ ​/​/​ ​b​l​a​h​

There is no need to check that you are re-setting a previous value. There is also no need to use @synchronized to get atomic assignments, as those are handled automatically by the GC machinery.

Retain cycles are a problem when using reference counting memory management. Object A retaining B retaining C retaining A forms a cycle that will never get released. This is why delegate objects are not retained and why child objects do not retain their parents. If you have cycles of objects in GC, with A pointing to B, B pointing to C, and C pointing back to A, the collector will notice when there are no external references to any of those objects and will collect the entire cycle.

Nib loading works differently between the GC and reference-counted worlds. When nib files are loaded in a reference-counted application, top-level objects in the nib file leak by default. They do not get released. These objects will get collected and will go away under GC. If you have a top-level nib object that you do not want to disappear, then you need to store self someplace the collector knows about, such as a global, to get it rooted. For AppController style classes created in MainMenu.nib files, you can make it a delegate of the File’s Owner (NSApplication). This is sufficient to keep it from being collected.

There are some Cocoa subsystems that require explicit disconnection before an object is destroyed. GC can handle some of these, such as unregistering with an NSNotificationCenter. NSNotificationCenter uses weak references to registered objects. Weak references are described below.

Other subsystems, such as timers and KVO, need to disassociate the object. You can do this in -finalize, but see below about problems with -finalize. You can also construct your class interface so that there is an explicit “termination of service,” or shutdown call, which can then unhook from these subsystems.

How it works

Cocoa GC manages all Objective-C objects, can manage Core Foundation objects like CFString and CFArray, and can also manage some forms of non-object memory.

The GC memory model is divided into four areas:

global variables in the static data segment

local variables on thread stacks

the GC heap, where all collectable memory lives

the malloc() zone

The first three play a role in the GC’s behavior, while the malloc() zone is never touched or looked at by the collector.

The collector scans memory to figure out what is alive. It starts with some well-known root locations, specifically globals and local variables, and then scans through memory looking for things. Things to make it go.

instance variables and globals of Objective-C object types

instance variables and globals marked as __strong (There is more on strong and weak references below.)

some thread stacks, those that have an associated NSThread

some heap blocks that have been explicitly allocated as collectable and that should be scanned

The collector scans all of these areas looking for things that look like pointers. It then builds a data structure of connected and unconnected objects. Objects that have a connection up to a root are good. Objects that do not have such a connection are considered garbage and put on a list. After the collector does its scan, it walks the garbage list. It zeroes out weak references to garbage, sends -finalize to every object in the garbage, and reclaims the memory.

The collector does not look at instance variables and globals of C pointer types nor heap blocks from malloc() or vm_allocate(). This can cause problems if you put addresses of collectable objects into malloc’d blocks. The collector will not see these references, so it might collect an object before it is actually garbage.

In OS X 10.6, the GC heap is limited to 32 gigabytes of memory in 64-bit applications. If you need to control more memory than that, perhaps you are loading a lot of graphics tiles or caching a lot of pages from a database. You will want to put the data that does not contain pointers to other objects into the malloc zone and reference the malloc’d memory from collected objects.

You can use NSGarbageCollector methods like -collectIfNeeded and -collectExhaustively to hint to the collector that now would be a good time to do some cleanup. Any place where you performed explicit autorelease pool manipulations would be a good place to tickle the collector. The collector is automatically triggered at the bottom of the application’s event loop.

Strong and weak references

A pointer pointing to a chunk of collectable memory can be a strong or a weak reference. A strong reference tells the collector not to collect the object since someone is using it. The collector finds objects by following strong references. Once all strong reference to an object are removed, the object is collected.

A weak reference tells the collector that you have an interest in the object being pointed to – but not enough of an interest to force the object to stay around once all of its strong references are gone. The pointer to that object is set to nil when a weak-referenced object is collected.

Object pointers are strong by default. You annotate non-object pointers with __strong:

i​n​t​ ​*​ ​_​_​s​t​r​o​n​g​ ​f​o​o​t​C​o​u​n​t​R​e​f​;​

If you got some collectable memory and then assigned it to footCountRef, the collector will make sure the object is not collected. If the __strong were missing from that declaration, the collector would not consider this a reference to the memory. Unless this is a local variable, which is considered a strong reference.

The compiler generates write barriers to catch assignments to variables with strong reference. A write barrier is simply a function call, like objc_assign_ivar() or objc_assign_global(), which can be found in the objc-auto.h header. The write barriers update the collector’s view of the world and then update the variable’s value in memory. Stack-based variables do not cause write barriers to be generated for the sake of efficiency. Instead, the collector scans the entire stack looking for pointers rather than using write barriers.

Weak references are automatically zero-filled when the object it points to is deleted. When accessing a weak reference, you either see the object or you get nil. This means you will not get dangling references.

In addition to generating a write barrier when a weak reference variable is changed, the compiler generates a read barrier. The read barrier catches the case when the pointed-to object has been collected and then zeros out the reference for the case when the collector has not already gotten around to zeroing out the reference by the time you access it.

Weak references are restricted to instance and global variables only. You cannot have a weak local variable. There is nothing the compiler can do to tell the collector when it scans call stacks that a local variable is weak. Weak references are more expensive than strong references because of this read barrier. Strong references are normal memory reads, but weak reference reads go through the read barrier function. Also, threads can block on the read barrier while weak references are being cleared.

The compiler only generates read and write barriers for Objective-C compiled code. .m and .mm files get it for free, but not .c or .cpp files. You can use the barrier calls yourself if you need to.

Finalize methods

The -dealloc method is never called when using GC. Instead, when an object meets its ultimate demise, the collector calls the object’s -finalize method to let the object do any final cleanup. Finalize methods are not nearly as convenient as they sound and should be avoided if at all possible.

After the collector makes its pass through objects to see what is garbage, it clears out the weak references to the objects. Then the finalize methods are called in an arbitrary order. Messaging objects in your finalizer might find you sending messages to an object that has already been finalized. The finalizers also have to be thread safe. They can be called on the collector thread or in some other thread. Also, your object might be messaged after it was finalized due to finalize methods being called in an arbitrary order. You also cannot depend on when the finalize method will be called.

These restrictions make using finalizers inconvenient for doing non-memory-related cleanup, such as closing files or terminating a network connection. Rather than combining resource management and memory management together, Apple recommends adding a specific “all done” call to your API, like a -closeFile or -terminateNetworkConnection. By doing this, you can get by without having any finalize method at all. If you have memory buffers that need to be collected, consider using an NSMutableData or NSAllocateCollectable(), which will get cleaned up automatically.

Unlike Java, you cannot resurrect an object in its finalize by assigning it somewhere to make the object alive. You will get a runtime error if you try this.

Non Objective-C objects

In addition to Objective-C objects, the collector can deal with Core Foundation objects, as well as arbitrary blocks of collectable memory. CF*Ref objects are collector-disabled GC objects and are not considered GC types, so a Core Foundation pointer is not considered a __strong pointer. If you do want to make a CF object collectable, you should CFRelease() it or call CFMakeCollectable().

The -retain, -release, and -autorelease methods are turned into no-ops when GC is active, being short-circuited in the method dispatch machinery. Therefore, autoreleasing a CF object will not do anything, and the object will leak.

As mentioned earlier, pointers to collectable memory should not be stored in a block of memory received from malloc() because the collector will not scan those blocks to see if the memory reference is still there. You can get a block of collectable memory, though, by calling NSAllocateCollectable():

v​o​i​d​ ​*​_​_​s​t​r​o​n​g​ ​N​S​A​l​l​o​c​a​t​e​C​o​l​l​e​c​t​a​b​l​e​ ​(​N​S​U​I​n​t​e​g​e​r​ ​s​i​z​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​U​I​n​t​e​g​e​r​ ​o​p​t​i​o​n​s​)​;​
v​o​i​d​ ​*​_​_​s​t​r​o​n​g​ ​N​S​R​e​a​l​l​o​c​a​t​e​C​o​l​l​e​c​t​a​b​l​e​ ​(​v​o​i​d​ ​*​p​t​r​,​ ​N​S​U​I​n​t​e​g​e​r​ ​s​i​z​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​U​I​n​t​e​g​e​r​ ​o​p​t​i​o​n​s​)​;​

options can be zero or the bitwise OR of these flags:

NSScannedOption : This tells the collector that it should scan the block looking for pointers to other bits of collectable memory. By default, the collector does not scan blocks returned by these calls.

NSCollectorDisabledOption : This tells the collector not to attempt to collect the block.

Do not use memmove() or memcpy() to move the contents of collected memory around. Instead use objc_memmove_collectable():

v​o​i​d​ ​*​o​b​j​c​_​m​e​m​m​o​v​e​_​c​o​l​l​e​c​t​a​b​l​e​ ​(​v​o​i​d​ ​*​d​s​t​,​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​s​r​c​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​_​t​ ​s​i​z​e​)​;​

This makes sure that the collector will catch any strong reference changes.

External reference counts

Even though GC objects seem like they don’t have a reference count, they actually do, but it defaults to zero. When this reference count is zero, the collector assumes that it is in control of the object and will clean it up when it thinks there are no more strong references to it. When this reference count is non-zero, the collector leaves it alone. You can use this to keep an object alive if you are storing it in a place where the collector will not look.

You increase this reference count by calling CFRetain() or using NSGarbageCollector’s -disableCollectorForPointer. Likewise, decrease the reference count by calling CFRelease() or using NSGarbageCollector’s -enableCollectorForPointer. CFMakeCollectable() is equivalent to CFRelease() but does not look nearly as weird in code as creating a CF object, immediately releasing it, and then proceeding to use the object. If you use the external reference counts, you still need to worry about retain cycles.

The "new" collection classes

Weak references are useful when you are caching data. When the cached data is removed for whatever reason, the weak references to it are zeroed out. It would be nice if the Cocoa collection classes (NSArray, NSSet, etc) supported automatic removal via weak references. Unfortunately, these collections cannot contain nil values.

Luckily, there were a couple of function pointer based quasi-objects that have been in Cocoa since the early NeXTStep days, NSMapTable and NSHashTable. In comparison to the rest of Cocoa, the API was strongly C-flavored and not object-oriented at all. Apple has taken these classes, objectified them, and made them useful in the GC world.

NSMapTable is like a dictionary with keys and values, but you can have keys and/or values that are held weakly. Entries are removed when one of the associated objects is collected. NSHashTable is like a set, with entries that can be removed when the object is collected. A new class, NSPointerArray, has been introduced that behaves like an NSArray, but can hold nil values.

There are many, many ways to configure these collection classes, with over 30 for NSMapTable, to fine-tune their behavior between objects, C pointers, and weak and strong references, whether through the use of flags, or using an NSPointerFunctions object.

NSMapTable provides a set of conveniences for common scenarios:

+​ ​(​i​d​)​ ​m​a​p​T​a​b​l​e​W​i​t​h​S​t​r​o​n​g​T​o​S​t​r​o​n​g​O​b​j​e​c​t​s​;​
+​ ​(​i​d​)​ ​m​a​p​T​a​b​l​e​W​i​t​h​W​e​a​k​T​o​S​t​r​o​n​g​O​b​j​e​c​t​s​;​
+​ ​(​i​d​)​ ​m​a​p​T​a​b​l​e​W​i​t​h​S​t​r​o​n​g​T​o​W​e​a​k​O​b​j​e​c​t​s​;​
+​ ​(​i​d​)​ ​m​a​p​T​a​b​l​e​W​i​t​h​W​e​a​k​T​o​W​e​a​k​O​b​j​e​c​t​s​;​

GC and threads

Threads created via NSThread will have their stacks crawled by the collector looking for active references. Threads created by the pthread API will not be collected unless you make an NSThread call in that thread or create an autorelease pool. That will register the thread with the collector. Performance-critical threads, like audio or video playback, should not be collected. The collector can temporarily interrupt a thread to do its work.

Be aware of race conditions if you pass GC memory from one thread to another. The memory might get collected before the thread starts and can add its own reference. This is a good place to use the external reference counts: increase the external reference count before starting the thread and then decrement it inside of the new thread.

Debugging

The main problems that can come up with GC involve objects that are collected too soon, usually because of a missing write barrier, or objects that are never collected and therefore leak. This is usually because of an unexpected reference.

gdb has two GC-related commands: info gc-references and info gc-roots. Give these commands an object pointer, and they will either tell you what references it or tell you the shortest path from a GC root.

There are a couple of environment variables you can set to control GC behavior::

OBJC_PRINT_GC : set to YES to see the GC status of the application and any loaded libraries.

AUTO_LOG_COLLECTIONS : set to YES to see collections as they happen.

OBJC_USE_TLC : set to NO to turn off the thread-local collector. Some instruments do not play well with the thread-local collector, so you’ll want to turn off TLC if Instruments is behaving oddly.

OBJC_DISABLE_GENERATIONAL : set to YES to turn off the generational algorithm and force the collector to do a full collection every time.

Exercises

Add pool growing to nodepool.m.

Profile nodepool with Shark or Instruments. What’s the major bottleneck for -allocNode? What causes it? How would you fix it?

Find all the errors in Example 7.11, some of which are memory related. (I found nine.)

Example 7.11. memerror.m

/​/​ ​m​e​m​e​r​r​o​r​.​m​ ​-​-​ ​t​r​y​ ​t​o​ ​f​i​n​d​ ​(​a​n​d​ ​f​i​x​!​)​ ​a​l​l​ ​t​h​e​ ​m​e​m​o​r​y​-​r​e​l​a​t​e​d​ ​e​r​r​o​r​s​ ​i​n​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​h​i​s​ ​p​r​o​g​r​a​m​

/​/​ ​T​a​k​e​ ​a​ ​s​t​r​i​n​g​ ​f​r​o​m​ ​t​h​e​ ​c​o​m​m​a​n​d​ ​l​i​n​e​.​ ​ ​M​a​k​e​ ​a​ ​l​i​n​k​e​d​-​l​i​s​t​ ​o​u​t​ ​o​f​ ​i​t​ ​i​n​
/​/​ ​r​e​v​e​r​s​e​ ​o​r​d​e​r​.​ ​ ​T​r​a​v​e​r​s​e​ ​i​t​ ​t​o​ ​c​o​n​s​t​r​u​c​t​ ​a​ ​s​t​r​i​n​g​ ​i​n​ ​r​e​v​e​r​s​e​.​ ​ ​T​h​e​n​ ​c​l​e​a​n​
/​/​ ​u​p​ ​a​f​t​e​r​w​a​r​d​s​.​

/​/​ ​g​c​c​ ​-​w​ ​-​g​ ​-​o​ ​m​e​m​e​r​r​o​r​ ​m​e​m​e​r​r​o​r​.​m​

#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​

t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​C​h​a​r​N​o​d​e​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​t​h​e​C​h​a​r​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​C​h​a​r​N​o​d​e​ ​*​n​e​x​t​;​
}​ ​C​h​a​r​N​o​d​e​;​

/​/​ ​B​u​i​l​d​ ​a​ ​l​i​n​k​e​d​ ​l​i​s​t​ ​b​a​c​k​w​a​r​d​s​,​ ​t​h​e​n​ ​w​a​l​k​ ​t​h​e​ ​l​i​s​t​.​
v​o​i​d​ ​r​e​v​e​r​s​e​I​t​ ​(​c​h​a​r​ ​*​s​t​r​i​n​g​b​u​f​f​e​r​)​ ​{​
 ​ ​ ​ ​C​h​a​r​N​o​d​e​ ​*​h​e​a​d​,​ ​*​n​o​d​e​;​
 ​ ​ ​ ​c​h​a​r​ ​*​s​c​a​n​,​ ​*​s​t​o​p​;​

 ​ ​ ​ ​/​/​ ​C​l​e​a​r​ ​o​u​t​ ​l​o​c​a​l​ ​v​a​r​s​
 ​ ​ ​ ​h​e​a​d​ ​=​ ​n​o​d​e​ ​=​ ​N​U​L​L​;​

 ​ ​ ​ ​/​/​ ​F​i​n​d​ ​t​h​e​ ​s​t​a​r​t​ ​a​n​d​ ​e​n​d​ ​o​f​ ​t​h​e​ ​s​t​r​i​n​g​ ​s​o​ ​w​e​ ​c​a​n​ ​w​a​l​k​ ​i​t​
 ​ ​ ​ ​s​c​a​n​ ​=​ ​s​t​r​i​n​g​b​u​f​f​e​r​;​
 ​ ​ ​ ​s​t​o​p​ ​=​ ​s​t​r​i​n​g​b​u​f​f​e​r​ ​+​ ​s​t​r​l​e​n​(​s​t​r​i​n​g​b​u​f​f​e​r​)​ ​+​ ​1​;​ ​ ​/​/​ ​t​r​a​i​l​i​n​g​ ​n​u​l​l​

 ​ ​ ​ ​/​/​ ​W​a​l​k​ ​t​h​e​ ​s​t​r​i​n​g​
 ​ ​ ​ ​w​h​i​l​e​ ​(​s​c​a​n​ ​<​ ​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​h​e​a​d​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​C​h​a​r​N​o​d​e​*​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​-​>​t​h​e​C​h​a​r​ ​=​ ​*​s​c​a​n​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​-​>​n​e​x​t​ ​=​ ​N​U​L​L​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​C​h​a​r​N​o​d​e​*​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​-​>​t​h​e​C​h​a​r​ ​=​ ​*​s​c​a​n​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​-​>​n​e​x​t​ ​=​ ​h​e​a​d​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​ ​=​ ​n​o​d​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​+​+​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​O​k​,​ ​r​e​-​p​o​i​n​t​ ​t​o​ ​t​h​e​ ​b​u​f​f​e​r​ ​s​o​ ​w​e​ ​c​a​n​ ​d​r​o​p​ ​t​h​e​ ​c​h​a​r​a​c​t​e​r​s​
 ​ ​ ​ ​s​c​a​n​ ​=​ ​s​t​r​i​n​g​b​u​f​f​e​r​;​

 ​ ​ ​ ​/​/​ ​W​a​l​k​ ​t​h​e​ ​n​o​d​e​s​ ​a​n​d​ ​a​d​d​ ​t​h​e​m​ ​t​o​ ​t​h​e​ ​s​t​r​i​n​g​
 ​ ​ ​ ​w​h​i​l​e​ ​(​h​e​a​d​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​*​s​c​a​n​ ​=​ ​h​e​a​d​-​>​t​h​e​C​h​a​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​r​e​e​ ​(​h​e​a​d​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​ ​=​ ​h​e​a​d​-​>​n​e​x​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​ ​=​ ​n​o​d​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​+​+​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​C​l​e​a​n​ ​u​p​ ​t​h​e​ ​h​e​a​d​
 ​ ​ ​ ​f​r​e​e​ ​(​h​e​a​d​)​;​

}​ ​/​/​ ​r​e​v​e​r​s​e​I​t​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​*​s​t​r​i​n​g​b​u​f​f​e​r​;​

 ​ ​ ​ ​/​/​ ​M​a​k​e​ ​s​u​r​e​ ​t​h​e​ ​u​s​e​r​ ​s​u​p​p​l​i​e​d​ ​e​n​o​u​g​h​ ​a​r​g​u​m​e​n​t​s​.​ ​ ​I​f​ ​n​o​t​,​ ​c​o​m​p​l​a​i​n​.​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​!​=​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​s​a​g​e​:​ ​%​s​ ​s​t​r​i​n​g​.​ ​ ​T​h​i​s​ ​r​e​v​e​r​s​e​s​ ​t​h​e​ ​s​t​r​i​n​g​ ​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​g​i​v​e​n​ ​o​n​ ​t​h​e​ ​c​o​m​m​a​n​d​ ​l​i​n​e​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​1​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​M​a​k​e​ ​a​ ​c​o​p​y​ ​o​f​ ​t​h​e​ ​a​r​g​u​m​e​n​t​ ​s​o​ ​w​e​ ​c​a​n​ ​m​a​k​e​ ​c​h​a​n​g​e​s​ ​t​o​ ​i​t​.​
 ​ ​ ​ ​s​t​r​i​n​g​b​u​f​f​e​r​ ​=​ ​m​a​l​l​o​c​ ​(​s​t​r​l​e​n​(​a​r​g​v​[​1​]​)​)​;​
 ​ ​ ​ ​s​t​r​c​p​y​ ​(​a​r​g​v​[​1​]​,​ ​s​t​r​i​n​g​b​u​f​f​e​r​)​;​

 ​ ​ ​ ​/​/​ ​r​e​v​e​r​s​e​ ​t​h​e​ ​s​t​r​i​n​g​
 ​ ​ ​ ​r​e​v​e​r​s​e​I​t​ ​(​s​t​r​i​n​g​b​u​f​f​e​r​)​;​

 ​ ​ ​ ​/​/​ ​a​n​d​ ​p​r​i​n​t​ ​i​t​ ​o​u​t​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​t​h​e​ ​r​e​v​e​r​s​e​d​ ​s​t​r​i​n​g​ ​i​s​ ​'​%​s​'​\​n​"​,​ ​*​s​t​r​i​n​g​b​u​f​f​e​r​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

8
Debugging With GDB

What Is a Debugger?

A debugger is a program that runs your program and has the power to suspend its execution and poke around in memory, examining and changing memory values. It can catch your program after it runs into trouble so you can investigate the problem. Debuggers know about the data structures you are using and can display those structures in an intelligent way. You can experiment with your program, and you can also step through someone else’s code to figure out how it works.

Mac OS X comes with gdb, the GNU project’s debugger, which has a long heritage dating back to 1988. It is fundamentally a command-line oriented tool, but it has been extended over the years to make integration into IDEs, like Xcode and emacs, pretty easy.

To effectively use the debugger, your program needs to be compiled with debugging symbols enabled (usually by giving the -g flag to the compiler). These debugging symbols include lookup tables that map addresses in memory to the appropriate source file and line of code as well as data type information for the program’s custom data structures. You can freely mix code which has debug symbols and no debug symbols. gdb will do its best to present a reasonable view of the world. Understandably, it will not be able to do much with code that has not been compiled with debug symbols.

Full documentation for gdb can be found online in the Mac OS X Reference Library, part of Apple’s Mac Dev Center. gdb has a positively huge feature list, and this chapter will just hit the highlights.

Using GDB from the Command Line

Let’s start by learning to drive gdb from the command line. Why waste time with gdb’s command-line mode? Historically, gdb has been a command-line program. GUIs layered on top of gdb never export all of its features: to wield its full power, you must learn to use its command-line interface. From the command line, you can leverage all of gdb’s features, both common and esoteric. Luckily, Xcode gives you a console pane to interact with gdb’s command line, so you have the best of both worlds there.

Being comfortable at the gdb command line also makes gdb more useful when you want to do remote debugging, that is, running your program and the debugger on another machine over an ssh connection. Xcode has some remote debugging facilities, but it requires that Xcode be installed on both the local and remote machines. gdb is also available for many Unix platforms, so once you’ve become comfortable with using the gdb command line under Mac OS X, your debugging skills also apply to those other platforms.

A sample GDB session

At the end of Chapter 7: Memory is a challenge to find all nine errors in the program memerror, which reverses a string given to the program as a command-line argument.

Here we will use gdb to track down some of the errors.

Compile the program and make sure the -g flag is used to turn on debug symbols:

$​ ​g​c​c​ ​-​g​ ​-​w​ ​-​o​ ​m​e​m​e​r​r​o​r​ ​m​e​m​e​r​r​o​r​.​m​

If you are using Xcode, make sure that no optimizations are turned on. Otherwise, single-stepping will behave erratically.

Trying to run the program gives this:

$​ ​.​/​m​e​m​e​r​r​o​r​ ​b​l​a​r​g​l​e​
t​h​e​ ​r​e​v​e​r​s​e​d​ ​s​t​r​i​n​g​ ​i​s​ ​'​(​n​u​l​l​)​'​

which is not quite the desired result.

Start gdb and tell it to use memerror as the target program:

$​ ​g​d​b​ ​.​/​m​e​m​e​r​r​o​r​
G​N​U​ ​g​d​b​ ​6​.​3​.​5​0​-​2​0​0​5​0​8​1​5​ ​(​A​p​p​l​e​ ​v​e​r​s​i​o​n​ ​g​d​b​-​1​5​1​8​)​ ​(​T​h​u​ ​J​a​n​ ​2​7​ ​0​8​:​3​4​:​4​7​ ​U​T​C​ ​2​0​1​1​)​
C​o​p​y​r​i​g​h​t​ ​2​0​0​4​ ​F​r​e​e​ ​S​o​f​t​w​a​r​e​ ​F​o​u​n​d​a​t​i​o​n​,​ ​I​n​c​.​
[​.​.​.​]​
(​g​d​b​)​ ​

Here gdb gives you its prompt. Since this is a small program, you will single-step over some code. Set a breakpoint on the main() function. A breakpoint is a spot in your code where gdb will halt your program’s execution and give control to gdb so you can look around.

Use the break command to set a breakpoint at the beginning of a function. This breakpoint will be triggered before any code in the function gets executed:

(​g​d​b​)​ ​b​r​e​a​k​ ​m​a​i​n​
B​r​e​a​k​p​o​i​n​t​ ​1​ ​a​t​ ​0​x​2​b​9​8​:​ ​f​i​l​e​ ​m​e​m​e​r​r​o​r​.​m​,​ ​l​i​n​e​ ​6​8​.​

and run the program:

(​g​d​b​)​ ​r​u​n​
S​t​a​r​t​i​n​g​ ​p​r​o​g​r​a​m​:​ ​/​U​s​e​r​s​/​m​a​r​k​d​/​P​r​o​j​e​c​t​s​/​c​o​r​e​-​o​s​x​/​g​d​b​-​c​h​a​p​/​m​e​m​e​r​r​o​r​ ​
R​e​a​d​i​n​g​ ​s​y​m​b​o​l​s​ ​f​o​r​ ​s​h​a​r​e​d​ ​l​i​b​r​a​r​i​e​s​ ​.​ ​d​o​n​e​

B​r​e​a​k​p​o​i​n​t​ ​1​,​ ​m​a​i​n​ ​(​a​r​g​c​=​1​,​ ​a​r​g​v​=​0​x​b​f​f​f​f​2​5​4​)​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​7​3​
6​8​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​!​=​ ​2​)​ ​{​

You can see that the breakpoint on main() was triggered. Single-stepping – executing the program one line of code at a time – is performed by using the next command.

(​g​d​b​)​ ​n​e​x​t​
7​4​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​s​a​g​e​:​ ​%​s​ ​s​t​r​i​n​g​.​ ​ ​T​h​i​s​ ​r​e​v​e​r​s​e​s​ ​t​h​e​ ​s​t​r​i​n​g​ ​"​

Hmmm, that’s interesting. This is the usage line. You get in this case if argc is not two. What is argc’s value?

(​g​d​b​)​ ​p​r​i​n​t​ ​a​r​g​c​
$​1​ ​=​ ​1​

argc has a value of 1 because you did not specify any arguments to the program (oops). The “$1” printed in the above statement can be ignored for now; it’s just a convenience variable you can use to refer to the value later.

So, just single-step on out to finish the program:

(​g​d​b​)​ ​n​e​x​t​
u​s​a​g​e​:​ ​$​?​?​_​?​ ​s​t​r​i​n​g​.​ ​T​h​i​s​ ​r​e​v​e​r​s​e​s​ ​t​h​e​ ​s​t​r​i​n​g​ ​g​i​v​e​n​ ​o​n​ ​t​h​e​ ​c​o​m​m​a​n​d​ ​l​i​n​e​.​

7​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​1​;​

Wow. A lot of garbage there. You might also see it print out (null). Looks like you stumbled across the first bug (bug #1) unexpectedly:

 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​s​a​g​e​:​ ​%​s​ ​s​t​r​i​n​g​.​ ​ ​T​h​i​s​ ​r​e​v​e​r​s​e​s​ ​t​h​e​ ​s​t​r​i​n​g​ ​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​g​i​v​e​n​ ​o​n​ ​t​h​e​ ​c​o​m​m​a​n​d​ ​l​i​n​e​\​n​"​)​;​

Note that fprintf() has a %s format specifier in the string but no corresponding value to plug in there, so the function picked up some garbage from the stack. Looks like this fprintf() is expecting to use the name of the program as specified by the user in the message. That is an easy enough fix:

 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​s​a​g​e​:​ ​%​s​ ​s​t​r​i​n​g​.​ ​ ​T​h​i​s​ ​r​e​v​e​r​s​e​s​ ​t​h​e​ ​s​t​r​i​n​g​ ​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​g​i​v​e​n​ ​o​n​ ​t​h​e​ ​c​o​m​m​a​n​d​ ​l​i​n​e​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​

You could quit gdb and run your compilation command again, or you could tell gdb to run a shell command for you:

(​g​d​b​)​ ​s​h​e​l​l​ ​g​c​c​ ​-​g​ ​-​w​ ​-​o​ ​m​e​m​e​r​r​o​r​ ​m​e​m​e​r​r​o​r​.​m​

Now restart the program with a command-line argument:

(​g​d​b​)​ ​r​u​n​ ​b​l​a​r​g​l​e​
T​h​e​ ​p​r​o​g​r​a​m​ ​b​e​i​n​g​ ​d​e​b​u​g​g​e​d​ ​h​a​s​ ​b​e​e​n​ ​s​t​a​r​t​e​d​ ​a​l​r​e​a​d​y​.​
S​t​a​r​t​ ​i​t​ ​f​r​o​m​ ​t​h​e​ ​b​e​g​i​n​n​i​n​g​?​ ​(​y​ ​o​r​ ​n​)​ ​y​

and answer y and press return.

It will print out:

`​/​U​s​e​r​s​/​m​a​r​k​d​/​W​r​i​t​i​n​g​/​c​o​r​e​-​o​s​x​/​m​e​m​o​r​y​-​c​h​a​p​/​P​r​o​j​e​c​t​s​/​m​e​m​e​r​r​o​r​'​ ​h​a​s​ ​c​h​a​n​g​e​d​;​
 ​ ​ ​ ​r​e​-​r​e​a​d​i​n​g​ ​s​y​m​b​o​l​s​.​
S​t​a​r​t​i​n​g​ ​p​r​o​g​r​a​m​:​ ​/​U​s​e​r​s​/​m​a​r​k​d​/​W​r​i​t​i​n​g​/​c​o​r​e​-​o​s​x​/​m​e​m​o​r​y​-​c​h​a​p​/​m​e​m​e​r​r​o​r​ ​b​l​a​r​g​l​e​
R​e​-​e​n​a​b​l​i​n​g​ ​s​h​a​r​e​d​ ​l​i​b​r​a​r​y​ ​b​r​e​a​k​p​o​i​n​t​ ​1​
B​r​e​a​k​p​o​i​n​t​ ​1​ ​a​t​ ​0​x​1​0​0​0​0​0​d​3​8​:​ ​f​i​l​e​ ​m​e​m​e​r​r​o​r​.​m​,​ ​l​i​n​e​ ​6​8​.​

to let you know that it realizes the program is different and needs to be reloaded.

Since you did not quit gdb, the breakpoint on main() is still active.

B​r​e​a​k​p​o​i​n​t​ ​1​,​ ​m​a​i​n​ ​(​a​r​g​c​=​2​,​ ​a​r​g​v​=​0​x​b​f​f​f​f​2​4​0​)​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​6​8​
6​8​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​!​=​ ​2​)​ ​{​

Double-check argc for paranoia’s sake:

(​g​d​b​)​ ​p​r​i​n​t​ ​a​r​g​c​
$​1​ ​=​ ​2​

A value of 2. Good. And for fun look at the argument vector:

(​g​d​b​)​ ​p​r​i​n​t​ ​a​r​g​v​
$​2​ ​=​ ​(​c​h​a​r​ ​*​*​)​ ​0​x​7​f​f​f​5​f​b​f​f​2​4​0​

(​g​d​b​)​ ​p​r​i​n​t​ ​a​r​g​v​[​0​]​
$​3​ ​=​ ​0​x​7​f​f​f​5​f​b​f​f​3​d​8​ ​"​/​U​s​e​r​s​/​m​a​r​k​d​/​W​r​i​t​i​n​g​/​c​o​r​e​-​o​s​x​/​m​e​m​o​r​y​-​c​h​a​p​/​P​r​o​j​e​c​t​s​/​m​e​m​e​r​r​o​r​"​

(​g​d​b​)​ ​p​r​i​n​t​ ​a​r​g​v​[​1​]​
$​4​ ​=​ ​0​x​7​f​f​f​5​f​b​f​f​4​1​4​ ​"​b​l​a​r​g​l​e​"​

That looks good, too. So single-step

(​g​d​b​)​ ​n​e​x​t​
7​5​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​r​i​n​g​b​u​f​f​e​r​ ​=​ ​m​a​l​l​o​c​ ​(​s​t​r​l​e​n​(​a​r​g​v​[​1​]​)​)​;​

and see how big that is going to be. You can call your program’s functions from inside the debugger:

(​g​d​b​)​ ​c​a​l​l​ ​(​i​n​t​)​ ​s​t​r​l​e​n​(​a​r​g​v​[​1​]​)​
$​5​ ​=​ ​7​

So this will allocate 7 bytes of memory. Single-step over the allocation:

(​g​d​b​)​ ​n​

You can abbreviate commands so long as they do not become ambiguous. In this case, n is the same as next.

(​g​d​b​)​ ​n​
8​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​r​c​p​y​ ​(​a​r​g​v​[​1​]​,​ ​s​t​r​i​n​g​b​u​f​f​e​r​)​;​

Hmm... wait a minute. Strings in C are null-terminated, meaning that you need an extra byte at the end. The call to malloc() did not allocate enough memory, so this call to strcpy() (which you have not executed yet) will clobber an extra byte of memory. That is easy enough to fix in code. You would change

 ​ ​ ​ ​s​t​r​i​n​g​b​u​f​f​e​r​ ​=​ ​m​a​l​l​o​c​ ​(​s​t​r​l​e​n​(​a​r​g​v​[​1​]​)​)​;​

to be

 ​ ​ ​ ​s​t​r​i​n​g​b​u​f​f​e​r​ ​=​ ​m​a​l​l​o​c​ ​(​s​t​r​l​e​n​(​a​r​g​v​[​1​]​)​ ​+​ ​1​)​;​

Go ahead and change the code (bug #2). No need to recompile and rerun, you can patch this error for this session immediately.

(​g​d​b​)​ ​s​e​t​ ​v​a​r​ ​s​t​r​i​n​g​b​u​f​f​e​r​ ​=​ ​(​v​o​i​d​ ​*​)​m​a​l​l​o​c​ ​(​(​i​n​t​)​s​t​r​l​e​n​(​a​r​g​v​[​1​]​)​ ​+​ ​1​)​

You can see that there are explicit casts for return values from the strlen() and malloc() functions. These casts are necessary when you call a function that does not have debug info like these library functions.

OK, with that done, execute the next line of code (the strcpy()):

(​g​d​b​)​ ​n​
7​9​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​v​e​r​s​e​I​t​ ​(​s​t​r​i​n​g​b​u​f​f​e​r​)​;​

Look at stringbuffer to make sure it has a reasonable value:

(​g​d​b​)​ ​p​r​i​n​t​ ​s​t​r​i​n​g​b​u​f​f​e​r​
$​6​ ​=​ ​0​x​1​0​0​1​0​0​0​8​0​ ​"​"​

What? The line of code in question is:

 ​ ​ ​ ​s​t​r​c​p​y​ ​(​a​r​g​v​[​1​]​,​ ​s​t​r​i​n​g​b​u​f​f​e​r​)​;​

Checking the manpage, it looks like the arguments are reversed: strcpy() takes destination first, then the source (bug #3). This is also an easy code change to make:

 ​ ​ ​ ​s​t​r​c​p​y​ ​(​s​t​r​i​n​g​b​u​f​f​e​r​,​ ​a​r​g​v​[​1​]​)​;​

Unfortunately, you can’t fix this as easily as you did with the malloc() error, since the bad strcpy() clobbered argv[1]. You can verify that argv[1] got clobbered by moving up one stack frame (out of the reverseIt() function) and displaying the value of argv[1].

(​g​d​b​)​ ​u​p​
(​g​d​b​)​ ​p​r​i​n​t​ ​a​r​g​v​[​1​]​
$​9​ ​=​ ​0​x​7​f​f​f​5​f​b​f​f​4​1​4​ ​"​"​

So, fix the code, and rebuild:

(​g​d​b​)​ ​s​h​e​l​l​ ​g​c​c​ ​-​g​ ​-​w​ ​-​o​ ​m​e​m​e​r​r​o​r​ ​m​e​m​e​r​r​o​r​.​m​

You are reasonably sure now that the code up until the call to reverseIt() is pretty good. So add a new breakpoint on reverseIt():

(​g​d​b​)​ ​b​r​e​a​k​ ​r​e​v​e​r​s​e​I​t​
B​r​e​a​k​p​o​i​n​t​ ​2​ ​a​t​ ​0​x​1​0​0​0​0​0​c​4​4​:​ ​f​i​l​e​ ​m​e​m​e​r​r​o​r​.​m​,​ ​l​i​n​e​ ​2​5​.​

and rerun the program. You do not need to respecify the arguments given to the program; gdb will remember them.

(​g​d​b​)​ ​r​u​n​
T​h​e​ ​p​r​o​g​r​a​m​ ​b​e​i​n​g​ ​d​e​b​u​g​g​e​d​ ​h​a​s​ ​b​e​e​n​ ​s​t​a​r​t​e​d​ ​a​l​r​e​a​d​y​.​
S​t​a​r​t​ ​i​t​ ​f​r​o​m​ ​t​h​e​ ​b​e​g​i​n​n​i​n​g​?​ ​(​y​ ​o​r​ ​n​)​ ​y​
`​/​U​s​e​r​s​/​m​a​r​k​d​/​W​r​i​t​i​n​g​/​c​o​r​e​-​o​s​x​/​m​e​m​o​r​y​-​c​h​a​p​/​m​e​m​e​r​r​o​r​'​ ​h​a​s​ ​c​h​a​n​g​e​d​;​ ​r​e​-​r​e​a​d​i​n​g​ ​s​y​m​b​o​l​s​.​
S​t​a​r​t​i​n​g​ ​p​r​o​g​r​a​m​:​ ​/​U​s​e​r​s​/​m​a​r​k​d​/​W​r​i​t​i​n​g​/​c​o​r​e​-​o​s​x​/​m​e​m​o​r​y​-​c​h​a​p​/​P​r​o​j​e​c​t​s​/​m​e​m​e​r​r​o​r​ ​b​l​a​r​g​l​e​
R​e​-​e​n​a​b​l​i​n​g​ ​s​h​a​r​e​d​ ​l​i​b​r​a​r​y​ ​b​r​e​a​k​p​o​i​n​t​ ​1​
R​e​-​e​n​a​b​l​i​n​g​ ​s​h​a​r​e​d​ ​l​i​b​r​a​r​y​ ​b​r​e​a​k​p​o​i​n​t​ ​2​

B​r​e​a​k​p​o​i​n​t​ ​1​,​ ​m​a​i​n​ ​(​a​r​g​c​=​2​,​ ​a​r​g​v​=​0​x​7​f​f​f​5​f​b​f​f​2​4​0​)​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​6​8​
6​8​ ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​!​=​ ​2​)​ ​{​
(​g​d​b​)​ ​

Thus, you can see that your first breakpoint is still there. continue will resume execution until the program exits or a breakpoint is hit.

(​g​d​b​)​ ​c​o​n​t​i​n​u​e​
C​o​n​t​i​n​u​i​n​g​.​

B​r​e​a​k​p​o​i​n​t​ ​2​,​ ​r​e​v​e​r​s​e​I​t​ ​(​s​t​r​i​n​g​b​u​f​f​e​r​=​0​x​1​0​0​1​0​0​0​8​0​ ​"​b​l​a​r​g​l​e​"​)​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​2​5​
2​5​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​ ​=​ ​n​o​d​e​ ​=​ ​N​U​L​L​;​

You are in reverseIt(). You can ask gdb for a listing to remind yourself what code is involved:

(​g​d​b​)​ ​l​i​s​t​
2​0​ ​ ​ ​ ​v​o​i​d​ ​r​e​v​e​r​s​e​I​t​ ​(​c​h​a​r​ ​*​s​t​r​i​n​g​b​u​f​f​e​r​)​ ​{​
2​1​ ​ ​ ​ ​ ​ ​ ​ ​C​h​a​r​N​o​d​e​ ​*​h​e​a​d​,​ ​*​n​o​d​e​;​
2​2​ ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​*​s​c​a​n​,​ ​*​s​t​o​p​;​
2​3​ ​ ​ ​ ​ ​ ​ ​ ​
2​4​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​l​e​a​r​ ​o​u​t​ ​l​o​c​a​l​ ​v​a​r​s​
2​5​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​ ​=​ ​n​o​d​e​ ​=​ ​N​U​L​L​;​
2​6​
2​7​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​F​i​n​d​ ​t​h​e​ ​s​t​a​r​t​ ​a​n​d​ ​e​n​d​ ​o​f​ ​t​h​e​ ​s​t​r​i​n​g​ ​s​o​ ​w​e​ ​c​a​n​ ​w​a​l​k​ ​i​t​
2​8​ ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​ ​=​ ​s​t​r​i​n​g​b​u​f​f​e​r​;​
2​9​ ​ ​ ​ ​ ​ ​ ​ ​s​t​o​p​ ​=​ ​s​t​r​i​n​g​b​u​f​f​e​r​ ​+​ ​s​t​r​l​e​n​(​s​t​r​i​n​g​b​u​f​f​e​r​)​ ​+​ ​1​;​ ​ ​/​/​ ​t​r​a​i​l​i​n​g​ ​n​u​l​l​

So you are about ready to execute line 30.

So some more single-stepping

(​g​d​b​)​ ​n​
2​8​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​ ​=​ ​s​t​r​i​n​g​b​u​f​f​e​r​;​
(​g​d​b​)​ ​n​
2​9​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​o​p​ ​=​ ​s​t​r​i​n​g​b​u​f​f​e​r​ ​+​ ​s​t​r​l​e​n​(​s​t​r​i​n​g​b​u​f​f​e​r​)​ ​+​ ​1​;​
(​g​d​b​)​ ​n​
3​2​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​h​i​l​e​ ​(​s​c​a​n​ ​<​ ​s​t​o​p​)​ ​{​

and take a look at the pointer chase variables

(​g​d​b​)​ ​p​r​i​n​t​ ​s​c​a​n​
$​1​ ​=​ ​0​x​1​0​0​1​0​0​0​8​0​ ​"​b​l​a​r​g​l​e​"​

(​g​d​b​)​ ​p​r​i​n​t​ ​s​t​o​p​
$​2​ ​=​ ​0​x​1​0​0​1​0​0​0​8​8​ ​"​"​
(​g​d​b​)​ ​

That looks OK. The address that stop has, 0x100100088, is 8 bytes past 0x100100080, the contents of scan. “Blargle” is 7 characters, plus the null byte is 8. You can use gdb to verify that.

(​g​d​b​)​ ​p​r​i​n​t​ ​0​x​1​0​0​1​0​0​0​8​8​ ​-​ ​0​x​1​0​0​1​0​0​0​8​0​
$​3​ ​=​ ​8​

If you do not want to type out those addresses, you can use the dollar-variable labels:

(​g​d​b​)​ ​p​r​i​n​t​ ​$​2​ ​-​ ​$​1​
$​4​ ​=​ ​8​

That looks good. More single stepping:

(​g​d​b​)​ ​n​
3​3​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​h​e​a​d​ ​=​=​ ​N​U​L​L​)​ ​{​

(​g​d​b​)​ ​n​
3​4​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​C​h​a​r​N​o​d​e​*​)​)​;​

To sanity check the amount of memory being allocated:

(​g​d​b​)​ ​p​r​i​n​t​ ​s​i​z​e​o​f​(​C​h​a​r​N​o​d​e​*​)​
$​5​ ​=​ ​8​

8 bytes. Pull apart the types here:

(​g​d​b​)​ ​w​h​a​t​i​s​ ​h​e​a​d​
t​y​p​e​ ​=​ ​C​h​a​r​N​o​d​e​ ​*​

head is a pointer to a CharNode. What is a CharNode?

(​g​d​b​)​ ​p​t​y​p​e​ ​C​h​a​r​N​o​d​e​
t​y​p​e​ ​=​ ​s​t​r​u​c​t​ ​C​h​a​r​N​o​d​e​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​t​h​e​C​h​a​r​;​
 ​ ​ ​ ​C​h​a​r​N​o​d​e​ ​*​n​e​x​t​;​
}​

A CharNode is a char (one byte) plus a pointer (8 bytes for a 64-bit pointer). That sounds like it should be more than 8 bytes.

(​g​d​b​)​ ​p​r​i​n​t​ ​s​i​z​e​o​f​(​C​h​a​r​N​o​d​e​)​
$​6​ ​=​ ​1​6​

Sure enough, that is not allocating enough memory. Here is the line of code again:

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​C​h​a​r​N​o​d​e​*​)​)​;​

Looks like a common C beginner’s mistake, confusing a pointer to what it points to. The malloc() here is allocating enough memory for a pointer to a CharNode, not a full CharNode. To fix this, it should be

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​C​h​a​r​N​o​d​e​)​)​;​

(bug #4). Looking at the code, there is a nearly identical line of code in the else branch. That should be fixed too.

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​C​h​a​r​N​o​d​e​*​)​)​;​

becomes

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​C​h​a​r​N​o​d​e​)​)​;​

(bug #5).

You’ll need to recompile and restart things to fix this. Before doing that, clean up the breakpoints. You don’t need the one on main(), and you probably don’t need the one at the top of reverseIt(), since you are pretty sure the beginning of that function is good.

For fun, do the where command to see the call stack.

(​g​d​b​)​ ​w​h​e​r​e​
#​0​ ​ ​r​e​v​e​r​s​e​I​t​ ​(​s​t​r​i​n​g​b​u​f​f​e​r​=​0​x​1​0​0​1​0​0​0​8​0​ ​"​b​l​a​r​g​l​e​"​)​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​3​4​
#​1​ ​ ​0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​d​0​ ​i​n​ ​m​a​i​n​ ​(​a​r​g​c​=​2​,​ ​a​r​g​v​=​0​x​7​f​f​f​5​f​b​f​f​2​4​0​)​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​7​9​

so you are at line 34 of memerror.m in reverseIt() and at line 79 of memerror.m, inside of main(). Do a list to see exactly what gdb thinks is line 34.

(​g​d​b​)​ ​l​i​s​t​
2​9​ ​ ​ ​ ​ ​ ​ ​s​t​o​p​ ​=​ ​s​t​r​i​n​g​b​u​f​f​e​r​ ​+​ ​s​t​r​l​e​n​(​s​t​r​i​n​g​b​u​f​f​e​r​)​ ​+​ ​1​;​ ​ ​/​/​ ​t​r​a​i​l​i​n​g​ ​n​u​l​l​
3​0​
3​1​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​a​l​k​ ​t​h​e​ ​s​t​r​i​n​g​
3​2​ ​ ​ ​ ​ ​ ​ ​ ​w​h​i​l​e​ ​(​s​c​a​n​ ​<​ ​s​t​o​p​)​ ​{​
3​3​ ​ ​ ​ ​i​f​ ​(​h​e​a​d​ ​=​=​ ​N​U​L​L​)​ ​{​
3​4​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​C​h​a​r​N​o​d​e​)​)​;​
3​5​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​-​>​t​h​e​C​h​a​r​ ​=​ ​*​s​c​a​n​;​
3​6​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​-​>​n​e​x​t​ ​=​ ​N​U​L​L​;​
3​7​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
3​8​ ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​C​h​a​r​N​o​d​e​)​)​;​

A good place to break would be on line 32, right before entering the loop.

(​g​d​b​)​ ​b​r​e​a​k​ ​m​e​m​e​r​r​o​r​.​m​:​3​2​
B​r​e​a​k​p​o​i​n​t​ ​3​ ​a​t​ ​0​x​1​0​0​0​0​0​c​7​0​:​ ​f​i​l​e​ ​m​e​m​e​r​r​o​r​.​m​,​ ​l​i​n​e​ ​3​2​.​

To see all the current breakpoints, info breakpoints will show them and their ID number:

(​g​d​b​)​ ​i​n​f​o​ ​b​r​e​a​k​p​o​i​n​t​s​
N​u​m​ ​T​y​p​e​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​D​i​s​p​ ​E​n​b​ ​A​d​d​r​e​s​s​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​W​h​a​t​
1​ ​ ​ ​b​r​e​a​k​p​o​i​n​t​ ​ ​ ​ ​ ​k​e​e​p​ ​y​ ​ ​ ​0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​3​8​ ​i​n​ ​m​a​i​n​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​6​8​
b​r​e​a​k​p​o​i​n​t​ ​a​l​r​e​a​d​y​ ​h​i​t​ ​1​ ​t​i​m​e​
2​ ​ ​ ​b​r​e​a​k​p​o​i​n​t​ ​ ​ ​ ​ ​k​e​e​p​ ​y​ ​ ​ ​0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​c​4​4​ ​i​n​ ​r​e​v​e​r​s​e​I​t​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​2​5​
b​r​e​a​k​p​o​i​n​t​ ​a​l​r​e​a​d​y​ ​h​i​t​ ​1​ ​t​i​m​e​
3​ ​ ​ ​b​r​e​a​k​p​o​i​n​t​ ​ ​ ​ ​ ​k​e​e​p​ ​y​ ​ ​ ​0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​c​7​0​ ​i​n​ ​r​e​v​e​r​s​e​I​t​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​3​2​

Disable the first two:

(​g​d​b​)​ ​d​i​s​a​b​l​e​ ​1​
(​g​d​b​)​ ​d​i​s​a​b​l​e​ ​2​

and double-check that they are disabled:

(​g​d​b​)​ ​i​n​f​o​ ​b​r​e​a​k​
N​u​m​ ​T​y​p​e​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​D​i​s​p​ ​E​n​b​ ​A​d​d​r​e​s​s​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​W​h​a​t​
1​ ​ ​ ​b​r​e​a​k​p​o​i​n​t​ ​ ​ ​ ​ ​k​e​e​p​ ​n​ ​ ​ ​0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​3​8​ ​i​n​ ​m​a​i​n​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​6​8​
b​r​e​a​k​p​o​i​n​t​ ​a​l​r​e​a​d​y​ ​h​i​t​ ​1​ ​t​i​m​e​
2​ ​ ​ ​b​r​e​a​k​p​o​i​n​t​ ​ ​ ​ ​ ​k​e​e​p​ ​n​ ​ ​ ​0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​c​4​4​ ​i​n​ ​r​e​v​e​r​s​e​I​t​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​2​5​
b​r​e​a​k​p​o​i​n​t​ ​a​l​r​e​a​d​y​ ​h​i​t​ ​1​ ​t​i​m​e​
3​ ​ ​ ​b​r​e​a​k​p​o​i​n​t​ ​ ​ ​ ​ ​k​e​e​p​ ​y​ ​ ​ ​0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​c​7​0​ ​i​n​ ​r​e​v​e​r​s​e​I​t​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​3​2​

The “enabled” column now reads n for the first two breakpoints.

So, assuming you have fixed the above mallocs, rebuild the program

(​g​d​b​)​ ​s​h​e​l​l​ ​g​c​c​ ​-​g​ ​-​w​ ​-​o​ ​m​e​m​e​r​r​o​r​ ​m​e​m​e​r​r​o​r​.​m​

and run it

(​g​d​b​)​ ​r​u​n​
T​h​e​ ​p​r​o​g​r​a​m​ ​b​e​i​n​g​ ​d​e​b​u​g​g​e​d​ ​h​a​s​ ​b​e​e​n​ ​s​t​a​r​t​e​d​ ​a​l​r​e​a​d​y​.​
.​.​.​

B​r​e​a​k​p​o​i​n​t​ ​3​,​ ​r​e​v​e​r​s​e​I​t​ ​(​s​t​r​i​n​g​b​u​f​f​e​r​=​0​x​3​0​0​1​5​0​ ​"​b​l​a​r​g​l​e​"​)​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​3​6​
3​2​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​h​i​l​e​ ​(​s​c​a​n​ ​<​ ​s​t​o​p​)​ ​{​

And sure enough, you are at the beginning of the loop. Time to step again.

(​g​d​b​)​ ​n​
3​3​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​h​e​a​d​ ​=​=​ ​N​U​L​L​)​ ​{​

(​g​d​b​)​ ​p​r​i​n​t​ ​h​e​a​d​
$​1​ ​=​ ​(​C​h​a​r​N​o​d​e​ ​*​)​ ​0​x​0​

So you will go into the first branch of the if.

(​g​d​b​)​ ​n​
3​4​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​C​h​a​r​N​o​d​e​)​)​;​
(​g​d​b​)​ ​n​
3​5​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​-​>​t​h​e​C​h​a​r​ ​=​ ​*​s​c​a​n​;​
(​g​d​b​)​ ​n​
3​6​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​-​>​n​e​x​t​ ​=​ ​N​U​L​L​;​
(​g​d​b​)​ ​n​
4​3​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​+​+​;​

And for fun, print out head to make sure it is sane:

(​g​d​b​)​ ​p​r​i​n​t​ ​*​h​e​a​d​
$​2​ ​=​ ​{​
 ​ ​t​h​e​C​h​a​r​ ​=​ ​9​8​ ​'​b​'​,​ ​
 ​ ​n​e​x​t​ ​=​ ​0​x​0​
}​

Looks good. Now step back through the top of the loop:

(​g​d​b​)​ ​n​
3​2​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​h​i​l​e​ ​(​s​c​a​n​ ​<​ ​s​t​o​p​)​ ​{​

And you are back at the top. For fun, double-check the value of scan:

(​g​d​b​)​ ​p​r​i​n​t​ ​s​c​a​n​
$​3​ ​=​ ​0​x​1​0​0​1​0​0​0​8​1​ ​"​l​a​r​g​l​e​"​

This is good: you are one character into the string. Step a couple of more times.

(​g​d​b​)​ ​n​
3​3​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​h​e​a​d​ ​=​=​ ​N​U​L​L​)​ ​{​
(​g​d​b​)​ ​n​
3​8​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​ ​=​ ​m​a​l​l​o​c​ ​(​s​i​z​e​o​f​(​C​h​a​r​N​o​d​e​)​)​;​

Now you are into the else clause (notice the line number jump from 33 to 38).

(​g​d​b​)​ ​n​
3​9​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​-​>​t​h​e​C​h​a​r​ ​=​ ​*​s​c​a​n​;​
(​g​d​b​)​ ​n​
4​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​-​>​n​e​x​t​ ​=​ ​h​e​a​d​;​
(​g​d​b​)​ ​n​
4​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​ ​=​ ​n​o​d​e​;​
(​g​d​b​)​ ​n​
4​3​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​+​+​;​

And sanity check stuff:

(​g​d​b​)​ ​p​r​i​n​t​ ​*​n​o​d​e​
$​4​ ​=​ ​{​
 ​ ​t​h​e​C​h​a​r​ ​=​ ​1​0​8​ ​'​l​'​,​ ​
 ​ ​n​e​x​t​ ​=​ ​0​x​1​0​0​1​0​0​0​9​0​
}​
(​g​d​b​)​ ​p​r​i​n​t​ ​*​n​o​d​e​-​>​n​e​x​t​
$​5​ ​=​ ​{​
 ​ ​t​h​e​C​h​a​r​ ​=​ ​9​8​ ​'​b​'​,​ ​
 ​ ​n​e​x​t​ ​=​ ​0​x​0​
}​

So the linked list looks pretty good.

Step over the scan++:

(​g​d​b​)​ ​n​
3​2​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​h​i​l​e​ ​(​s​c​a​n​ ​<​ ​s​t​o​p​)​ ​{​

So you are reasonably sure the loop is good. The gdb command until will resume execution until the line of code after the current one. Even though gdb shows us poised at the beginning of the loop, it knows that we have just finished an iteration of the loop. So if you issue the until command now, execution will continue until the loop finishes (no need to single-step through everything).

So, disable the breakpoint at the top of the loop (breakpoint 3 above)

(​g​d​b​)​ ​d​i​s​ ​3​

and do until

(​g​d​b​)​ ​u​n​t​i​l​
4​7​ ​ ​ ​ ​s​c​a​n​ ​=​ ​s​t​r​i​n​g​b​u​f​f​e​r​;​

which just so happens to be after the loop.

Take a look at the linked list just to be sure:

(​g​d​b​)​ ​p​r​i​n​t​ ​*​h​e​a​d​
$​6​ ​=​ ​{​
 ​ ​t​h​e​C​h​a​r​ ​=​ ​0​ ​'​\​0​0​0​'​,​ ​
 ​ ​n​e​x​t​ ​=​ ​0​x​1​0​0​1​0​0​0​f​0​
}​

(​g​d​b​)​ ​p​r​i​n​t​ ​h​e​a​d​-​>​n​e​x​t​
$​7​ ​=​ ​(​s​t​r​u​c​t​ ​C​h​a​r​N​o​d​e​ ​*​)​ ​0​x​1​0​0​1​0​0​0​f​0​

(​g​d​b​)​ ​p​r​i​n​t​ ​*​h​e​a​d​-​>​n​e​x​t​
$​8​ ​=​ ​{​
 ​ ​t​h​e​C​h​a​r​ ​=​ ​1​0​1​ ​'​e​'​,​ ​
 ​ ​n​e​x​t​ ​=​ ​0​x​1​0​0​1​0​0​0​e​0​
}​

(​g​d​b​)​ ​p​r​i​n​t​ ​*​h​e​a​d​-​>​n​e​x​t​-​>​n​e​x​t​
$​9​ ​=​ ​{​
 ​ ​t​h​e​C​h​a​r​ ​=​ ​1​0​8​ ​'​l​'​,​ ​
 ​ ​n​e​x​t​ ​=​ ​0​x​1​0​0​1​0​0​0​d​0​
}​

(​g​d​b​)​ ​p​r​i​n​t​ ​*​h​e​a​d​-​>​n​e​x​t​-​>​n​e​x​t​-​>​n​e​x​t​
$​1​0​ ​=​ ​{​
 ​ ​t​h​e​C​h​a​r​ ​=​ ​1​0​3​ ​'​g​'​,​ ​
 ​ ​n​e​x​t​ ​=​ ​0​x​1​0​0​1​0​0​0​c​0​
}​

Looks like a reversed string. That is a good sign. That leading zero value at the head looks a bit odd, though. You might or might not want that in there. So, continuing on:

(​g​d​b​)​ ​n​
5​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​h​i​l​e​ ​(​h​e​a​d​ ​!​=​ ​N​U​L​L​)​ ​{​

(​g​d​b​)​ ​n​
5​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​*​s​c​a​n​ ​=​ ​h​e​a​d​-​>​t​h​e​C​h​a​r​;​

(​g​d​b​)​ ​n​
5​2​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​r​e​e​ ​(​h​e​a​d​)​;​

(​g​d​b​)​ ​n​
5​3​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​ ​=​ ​h​e​a​d​-​>​n​e​x​t​;​

Something does not look right there. Print out *head again:

(​g​d​b​)​ ​p​r​i​n​t​ ​*​h​e​a​d​
$​1​1​ ​=​ ​{​
 ​ ​t​h​e​C​h​a​r​ ​=​ ​0​ ​'​\​0​0​0​'​,​ ​
 ​ ​n​e​x​t​ ​=​ ​0​x​1​0​0​1​0​0​0​f​0​
}​

Still looks the same, but something smells wrong with the code. Oops. The head gets freed, and then the memory gets used after the free() (bug #6). That is pretty bad. So fix it. Change

 ​ ​ ​ ​ ​ ​ ​ ​f​r​e​e​ ​(​h​e​a​d​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​ ​=​ ​h​e​a​d​-​>​n​e​x​t​;​

to

 ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​ ​=​ ​h​e​a​d​-​>​n​e​x​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​r​e​e​ ​(​h​e​a​d​)​;​

Step a couple of times to go back to the top of the loop

(​g​d​b​)​ ​n​
5​4​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​ ​=​ ​n​o​d​e​;​
(​g​d​b​)​ ​n​
5​5​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​+​+​;​
(​g​d​b​)​ ​n​
5​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​h​i​l​e​ ​(​h​e​a​d​ ​!​=​ ​N​U​L​L​)​ ​{​

and set a breakpoint here

(​g​d​b​)​ ​b​r​e​a​k​
B​r​e​a​k​p​o​i​n​t​ ​4​ ​a​t​ ​0​x​1​0​0​0​0​0​d​1​7​:​ ​f​i​l​e​ ​m​e​m​e​r​r​o​r​.​m​,​ ​l​i​n​e​ ​5​0​.​

Just break by itself sets a breakpoint at the current position.

So, rebuild

(​g​d​b​)​ ​s​h​e​l​l​ ​g​c​c​ ​-​g​ ​-​w​ ​-​o​ ​m​e​m​e​r​r​o​r​ ​m​e​m​e​r​r​o​r​.​m​

and restart. (I know I get a little peeved at “The program being debugged has been started already. Start it from the beginning? (y or n)” messages, so I am going to turn them off and then restart):

(​g​d​b​)​ ​s​e​t​ ​c​o​n​f​i​r​m​ ​o​f​f​

(​g​d​b​)​ ​r​u​n​
`​/​U​s​e​r​s​/​m​a​r​k​d​/​P​r​o​j​e​c​t​s​/​c​o​r​e​-​o​s​x​/​g​d​b​-​c​h​a​p​/​m​e​m​e​r​r​o​r​'​ ​h​a​s​ ​c​h​a​n​g​e​d​;​ ​
r​e​-​r​e​a​d​i​n​g​ ​s​y​m​b​o​l​s​.​

B​r​e​a​k​p​o​i​n​t​ ​4​,​ ​r​e​v​e​r​s​e​I​t​ ​(​s​t​r​i​n​g​b​u​f​f​e​r​=​0​x​1​0​0​1​0​0​0​8​0​ ​"​b​l​a​r​g​l​e​"​)​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​5​0​
5​0​ ​ ​ ​ ​w​h​i​l​e​ ​(​h​e​a​d​ ​!​=​ ​N​U​L​L​)​ ​{​

and then single-step some more and verify that the code is doing what you want. Then finish the loop:

5​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​h​i​l​e​ ​(​h​e​a​d​ ​!​=​ ​N​U​L​L​)​ ​{​
(​g​d​b​)​ ​n​
5​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​*​s​c​a​n​ ​=​ ​h​e​a​d​-​>​t​h​e​C​h​a​r​;​
(​g​d​b​)​ ​n​
5​2​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​o​d​e​ ​=​ ​h​e​a​d​-​>​n​e​x​t​;​
(​g​d​b​)​ ​n​
5​3​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​r​e​e​ ​(​h​e​a​d​)​;​
(​g​d​b​)​ ​n​
5​4​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​ ​=​ ​n​o​d​e​;​
(​g​d​b​)​ ​n​
5​5​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​+​+​;​

(​g​d​b​)​ ​d​i​s​a​b​l​e​ ​4​
(​g​d​b​)​ ​u​n​t​i​l​
5​9​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​r​e​e​ ​(​h​e​a​d​)​;​

Now take a look at the buffer

(​g​d​b​)​ ​p​r​i​n​t​ ​s​t​r​i​n​g​b​u​f​f​e​r​
$​1​ ​=​ ​0​x​1​0​0​1​0​0​0​8​0​ ​"​"​

That does not look promising. Maybe the leading zero byte in the linked list is messing things up. Look at the memory one byte into the string:

(​g​d​b​)​ ​p​r​i​n​t​ ​(​c​h​a​r​ ​*​)​(​s​t​r​i​n​g​b​u​f​f​e​r​ ​+​ ​1​)​
$​2​ ​=​ ​0​x​1​0​0​1​0​0​0​8​1​ ​"​e​l​g​r​a​l​b​"​

Sure enough, that is “blargle” spelled backwards. So it looks like bug #7 is that extra zero byte. Where would that have come from? The code is walking the string from beginning to end and building a reversed linked list, so the last character of the string becomes the head of the linked list, and it is the head where that zero byte is. So it looks like the first loop is going one byte too far. Revisit this line of code:

 ​ ​ ​ ​s​t​o​p​ ​=​ ​s​t​r​i​n​g​b​u​f​f​e​r​ ​+​ ​s​t​r​l​e​n​(​s​t​r​i​n​g​b​u​f​f​e​r​)​ ​+​ ​1​;​

There it is right there! It explicitly includes the trailing zero byte, but you do not want it. Change this line of code to

 ​ ​ ​ ​s​t​o​p​ ​=​ ​s​t​r​i​n​g​b​u​f​f​e​r​ ​+​ ​s​t​r​l​e​n​(​s​t​r​i​n​g​b​u​f​f​e​r​)​;​

So, it looks like you found the problem! We must be done. Time to send the program over to QA and also put some T-shirts on order before the product launch. Fix the code and quit gdb:

(​g​d​b​)​ ​q​u​i​t​
$​

and rebuild the program:

$​ ​g​c​c​ ​-​g​ ​-​w​ ​-​o​ ​m​e​m​e​r​r​o​r​ ​m​e​m​e​r​r​o​r​.​m​

and run it:

$​ ​.​/​m​e​m​e​r​r​o​r​ ​b​l​a​r​g​l​e​
B​u​s​ ​e​r​r​o​r​

Ack! You crashed. You were, like most programmers, a little too optimistic. gdb is pretty handy for catching crashes like these. When there is a crash like this, there is usually a smoking gun pointing to the problem. So, gdb the program again:

$​ ​g​d​b​ ​.​/​m​e​m​e​r​r​o​r​
[​.​.​.​ ​c​o​p​y​r​i​g​h​t​ ​s​t​u​f​f​ ​.​.​.​]​

You have to respecify the command-line arguments since you exited gdb earlier.

(​g​d​b​)​ ​r​u​n​ ​b​l​a​r​g​l​e​
.​.​.​
P​r​o​g​r​a​m​ ​r​e​c​e​i​v​e​d​ ​s​i​g​n​a​l​ ​E​X​C​_​B​A​D​_​A​C​C​E​S​S​,​ ​C​o​u​l​d​ ​n​o​t​ ​a​c​c​e​s​s​ ​m​e​m​o​r​y​.​
R​e​a​s​o​n​:​ ​K​E​R​N​_​I​N​V​A​L​I​D​_​A​D​D​R​E​S​S​ ​a​t​ ​a​d​d​r​e​s​s​:​ ​0​x​0​0​0​0​0​0​0​0​0​0​0​0​0​0​6​0​
0​x​0​0​0​0​7​f​f​f​8​2​f​7​a​1​2​0​ ​i​n​ ​s​t​r​l​e​n​ ​(​)​

and look at the stack:

(​g​d​b​)​ ​w​h​e​r​e​
#​0​ ​ ​0​x​0​0​0​0​7​f​f​f​8​2​f​7​a​1​2​0​ ​i​n​ ​s​t​r​l​e​n​ ​(​)​
#​1​ ​ ​0​x​0​0​0​0​7​f​f​f​8​2​f​8​5​b​1​c​ ​i​n​ ​_​_​v​f​p​r​i​n​t​f​ ​(​)​
#​2​ ​ ​0​x​0​0​0​0​7​f​f​f​8​2​f​c​6​d​c​b​ ​i​n​ ​v​f​p​r​i​n​t​f​_​l​ ​(​)​
#​3​ ​ ​0​x​0​0​0​0​7​f​f​f​8​2​f​f​4​4​8​3​ ​i​n​ ​p​r​i​n​t​f​ ​(​)​
#​4​ ​ ​0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​e​c​ ​i​n​ ​m​a​i​n​ ​(​a​r​g​c​=​2​,​ ​a​r​g​v​=​0​x​7​f​f​f​5​f​b​f​f​2​4​0​)​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​8​2​

Looks like something bad is happening at line 82 in memerror.m, stack frame number 4. Go to that frame:

(​g​d​b​)​ ​f​r​a​m​e​ ​4​
#​4​ ​ ​0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​e​c​ ​i​n​ ​m​a​i​n​ ​(​a​r​g​c​=​2​,​ ​a​r​g​v​=​0​x​7​f​f​f​5​f​b​f​f​2​4​0​)​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​8​2​
8​2​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​t​h​e​ ​r​e​v​e​r​s​e​d​ ​s​t​r​i​n​g​ ​i​s​ ​'​%​s​'​\​n​"​,​ ​*​s​t​r​i​n​g​b​u​f​f​e​r​)​;​

What is stringbuffer?

(​g​d​b​)​ ​p​r​i​n​t​ ​s​t​r​i​n​g​b​u​f​f​e​r​
$​1​ ​=​ ​0​x​1​0​0​1​0​0​0​8​0​ ​"​e​l​g​r​a​l​b​"​

That looks OK. Of course, looking closer at the code, why did the programmer dereference the stringbuffer pointer?

(​g​d​b​)​ ​p​r​i​n​t​ ​*​s​t​r​i​n​g​b​u​f​f​e​r​
$​2​ ​=​ ​1​0​1​ ​'​e​'​

So printf() is trying to interpret the number 101 as an address of a string. That is not a valid address, so eventually some function deep in the standard library will use that bad address and choke. Generally, if you see standard library functions on the stack, there is not anything really wrong with them. The code calling them has messed something up.

This is an easy enough fix. Change

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​t​h​e​ ​r​e​v​e​r​s​e​d​ ​s​t​r​i​n​g​ ​i​s​ ​'​%​s​'​\​n​"​,​ ​*​s​t​r​i​n​g​b​u​f​f​e​r​)​;​

to

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​t​h​e​ ​r​e​v​e​r​s​e​d​ ​s​t​r​i​n​g​ ​i​s​ ​'​%​s​'​\​n​"​,​ ​s​t​r​i​n​g​b​u​f​f​e​r​)​;​

Get out of gdb:

(​g​d​b​)​ ​q​u​i​t​
T​h​e​ ​p​r​o​g​r​a​m​ ​i​s​ ​r​u​n​n​i​n​g​.​ ​ ​E​x​i​t​ ​a​n​y​w​a​y​?​ ​(​y​ ​o​r​ ​n​)​ ​y​

Fix the code and rebuild:

$​ ​g​c​c​ ​-​g​ ​-​o​ ​m​e​m​e​r​r​o​r​ ​m​e​m​e​r​r​o​r​.​m​

and run it:

$​ ​.​/​m​e​m​e​r​r​o​r​ ​b​l​a​r​g​l​e​
t​h​e​ ​r​e​v​e​r​s​e​d​ ​s​t​r​i​n​g​ ​i​s​ ​'​e​l​g​r​a​l​b​'​

Hooray! It works! You’ve found eight errors. There are actually nine. The last one does not affect the program’s output, but it is a little bit of sloppiness: The string buffer gets memory from malloc(), but that memory is never explicitly freed.

You may, or may not, have noticed the -w flag that was used for the compile lines. This suppresses compiler warnings; otherwise two of the bugs would have been too easy to find:

$​ ​g​c​c​ ​-​g​ ​-​o​ ​m​e​m​e​r​r​o​r​ ​m​e​m​e​r​r​o​r​.​m​
m​e​m​e​r​r​o​r​.​m​:​ ​I​n​ ​f​u​n​c​t​i​o​n​ ​‘​m​a​i​n​’​:​
m​e​m​e​r​r​o​r​.​m​:​7​0​:​ ​w​a​r​n​i​n​g​:​ ​t​o​o​ ​f​e​w​ ​a​r​g​u​m​e​n​t​s​ ​f​o​r​ ​f​o​r​m​a​t​
m​e​m​e​r​r​o​r​.​m​:​8​2​:​ ​w​a​r​n​i​n​g​:​ ​f​o​r​m​a​t​ ​‘​%​s​’​ ​e​x​p​e​c​t​s​ ​t​y​p​e​ ​‘​c​h​a​r​ ​*​’​,​ ​
 ​ ​ ​ ​b​u​t​ ​a​r​g​u​m​e​n​t​ ​2​ ​h​a​s​ ​t​y​p​e​ ​‘​i​n​t​’​

GDB Specifics

The above walkthrough hits on the major things you can do with gdb in command-line mode:

See program listings

See the stack trace and move around in the stack looking at the variables in various functions

Set and disable breakpoints

Display data

Change data

Change execution flow

What follows is a survey of different commands that could be useful. This is still a very small subset of what gdb is capable of.

Help

gdb has extensive online help. Just doing help shows you the top-level classes of help available:

(​g​d​b​)​ ​h​e​l​p​
L​i​s​t​ ​o​f​ ​c​l​a​s​s​e​s​ ​o​f​ ​c​o​m​m​a​n​d​s​:​

a​l​i​a​s​e​s​ ​-​-​ ​A​l​i​a​s​e​s​ ​o​f​ ​o​t​h​e​r​ ​c​o​m​m​a​n​d​s​
b​r​e​a​k​p​o​i​n​t​s​ ​-​-​ ​M​a​k​i​n​g​ ​p​r​o​g​r​a​m​ ​s​t​o​p​ ​a​t​ ​c​e​r​t​a​i​n​ ​p​o​i​n​t​s​
d​a​t​a​ ​-​-​ ​E​x​a​m​i​n​i​n​g​ ​d​a​t​a​
f​i​l​e​s​ ​-​-​ ​S​p​e​c​i​f​y​i​n​g​ ​a​n​d​ ​e​x​a​m​i​n​i​n​g​ ​f​i​l​e​s​
i​n​t​e​r​n​a​l​s​ ​-​-​ ​M​a​i​n​t​e​n​a​n​c​e​ ​c​o​m​m​a​n​d​s​
o​b​s​c​u​r​e​ ​-​-​ ​O​b​s​c​u​r​e​ ​f​e​a​t​u​r​e​s​
r​u​n​n​i​n​g​ ​-​-​ ​R​u​n​n​i​n​g​ ​t​h​e​ ​p​r​o​g​r​a​m​
s​t​a​c​k​ ​-​-​ ​E​x​a​m​i​n​i​n​g​ ​t​h​e​ ​s​t​a​c​k​
s​t​a​t​u​s​ ​-​-​ ​S​t​a​t​u​s​ ​i​n​q​u​i​r​i​e​s​
s​u​p​p​o​r​t​ ​-​-​ ​S​u​p​p​o​r​t​ ​f​a​c​i​l​i​t​i​e​s​
t​r​a​c​e​p​o​i​n​t​s​ ​-​-​ ​T​r​a​c​i​n​g​ ​o​f​ ​p​r​o​g​r​a​m​ ​e​x​e​c​u​t​i​o​n​ ​w​i​t​h​o​u​t​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​o​p​p​i​n​g​ ​t​h​e​ ​p​r​o​g​r​a​m​
u​s​e​r​-​d​e​f​i​n​e​d​ ​-​-​ ​U​s​e​r​-​d​e​f​i​n​e​d​ ​c​o​m​m​a​n​d​s​

You can look at a particular class of stuff:

(​g​d​b​)​ ​h​e​l​p​ ​b​r​e​a​k​p​o​i​n​t​s​

M​a​k​i​n​g​ ​p​r​o​g​r​a​m​ ​s​t​o​p​ ​a​t​ ​c​e​r​t​a​i​n​ ​p​o​i​n​t​s​.​

L​i​s​t​ ​o​f​ ​c​o​m​m​a​n​d​s​:​

a​w​a​t​c​h​ ​-​-​ ​S​e​t​ ​a​ ​w​a​t​c​h​p​o​i​n​t​ ​f​o​r​ ​a​n​ ​e​x​p​r​e​s​s​i​o​n​
b​r​e​a​k​ ​-​-​ ​S​e​t​ ​b​r​e​a​k​p​o​i​n​t​ ​a​t​ ​s​p​e​c​i​f​i​e​d​ ​l​i​n​e​ ​o​r​ ​f​u​n​c​t​i​o​n​
c​a​t​c​h​ ​-​-​ ​S​e​t​ ​c​a​t​c​h​p​o​i​n​t​s​ ​t​o​ ​c​a​t​c​h​ ​e​v​e​n​t​s​
c​l​e​a​r​ ​-​-​ ​C​l​e​a​r​ ​b​r​e​a​k​p​o​i​n​t​ ​a​t​ ​s​p​e​c​i​f​i​e​d​ ​l​i​n​e​ ​o​r​ ​f​u​n​c​t​i​o​n​
[​.​.​.​]​
t​b​r​e​a​k​ ​-​-​ ​S​e​t​ ​a​ ​t​e​m​p​o​r​a​r​y​ ​b​r​e​a​k​p​o​i​n​t​
t​c​a​t​c​h​ ​-​-​ ​S​e​t​ ​t​e​m​p​o​r​a​r​y​ ​c​a​t​c​h​p​o​i​n​t​s​ ​t​o​ ​c​a​t​c​h​ ​e​v​e​n​t​s​
t​h​b​r​e​a​k​ ​-​-​ ​S​e​t​ ​a​ ​t​e​m​p​o​r​a​r​y​ ​h​a​r​d​w​a​r​e​ ​a​s​s​i​s​t​e​d​ ​b​r​e​a​k​p​o​i​n​t​
w​a​t​c​h​ ​-​-​ ​S​e​t​ ​a​ ​w​a​t​c​h​p​o​i​n​t​ ​f​o​r​ ​a​n​ ​e​x​p​r​e​s​s​i​o​n​

as well as help on a particular command:

(​g​d​b​)​ ​h​e​l​p​ ​u​n​t​i​l​
E​x​e​c​u​t​e​ ​u​n​t​i​l​ ​t​h​e​ ​p​r​o​g​r​a​m​ ​r​e​a​c​h​e​s​ ​a​ ​s​o​u​r​c​e​ ​l​i​n​e​ ​g​r​e​a​t​e​r​ ​t​h​a​n​ ​t​h​e​ ​c​u​r​r​e​n​t​
o​r​ ​a​ ​s​p​e​c​i​f​i​e​d​ ​l​o​c​a​t​i​o​n​ ​(​s​a​m​e​ ​a​r​g​s​ ​a​s​ ​b​r​e​a​k​ ​c​o​m​m​a​n​d​)​ ​w​i​t​h​i​n​ ​t​h​e​ ​c​u​r​r​e​n​t​ ​f​r​a​m​e​.​

The apropos command lets you search through the help if you do not know the exact name or class of a command.

(​g​d​b​)​ ​a​p​r​o​p​o​s​ ​t​h​r​e​a​d​
c​a​t​c​h​ ​-​-​ ​S​e​t​ ​c​a​t​c​h​p​o​i​n​t​s​ ​t​o​ ​c​a​t​c​h​ ​e​v​e​n​t​s​
i​n​f​o​ ​m​a​c​h​-​t​h​r​e​a​d​ ​-​-​ ​G​e​t​ ​i​n​f​o​ ​o​n​ ​a​ ​s​p​e​c​i​f​i​c​ ​t​h​r​e​a​d​
i​n​f​o​ ​m​a​c​h​-​t​h​r​e​a​d​s​ ​-​-​ ​G​e​t​ ​l​i​s​t​ ​o​f​ ​t​h​r​e​a​d​s​ ​i​n​ ​a​ ​t​a​s​k​
i​n​f​o​ ​t​h​r​e​a​d​ ​-​-​ ​G​e​t​ ​i​n​f​o​r​m​a​t​i​o​n​ ​o​n​ ​t​h​r​e​a​d​
i​n​f​o​ ​t​h​r​e​a​d​s​ ​-​-​ ​I​D​s​ ​o​f​ ​c​u​r​r​e​n​t​l​y​ ​k​n​o​w​n​ ​t​h​r​e​a​d​s​
[​.​.​.​]​
t​h​r​e​a​d​ ​-​-​ ​U​s​e​ ​t​h​i​s​ ​c​o​m​m​a​n​d​ ​t​o​ ​s​w​i​t​c​h​ ​b​e​t​w​e​e​n​ ​t​h​r​e​a​d​s​
t​h​r​e​a​d​ ​a​p​p​l​y​ ​-​-​ ​A​p​p​l​y​ ​a​ ​c​o​m​m​a​n​d​ ​t​o​ ​a​ ​l​i​s​t​ ​o​f​ ​t​h​r​e​a​d​s​
a​p​p​l​y​ ​a​l​l​ ​-​-​ ​A​p​p​l​y​ ​a​ ​c​o​m​m​a​n​d​ ​t​o​ ​a​l​l​ ​t​h​r​e​a​d​s​
t​h​r​e​a​d​ ​r​e​s​u​m​e​ ​-​-​ ​D​e​c​r​e​m​e​n​t​ ​t​h​e​ ​s​u​s​p​e​n​d​ ​c​o​u​n​t​ ​o​f​ ​a​ ​t​h​r​e​a​d​
t​h​r​e​a​d​ ​s​u​s​p​e​n​d​ ​-​-​ ​I​n​c​r​e​m​e​n​t​ ​t​h​e​ ​s​u​s​p​e​n​d​ ​c​o​u​n​t​ ​o​f​ ​a​ ​t​h​r​e​a​d​

Stack Traces

You can see a stack trace, that is, all of the currently active functions, with the where command (also backtrace and bt). It will show you the stack frames currently active:

(​g​d​b​)​ ​w​h​e​r​e​
#​0​ ​ ​0​x​0​0​0​0​7​f​f​f​8​2​f​7​a​1​2​0​ ​i​n​ ​s​t​r​l​e​n​ ​(​)​
#​1​ ​ ​0​x​0​0​0​0​7​f​f​f​8​2​f​8​5​b​1​c​ ​i​n​ ​_​_​v​f​p​r​i​n​t​f​ ​(​)​
#​2​ ​ ​0​x​0​0​0​0​7​f​f​f​8​2​f​c​6​d​c​b​ ​i​n​ ​v​f​p​r​i​n​t​f​_​l​ ​(​)​
#​3​ ​ ​0​x​0​0​0​0​7​f​f​f​8​2​f​f​4​4​8​3​ ​i​n​ ​p​r​i​n​t​f​ ​(​)​
#​4​ ​ ​0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​e​c​ ​i​n​ ​m​a​i​n​ ​(​a​r​g​c​=​2​,​ ​a​r​g​v​=​0​x​7​f​f​f​5​f​b​f​f​2​4​0​)​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​8​2​

The bottom-most frame, main() in this case, is termed the “innermost” stack frame. The top of the stack is the “outermost” frame (good to know for some gdb documentation). You can move up and down the stack using up and down, “up” being towards the innermost frame and “down” being towards the outermost. Unfortunately, that is backwards from the way the stack is listed in the backtrace. Specifically, if you were at frame #3 (main()), doing down would put you into frame #2, and doing up would put you into frame #4. You can go directly to a frame with the frame command.

You can look at the local variables or arguments with a single operation:

info args | Show all the arguments to the function.
info locals | Show the local variables and their values.
info catch | Show any active C++ exception handlers.

Program Listings

You can see around where you are by using the list command. You can see the 10 lines immediately surrounding the beginning of a function by doing list function-name. To change the number of lines listed, use set listsize:

(​g​d​b​)​ ​s​e​t​ ​l​i​s​t​s​i​z​e​ ​1​7​
(​g​d​b​)​ ​l​i​s​t​ ​r​e​v​e​r​s​e​I​t​
1​2​ ​ ​ ​ ​#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​
1​3​
1​4​ ​ ​ ​ ​t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​C​h​a​r​N​o​d​e​ ​{​
1​5​ ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​t​h​e​C​h​a​r​;​
1​6​ ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​C​h​a​r​N​o​d​e​ ​*​n​e​x​t​;​
1​7​ ​ ​ ​ ​}​ ​C​h​a​r​N​o​d​e​;​
1​8​
1​9​ ​ ​ ​ ​/​/​ ​B​u​i​l​d​ ​a​ ​l​i​n​k​e​d​ ​l​i​s​t​ ​b​a​c​k​w​a​r​d​s​,​ ​t​h​e​n​ ​w​a​l​k​ ​t​h​e​ ​l​i​s​t​.​
2​0​ ​ ​ ​ ​v​o​i​d​ ​r​e​v​e​r​s​e​I​t​ ​(​c​h​a​r​ ​*​s​t​r​i​n​g​b​u​f​f​e​r​)​ ​{​
2​1​ ​ ​ ​ ​ ​ ​ ​ ​C​h​a​r​N​o​d​e​ ​*​h​e​a​d​,​ ​*​n​o​d​e​;​
2​2​ ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​*​s​c​a​n​,​ ​*​s​t​o​p​;​
2​3​ ​ ​ ​ ​ ​ ​ ​ ​
2​4​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​l​e​a​r​ ​o​u​t​ ​l​o​c​a​l​ ​v​a​r​s​
2​5​ ​ ​ ​ ​ ​ ​ ​ ​h​e​a​d​ ​=​ ​n​o​d​e​ ​=​ ​N​U​L​L​;​
2​6​
2​7​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​F​i​n​d​ ​t​h​e​ ​s​t​a​r​t​ ​a​n​d​ ​e​n​d​ ​o​f​ ​t​h​e​ ​s​t​r​i​n​g​ ​s​o​ ​w​e​ ​c​a​n​ ​w​a​l​k​ ​i​t​
2​8​ ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​ ​=​ ​s​t​r​i​n​g​b​u​f​f​e​r​;​

gdb lists the lines around the start of the given function to give you some context.

If you are very curious, you can see a disassembly of your code:

(​g​d​b​)​ ​b​r​e​a​k​ ​m​a​i​n​
N​o​t​e​:​ ​b​r​e​a​k​p​o​i​n​t​ ​1​ ​a​l​s​o​ ​s​e​t​ ​a​t​ ​p​c​ ​0​x​1​0​0​0​0​0​d​4​0​.​
B​r​e​a​k​p​o​i​n​t​ ​2​ ​a​t​ ​0​x​1​0​0​0​0​0​d​4​0​:​ ​f​i​l​e​ ​m​e​m​e​r​r​o​r​.​m​,​ ​l​i​n​e​ ​6​8​.​
(​g​d​b​)​ ​r​u​n​
S​t​a​r​t​i​n​g​ ​p​r​o​g​r​a​m​:​/​U​s​e​r​s​/​m​a​r​k​d​/​B​N​R​u​n​i​x​/​g​d​b​-​c​h​a​p​t​e​r​/​.​/​m​e​m​e​r​r​o​r​ ​b​l​a​r​g​l​e​
[​S​w​i​t​c​h​i​n​g​ ​t​o​ ​t​h​r​e​a​d​ ​1​ ​(​p​r​o​c​e​s​s​ ​9​1​6​ ​t​h​r​e​a​d​ ​0​x​2​1​0​7​)​]​

B​r​e​a​k​p​o​i​n​t​ ​1​,​ ​m​a​i​n​ ​(​a​r​g​c​=​2​,​ ​a​r​g​v​=​0​x​7​f​f​f​5​f​b​f​f​2​4​0​)​ ​a​t​ ​m​e​m​e​r​r​o​r​.​m​:​6​8​
6​8​ ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​!​=​ ​2​)​ ​{​
(​g​d​b​)​ ​d​i​s​a​s​s​e​m​b​l​e​
D​u​m​p​ ​o​f​ ​a​s​s​e​m​b​l​e​r​ ​c​o​d​e​ ​f​o​r​ ​f​u​n​c​t​i​o​n​ ​m​a​i​n​:​
0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​3​1​ ​<​m​a​i​n​+​0​>​:​p​u​s​h​ ​ ​ ​%​r​b​p​
0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​3​2​ ​<​m​a​i​n​+​1​>​:​m​o​v​ ​ ​ ​ ​%​r​s​p​,​%​r​b​p​
0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​3​5​ ​<​m​a​i​n​+​4​>​:​s​u​b​ ​ ​ ​ ​$​0​x​3​0​,​%​r​s​p​
0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​3​9​ ​<​m​a​i​n​+​8​>​:​m​o​v​ ​ ​ ​ ​%​e​d​i​,​-​0​x​1​4​(​%​r​b​p​)​
0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​3​c​ ​<​m​a​i​n​+​1​1​>​:​m​o​v​ ​ ​ ​ ​%​r​s​i​,​-​0​x​2​0​(​%​r​b​p​)​
0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​4​0​ ​<​m​a​i​n​+​1​5​>​:​c​m​p​l​ ​ ​ ​$​0​x​2​,​-​0​x​1​4​(​%​r​b​p​)​
0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​4​4​ ​<​m​a​i​n​+​1​9​>​:​j​e​ ​ ​ ​ ​ ​0​x​1​0​0​0​0​0​d​6​d​ ​<​m​a​i​n​+​6​0​>​
0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​4​6​ ​<​m​a​i​n​+​2​1​>​:​m​o​v​ ​ ​ ​ ​0​x​2​e​b​(​%​r​i​p​)​,​%​r​a​x​ ​ ​ ​ ​ ​ ​ ​ ​#​ ​0​x​1​0​0​0​0​1​0​3​8​
.​.​.​
0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​d​a​ ​<​m​a​i​n​+​1​6​9​>​:​l​e​a​ ​ ​ ​ ​0​x​1​0​6​(​%​r​i​p​)​,​%​r​d​i​ ​ ​ ​ ​ ​ ​ ​ ​#​ ​0​x​1​0​0​0​0​0​e​e​7​
0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​e​1​ ​<​m​a​i​n​+​1​7​6​>​:​m​o​v​ ​ ​ ​ ​$​0​x​0​,​%​e​a​x​
0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​e​6​ ​<​m​a​i​n​+​1​8​1​>​:​c​a​l​l​q​ ​ ​0​x​1​0​0​0​0​0​e​3​c​ ​<​d​y​l​d​_​s​t​u​b​_​p​r​i​n​t​f​>​
0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​e​b​ ​<​m​a​i​n​+​1​8​6​>​:​m​o​v​l​ ​ ​ ​$​0​x​0​,​-​0​x​2​4​(​%​r​b​p​)​
0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​f​2​ ​<​m​a​i​n​+​1​9​3​>​:​m​o​v​ ​ ​ ​ ​-​0​x​2​4​(​%​r​b​p​)​,​%​e​a​x​
0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​f​5​ ​<​m​a​i​n​+​1​9​6​>​:​l​e​a​v​e​q​ ​
0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​d​f​6​ ​<​m​a​i​n​+​1​9​7​>​:​r​e​t​q​ ​ ​ ​
E​n​d​ ​o​f​ ​a​s​s​e​m​b​l​e​r​ ​d​u​m​p​.​

Breakpoints

Use break to set a breakpoint. You can break on a function name, or you can give a filename:line specification to stop in a specific place.

Breakpoints can have conditions attached to them:

(​g​d​b​)​ ​b​r​e​a​k​ ​m​e​m​e​r​r​o​r​.​m​:​7​4​ ​i​f​ ​a​r​g​c​ ​!​=​ ​2​
B​r​e​a​k​p​o​i​n​t​ ​1​ ​a​t​ ​0​x​1​d​1​c​:​ ​f​i​l​e​ ​m​e​m​e​r​r​o​r​.​m​,​ ​l​i​n​e​ ​7​4​.​

This breakpoint will only be triggered if argc is not 2. You can also attach conditions after the breakpoint has been created using the cond command. You specify the condition using the syntax of whatever language you are debugging. If you are debugging an Ada or a FORTRAN program, you would use the logical syntax of those languages.

(​g​d​b​)​ ​c​o​n​d​ ​2​ ​(​a​r​g​c​ ​!​=​ ​2​)​

rbreak lets you use a regular expression to stop on a bunch of functions. Very handy for overloaded functions in C++.

(​g​d​b​)​ ​r​b​r​e​a​k​ ​.​*​p​r​i​n​t​f​.​*​
(​s​e​t​s​ ​a​b​o​u​t​ ​3​5​ ​b​r​e​a​k​p​o​i​n​t​s​ ​f​o​r​ ​m​e​)​

You can also put a breakpoint on a specific template instantiation:

(​g​d​b​)​ ​b​r​e​a​k​ ​S​t​i​t​c​h​F​i​e​n​d​<​i​n​t​>​:​:​b​l​a​r​g​l​e​

info breakpoints will show you all the currently active breakpoints.

You saw the next command previously. That steps one line of code at a time, but it does not step into function calls. You can use step to go into function calls.

Breakpoints can be disabled (so they don’t fire) or enabled (to wake up a disabled breakpoint). You can set an ignore count on a breakpoint which gets decremented every time the breakpoint is hit by using ignore breakpoint# count. When the ignore count reaches zero, the breakpoint will trigger. You would use this when you know that the first 700 pieces of data process OK but item 701 fails.

You can also attach commands to breakpoints. When you put a command on a breakpoint, that command will be run by gdb. Any gdb command can be used on a breakpoint, even the continue command, which will resume execution, and commands to enable or disable other breakpoints. To attach commands to a breakpoint, use the commands command:

(​g​d​b​)​ ​b​r​e​a​k​ ​w​a​l​k​T​r​e​e​P​o​s​t​o​r​d​e​r​
B​r​e​a​k​p​o​i​n​t​ ​2​ ​a​t​ ​0​x​1​d​8​0​:​ ​f​i​l​e​ ​t​r​e​e​f​u​n​c​.​c​,​ ​l​i​n​e​ ​6​

(​g​d​b​)​ ​c​o​m​m​a​n​d​s​ ​2​
T​y​p​e​ ​c​o​m​m​a​n​d​s​ ​f​o​r​ ​w​h​e​n​ ​b​r​e​a​k​p​o​i​n​t​ ​2​ ​i​s​ ​h​i​t​,​ ​o​n​e​ ​p​e​r​ ​l​i​n​e​.​
E​n​d​ ​w​i​t​h​ ​a​ ​l​i​n​e​ ​s​a​y​i​n​g​ ​j​u​s​t​ ​"​e​n​d​"​.​
>​w​h​e​r​e​
>​p​r​i​n​t​ ​n​o​d​e​
>​c​o​n​t​i​n​u​e​
>​e​n​d​

These commands will print out the value of the node parameter every time the walkTreePostorder function is called.

Displaying Data

print can be used to display variables and the result of function calls. You can control the format of the displayed data by adding a format flag after the command:

(​g​d​b​)​ ​p​r​i​n​t​ ​i​
$​1​ ​=​ ​1​7​2​6​3​8​1​2​

(​g​d​b​)​ ​p​r​i​n​t​/​x​ ​i​
$​2​ ​=​ ​0​x​1​0​7​6​c​c​4​

(​g​d​b​)​ ​p​r​i​n​t​ ​/​o​ ​i​
$​3​ ​=​ ​0​1​0​1​6​6​6​3​0​4​

(​g​d​b​)​ ​p​r​i​n​t​/​t​ ​i​
$​4​ ​=​ ​1​0​0​0​0​0​1​1​1​0​1​1​0​1​1​0​0​1​1​0​0​0​1​0​0​

Here are some of the format flags:

/x | Hexadecimal
/d | Signed decimal
/u | Unsigned decimal
/o | Octal
/t | Binary (t for "two")
/c | Print as a character constant
/f | Floating point

You can use these in ad-hoc expressions too:

(​g​d​b​)​ ​p​r​i​n​t​/​o​ ​0​x​f​e​e​d​f​a​c​e​
$​5​ ​=​ ​0​3​7​6​7​3​3​7​5​3​1​6​

(​g​d​b​)​ ​p​r​i​n​t​/​d​ ​"​h​e​l​p​"​
$​7​ ​=​ ​{​1​0​4​,​ ​1​0​1​,​ ​1​0​8​,​ ​1​1​2​,​ ​0​}​

(​g​d​b​)​ ​p​r​i​n​t​ ​/​x​ ​0​6​4​4​
$​8​ ​=​ ​0​x​1​a​4​

These are a life saver if your HP-16C calculator is not handy to do base conversions.

You can also use gdb to display arrays. There was a billboard in Silicon Valley that showed something like this:

i​n​t​ ​i​m​s​g​[​]​ ​=​ ​{​7​8​,​ ​1​1​1​,​ ​1​1​9​,​ ​3​2​,​ ​7​2​,​ ​1​0​5​,​ ​1​1​4​,​ ​1​0​5​,​ ​1​1​0​,​ ​1​0​3​,​ ​0​}​;​

I wonder what that means? The gdb array display features can help us out. By putting a type in curly braces, you tell gdb that you are interested in seeing data shown as an array. You can show a certain number of elements in the array:

(​g​d​b​)​ ​p​r​i​n​t​ ​{​i​n​t​}​ ​i​m​s​g​ ​@​ ​1​0​
$​2​ ​=​ ​{​7​8​,​ ​1​1​1​,​ ​1​1​9​,​ ​3​2​,​ ​7​2​,​ ​1​0​5​,​ ​1​1​4​,​ ​1​0​5​,​ ​1​1​0​,​ ​1​0​3​}​

You can also show a slice of an array. This shows two elements, starting at the third index (which is actually the fourth element):

(​g​d​b​)​ ​p​r​i​n​t​ ​{​i​n​t​}​(​i​m​s​g​ ​+​ ​3​)​@​2​
$​3​ ​=​ ​{​3​2​,​ ​7​2​}​

and you can print each of the elements of the array as a specific type:

(​g​d​b​)​ ​p​r​i​n​t​/​c​ ​{​i​n​t​}​ ​i​m​s​g​ ​@​ ​1​0​
$​4​ ​=​ ​{​7​8​ ​'​N​'​,​ ​1​1​1​ ​'​o​'​,​ ​1​1​9​ ​'​w​'​,​ ​3​2​ ​'​ ​'​,​ ​7​2​ ​'​H​'​,​ ​1​0​5​ ​'​i​'​,​ ​1​1​4​ ​'​r​'​,​
 ​ ​ ​ ​ ​ ​1​0​5​ ​'​i​'​,​ ​1​1​0​ ​'​n​'​,​ ​1​0​3​ ​'​g​'​}​

which spells out “Now Hiring” in ASCII.

You can look at static/global variables in other scopes by qualifying the variable name with a scope:

by file | "file"::variable-name
by function | function-name::variable-name

If you want to see all of the processor’s registers, use info registers, which shows all registers except the floating point ones. info all-registers shows all of them, including the vector registers.

For Objective-C programs, there are a couple of commands:

info classes | Show all classes that have debugging symbols.
info selectors | Show all selectors.

If you are looking at a variable, whatis variable-name will show you the type of the variable name. ptype type-name will show you the data structure for that type.

Lastly, you can call functions in your program with call function-name, independent of the main flow of execution that gdb currently has interrupted. This is nice if you have a complex data structure – you can write a program to look at the data structure and return a string that presents it in a more readable form. This is pretty much what the po (print-object) command does. It invokes the -debugDescription method if the object responds to it and falls back to the -description method if it doesn’t. One thing to look out for is that this function could change the program state.

Changing Data

This is really easy. Use set var varname = expression, where expression can include standard C operators (+, -, |, &, etc.) and can call functions in your program.

Changing Execution Flow

You can bail out of a function early with return. For functions that return values, you can also specify a return value. This is very useful if you know the function is going to return a bad value and you know what it should be returning.

finish will continue execution until the current function ends and then gdb will break back in again. until will resume execution and break on the next instruction after where until was invoked. This is useful (as seen above) for letting a loop finish.

Handy Tricks

Sometimes you are running, and you get an error like this:

a​.​o​u​t​(​1​1​2​3​3​)​ ​m​a​l​l​o​c​:​ ​*​*​*​ ​e​r​r​o​r​ ​f​o​r​ ​o​b​j​e​c​t​ ​0​x​1​0​0​1​2​0​:​ ​d​o​u​b​l​e​ ​f​r​e​e​
*​*​*​ ​s​e​t​ ​a​ ​b​r​e​a​k​p​o​i​n​t​ ​i​n​ ​m​a​l​l​o​c​_​e​r​r​o​r​_​b​r​e​a​k​ ​t​o​ ​d​e​b​u​g​

Usually by the time you can react, your program has moved far past that. As suggested, you can set a breakpoint on malloc_error_break to catch the offending free().

Another even more common occurrence in Cocoa programming is this error:

2​0​1​0​-​0​1​-​2​0​ ​2​0​:​0​3​:​3​8​.​0​8​0​ ​b​a​d​m​e​s​s​a​g​e​[​1​1​3​0​3​:​1​0​b​]​ ​*​*​*​ ​-​[​N​S​C​F​A​r​r​a​y​
 ​ ​ ​ ​f​r​o​b​u​l​a​t​e​:​]​:​ ​u​n​r​e​c​o​g​n​i​z​e​d​ ​s​e​l​e​c​t​o​r​ ​s​e​n​t​ ​t​o​ ​i​n​s​t​a​n​c​e​ ​0​x​1​0​4​a​b​0​
2​0​1​0​-​0​1​-​2​0​ ​2​0​:​0​3​:​3​8​.​0​8​2​ ​b​a​d​m​e​s​s​a​g​e​[​1​1​3​0​3​:​1​0​b​]​ ​*​*​*​ ​T​e​r​m​i​n​a​t​i​n​g​ ​a​p​p​ ​d​u​e​
 ​ ​ ​ ​t​o​ ​u​n​c​a​u​g​h​t​ ​e​x​c​e​p​t​i​o​n​ ​'​N​S​I​n​v​a​l​i​d​A​r​g​u​m​e​n​t​E​x​c​e​p​t​i​o​n​'​,​ ​r​e​a​s​o​n​:​
 ​ ​ ​ ​'​*​*​*​ ​-​[​N​S​C​F​A​r​r​a​y​ ​f​r​o​b​u​l​a​t​e​:​]​:​ ​u​n​r​e​c​o​g​n​i​z​e​d​ ​s​e​l​e​c​t​o​r​ ​s​e​n​t​ ​t​o​
 ​ ​ ​ ​i​n​s​t​a​n​c​e​ ​0​x​1​0​4​a​b​0​'​

By the time you see this, your program is long past the point of the error. You can set a breakpoint on -[NSException raise] to break every time this happens. If you are using native Objective-C exceptions, discussed in Chapter 5: Exceptions, Error Handling, and Signals, you can put a breakpoint on the objc_exception_throw function to catch @throws when they happen. If not, you can break on -[NSException raise].

It would be really handy to have a breakpoint put on objc_exception_throw() every time you run gdb. You could add it to your Xcode project and to the project templates. But that would not help you if you get someone else’s project and they have this error. When gdb starts up, it looks for a file in your home directory called .gdbinit. It will read each line and execute it as if you had typed it in yourself. My .gdbinit contains:

Example 8.1. .gdbinit

f​b​ ​o​b​j​c​_​e​x​c​e​p​t​i​o​n​_​t​h​r​o​w​
f​b​ ​m​a​l​l​o​c​_​e​r​r​o​r​_​b​r​e​a​k​
f​b​ ​_​N​S​L​o​c​k​E​r​r​o​r​
f​b​ ​N​S​K​V​O​D​e​a​l​l​o​c​a​t​e​B​r​e​a​k​
f​b​ ​_​N​S​F​a​s​t​E​n​u​m​e​r​a​t​i​o​n​M​u​t​a​t​i​o​n​H​a​n​d​l​e​r​
f​b​ ​m​a​l​l​o​c​_​p​r​i​n​t​f​
f​b​ ​_​N​S​A​u​t​o​r​e​l​e​a​s​e​N​o​P​o​o​l​
f​b​ ​C​G​E​r​r​o​r​B​r​e​a​k​p​o​i​n​t​

This places breakpoints on a number of toolkit-supplied error handler functions. fb stands for future break. gdb attempts to set the breakpoint whenever it loads a shared library or a framework. Eventually it will load the framework that contains objc_exception_throw or malloc_error_break and set the breakpoint there.

What’s nice is that this sets stuff for every gdb session you run, no matter what is or is not set in an Xcode project.

When you are dealing with Objective-C code, you can poke around the processor registers to see some information about a method’s arguments even if you do not have debugging symbols for that method (such as something from Cocoa).

In an i386 method, a pointer to self will generally be 8 bytes above the address stored in the EBP register, which gdb calls $ebp. A pointer to the method’s selector, which currently is actually a pointer to a string, is just past that. Integer and pointer arguments then go up from there.

(​g​d​b​)​ ​p​o​ ​*​(​i​d​ ​*​)​(​$​e​b​p​ ​+​ ​8​)​
<​B​W​S​t​i​t​c​h​V​i​e​w​:​ ​0​x​1​1​0​7​c​0​>​
(​g​d​b​)​ ​p​r​i​n​t​ ​*​(​c​h​a​r​ ​*​*​)​(​$​e​b​p​ ​+​ ​1​2​)​
$​1​5​ ​=​ ​0​x​9​3​9​d​a​5​d​0​ ​"​d​r​a​w​R​e​c​t​:​"​

In an x86_64 method, the self pointer will be in the RDI register ($rdi), and the method selector in the RSI register ($rsi).

(​g​d​b​)​ ​p​o​ ​$​r​d​i​
<​B​W​S​t​i​t​c​h​V​i​e​w​:​ ​0​x​1​0​0​1​0​c​c​b​0​>​
(​g​d​b​)​ ​p​ ​(​c​h​a​r​ ​*​)​$​r​s​i​
$​1​ ​=​ ​0​x​7​f​f​f​8​4​0​3​6​f​3​8​ ​"​d​r​a​w​R​e​c​t​:​"​

Lastly, you can debug programs remotely, meaning that you do not have to be physically present at the machine where the program being debugged is running. For embedded systems and kernel programming, you can set up a network connection or a serial line. For ordinary, every day programs, it is much easier to just ssh into the box (which means you need a login) and run the program. You can also attach gdb to a program that is already running. This is very useful for the user who always has some kind of bad problem, but it never happens when you are around watching. In this case, ssh into their machine, attach to the program, set some breakpoints, do a continue, and leave it. Eventually the problem will manifest itself, and you can poke around and see what is going wrong.

Debugging Techniques

Being able to use debugging tools is only part of the debugging battle. Becoming an effective debugger is a holistic, never-ending process. I have been debugging software for over twenty years, and I am still learning new ways of producing (and finding) program errors. Here are some things I have learned over the years.

One of the most important things to remember is that bugs are just errors. They are mistakes that people, either you or others, have made. They are not things that randomly crawl into your code. Some programmers don’t call bugs “bugs” but instead use the more accurate (but less fun) term “defect” to emphasize that bugs are just mistakes. Knowing that a bug can be tracked down to a mistake that has been made along the line takes some of the mystery out of them. One of the nice things about being human is that we get to make mistakes, and I find that most of my best learning happens in the context of having made a mistake. So long as you do not make the same mistake again and again, there is no shame in messing up every now and then.

One piece of advice I wish I had received early on in my career is “try not to get too debugger happy.” Debuggers are great tools, but they are not the hammer to use to pound all nails. When you see a bug, the debugger might not be the most effective way to find the problem. Sometimes inspecting code or writing a test program can isolate the program faster than cranking up the debugger. Robert C. Martin, a long-time regular in the comp.object newsgroup and author of a number of excellent books, posted a blog article claiming that “Debuggers Are a Wasteful Timesink.” He noticed in classes he teaches that some students waste a lot of time in debuggers when inspection or some caveman debugging can find the problems more quickly.

“Wasteful Timesink” is a bit of an extreme position, but it did elicit a very interesting discussion. The Java and scripting language crowd (Perl, Python, etc.) frequently said that they almost never used debuggers and wondered why they are such a big issue. The C and C++ crowd, on the other hand, use debuggers all the time due to the low-level nature of the languages. Because most Mac applications are coded in C, C++, or Objective-C, we do have a need for debuggers, but there are other techniques to employ.

Many of us have had the experience of going to a co-worker, starting to explain a bug, and about half way through the description saying, “Never mind, I know what it is now.” The act of having to explain the problem to another person helps solidify the evidence you have gathered about the bug and also helps your subconscious mind work on the problem and move toward a solution. I have worked with programmers that keep a teddy bear, rubber duck, or Bill the Cat doll on their desk and regularly use it as a sounding-board for debugging.

Sometimes you come across code that “smells.” You look at some code, and something just does not smell right. There may be conditionals that are very deeply nested, or you see a lot of copy and paste with minor tweaks, or you find thousand-line functions with nested switch statements and gotos that jump backwards. Code like this frequently is a source of bugs and can be a good first place to start tracking down problems.

If you have a particularly nasty problem, keep a log of what you have tried and what results you got. You can use a piece of paper or even just a plain text file to keep the log. You may find yourself in the third or fourth day of tracking down a problem having forgotten a vital piece of data you found on the first day. You can also look at your accumulated data for patterns. I am a fan of Flying Meat’s VoodooPad for keeping my debug logs.

Because C, C++, and Objective-C programmers are particularly dependent on debuggers, you should write code with debugging in mind. Objective-C is particularly bad, in that it makes it very easy to deeply chain method calls. New Cocoa programmers, and macho “see how studly I am” programmers, like to deeply nest their Objective-C code. It does look pretty cool, and it does reduce the amount of vertical space consumed by the code. Here is a contrived but representative example:

[​[​d​o​c​u​m​e​n​t​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​[​t​a​b​l​e​V​i​e​w​ ​s​e​l​e​c​t​e​d​R​o​w​]​]​
 ​ ​ ​ ​ ​s​e​t​F​o​n​t​:​[​N​S​F​o​n​t​ ​l​a​b​e​l​F​o​n​t​O​f​S​i​z​e​:​[​N​S​F​o​n​t​ ​l​a​b​e​l​F​o​n​t​S​i​z​e​]​]​]​

Now suppose you have a problem on this line of code. It is difficult to use a debugger on this statement because you cannot place a breakpoint on the interior method calls. Is the selectedRow a sane value? What is the document object that is being accessed and having its font set? When I am given code like this to debug, I break it apart:

c​o​n​s​t​ ​N​S​I​n​t​e​g​e​r​ ​s​e​l​e​c​t​e​d​R​o​w​ ​=​ ​[​t​a​b​l​e​V​i​e​w​ ​s​e​l​e​c​t​e​d​R​o​w​]​;​
P​a​r​a​g​r​a​p​h​ ​*​c​o​n​s​t​ ​p​a​r​a​g​r​a​p​h​ ​=​ ​[​d​o​c​u​m​e​n​t​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​s​e​l​e​c​t​e​d​R​o​w​]​;​
c​o​n​s​t​ ​C​G​F​l​o​a​t​ ​l​a​b​e​l​F​o​n​t​S​i​z​e​ ​=​ ​[​N​S​F​o​n​t​ ​l​a​b​e​l​F​o​n​t​S​i​z​e​]​;​
N​S​F​o​n​t​ ​*​c​o​n​s​t​ ​l​a​b​e​l​F​o​n​t​ ​=​ ​[​N​S​F​o​n​t​ ​l​a​b​e​l​F​o​n​t​O​f​S​i​z​e​:​l​a​b​e​l​F​o​n​t​S​i​z​e​]​;​
[​p​a​r​a​g​r​a​p​h​ ​s​e​t​F​o​n​t​:​l​a​b​e​l​F​o​n​t​]​;​

You can now put a breakpoint on any of these lines of code. You can also step through the code and inspect intermediate results. The usual complaint upon seeing code like this is “But that’s inefficient!” Until you have actually measured this code and the original code, you do not know that for sure. The compiler is free, in an optimized build, to remove variables and shuffle code around. Both versions can run equally as fast, but the second is easier to read, and certainly easier to debug.

Tracking down problems

You have to find the problem before you can fix it. In terms of finding problems, crashers are my favorite kind of bug. Usually the cause is very simple, and usually debuggers can give you a pointer directly to the smoking gun that caused the problem or at least the start of the trail to the ultimate problem.

If a problem is reproducible, it is dead. If you have a reproducible test case, you can, with enough tenacity, work the problem back to its origins. If you do not have a reproducible test case, do not give up. It just means you have more work ahead of you. Try to get as much information about reproducing the problem as you can. If you have the luxury of a lot of different bug reports about the sample problem, you may be able to glean enough data to construct a way to reproduce the bug.

Intermittent problems are some of the hardest to fix. You might have a server that misbehaves every couple of days, or you may have one user that makes your program crash. In situations like this, you can “camp” on the program. Attach to the program with a debugger and just let it sit until the program crashes or triggers a conditional breakpoint. A lot of times you can remotely log into another machine and attach to the misbehaving program. Then let it sit there until the problem happens. In the meantime, you can continue doing work rather than sitting and watching over someone’s shoulder for hours.

You can apply the binary search algorithm to debugging. Delete a chunk of code from a misbehaving program. If the problem still happens, you can ignore the code you just deleted; if the problem goes away, you can focus your attention on the code you just removed. It is situations like this where having a source code control system comes in very handy. Mutilate some code, gather some data about the problem, and then revert to a pristine version of the program. Then use the data you just collected to mutilate some other code and gather some more data.

Lastly, be consistent with your test data. If you use a different file in every iteration of your debugging, you can send yourself on wild goose chases if there are actually several different problems. On the other hand, try to use good test data that streamlines your debugging process. You may have a reproducible test case that involves a 300-mebibyte 3D model that takes five minutes to load. If you can reproduce the problem with a 30-KiB model that takes under a second to load, you can spend more time debugging and less time waiting for the data to load.

Debugger techniques

Sometimes it can be faster to compile and link in some printf() or NSLog() statements than to crank up a big program in gdb and set up cascading breakpoints. I like to call this technique “Caveman Debugging.” It is a pretty primitive way to do things, but sometimes the simplest techniques can be the most effective.

Become aware of your debugger’s features. gdb has a huge feature set, with features ranging from very basic to arcane and obscure. I try to read through the gdb documentation at least every year. Features that originally looked weird might now look useful, and new features are added to gdb all the time. Personally, I do not end up using a whole lot of esoteric debugger features. Setting breakpoints, getting stack traces, and doing some single-stepping make up most of my day-to-day work using gdb.

You can use gdb for code exploration, especially when you are first wrapping your mind around a new codebase. Pick an interesting function and put a breakpoint on it. Run the program and see who is calling that function and under what circumstances. Sometimes you find out that a function you think is the cause of a problem never actually gets called.

You can also use a debugger to single-step through brand new code. Bugs are generally introduced by new code, so it makes sense to really scrutinize new code that is added to a program. A common technique is to single-step through all new code and verify that it is behaving properly.

For the More Curious: Core Files

Core files are a Unix-ism where a program that has crashed (usually by trying to read or write into memory it does not have access to) will write its entire address space to disk. You can then poke around this core file with gdb and see what was happening when the program crashed, kind of like a software autopsy.

By default on Mac OS X, core files are not created when your program crashes. Core files take a long time to write on OS X, and, on machines with less-than-stellar disk throughput, it can hose your machine for a fair number of seconds while the core file is being written. So you will not get core files unless you ask for them.

Example 8.2 is a program that can generate errors that can drop core files:

Example 8.2. assert.m

/​/​ ​a​s​s​e​r​t​.​m​ ​-​-​ ​m​a​k​e​ ​a​ ​f​a​l​s​e​ ​a​s​s​e​r​t​i​o​n​,​ ​t​h​e​r​e​b​y​ ​d​u​m​p​i​n​g​ ​c​o​r​e​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​a​s​s​e​r​t​ ​a​s​s​e​r​t​.​m​

#​i​m​p​o​r​t​ ​<​a​s​s​e​r​t​.​h​>​ ​ ​/​/​ ​f​o​r​ ​a​s​s​e​r​t​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​/​/​ ​f​o​r​ ​s​t​r​l​e​n​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​

v​o​i​d​ ​a​n​o​t​h​e​r​F​u​n​c​t​i​o​n​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​o​o​k​)​ ​{​
 ​ ​ ​ ​a​s​s​e​r​t​ ​(​s​t​r​l​e​n​(​o​o​k​)​ ​>​ ​0​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​w​h​e​e​e​e​!​ ​G​o​t​ ​s​t​r​i​n​g​ ​%​s​\​n​"​,​ ​o​o​k​)​;​
}​ ​ ​/​/​ ​a​n​o​t​h​e​r​F​u​n​c​t​i​o​n​

v​o​i​d​ ​s​o​m​e​F​u​n​c​t​i​o​n​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​b​l​a​h​)​ ​{​
 ​ ​ ​ ​a​n​o​t​h​e​r​F​u​n​c​t​i​o​n​ ​(​b​l​a​h​)​;​
}​ ​ ​/​/​ ​s​o​m​e​F​u​n​c​t​i​o​n​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​s​o​m​e​F​u​n​c​t​i​o​n​ ​(​a​r​g​v​[​1​]​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​ ​/​/​ ​m​a​i​n​

If you run this with no arguments, you will get a segmentation fault because anotherFunction() tries to strlen() a NULL pointer:

$​ ​.​/​a​s​s​e​r​t​
S​e​g​m​e​n​t​a​t​i​o​n​ ​f​a​u​l​t​

If it is run with an argument of "", an assertion will be raised that could cause a core dump. If run with a non-empty argument, it will print the argument.

$​ ​.​/​a​s​s​e​r​t​ ​"​B​o​r​k​!​"​
w​h​e​e​e​e​!​ ​G​o​t​ ​s​t​r​i​n​g​ ​B​o​r​k​!​

If you run it using the default environment, no core file is dropped. You can tell your shell to allow programs to drop cores. The ulimit command lets you control resource limits. The -a command flag shows the current limits.

$​ ​u​l​i​m​i​t​ ​-​a​
c​o​r​e​ ​f​i​l​e​ ​s​i​z​e​ ​ ​ ​ ​ ​ ​ ​ ​(​b​l​o​c​k​s​,​ ​-​c​)​ ​0​
d​a​t​a​ ​s​e​g​ ​s​i​z​e​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​k​b​y​t​e​s​,​ ​-​d​)​ ​6​1​4​4​
f​i​l​e​ ​s​i​z​e​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​b​l​o​c​k​s​,​ ​-​f​)​ ​u​n​l​i​m​i​t​e​d​
m​a​x​ ​l​o​c​k​e​d​ ​m​e​m​o​r​y​ ​ ​ ​ ​ ​(​k​b​y​t​e​s​,​ ​-​l​)​ ​u​n​l​i​m​i​t​e​d​
m​a​x​ ​m​e​m​o​r​y​ ​s​i​z​e​ ​ ​ ​ ​ ​ ​ ​(​k​b​y​t​e​s​,​ ​-​m​)​ ​u​n​l​i​m​i​t​e​d​
o​p​e​n​ ​f​i​l​e​s​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​-​n​)​ ​2​5​6​
p​i​p​e​ ​s​i​z​e​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​5​1​2​ ​b​y​t​e​s​,​ ​-​p​)​ ​1​
s​t​a​c​k​ ​s​i​z​e​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​k​b​y​t​e​s​,​ ​-​s​)​ ​8​1​9​2​
c​p​u​ ​t​i​m​e​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​s​e​c​o​n​d​s​,​ ​-​t​)​ ​u​n​l​i​m​i​t​e​d​
m​a​x​ ​u​s​e​r​ ​p​r​o​c​e​s​s​e​s​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​-​u​)​ ​2​6​6​
v​i​r​t​u​a​l​ ​m​e​m​o​r​y​ ​ ​ ​ ​ ​ ​ ​ ​(​k​b​y​t​e​s​,​ ​-​v​)​ ​u​n​l​i​m​i​t​e​d​

Note that core file size is zero blocks. You can increase the limit with

$​ ​u​l​i​m​i​t​ ​-​c​ ​u​n​l​i​m​i​t​e​d​

Now, if you run the program

$​ ​.​/​a​s​s​e​r​t​ ​"​"​
A​s​s​e​r​t​i​o​n​ ​f​a​i​l​e​d​:​ ​(​s​t​r​l​e​n​(​o​o​k​)​ ​>​ ​0​)​,​ ​f​u​n​c​t​i​o​n​ ​a​n​o​t​h​e​r​F​u​n​c​t​i​o​n​,​
 ​ ​ ​ ​f​i​l​e​ ​a​s​s​e​r​t​.​m​,​ ​l​i​n​e​ ​1​4​.​
A​b​o​r​t​ ​t​r​a​p​ ​(​c​o​r​e​ ​d​u​m​p​e​d​)​

core files get dumped in the /cores directory with the name core.process-id.

Then you can look at it in gdb:

$​ ​g​d​b​ ​.​/​a​s​s​e​r​t​ ​/​c​o​r​e​s​/​c​o​r​e​.​1​1​0​4​
[​.​.​.​ ​c​o​p​y​r​i​g​h​t​ ​s​t​u​f​f​ ​.​.​.​]​
#​0​ ​ ​0​x​0​0​0​0​7​f​f​f​8​2​f​c​5​5​d​6​ ​i​n​ ​_​_​k​i​l​l​ ​(​)​

So the program terminated in the ___kill() function. Look at the whole stack trace:

(​g​d​b​)​ ​w​h​e​r​e​
#​0​ ​ ​0​x​0​0​0​0​7​f​f​f​8​2​f​c​5​5​d​6​ ​i​n​ ​_​_​k​i​l​l​ ​(​)​
#​1​ ​ ​0​x​0​0​0​0​7​f​f​f​8​3​0​6​5​c​d​6​ ​i​n​ ​a​b​o​r​t​ ​(​)​
#​2​ ​ ​0​x​0​0​0​0​7​f​f​f​8​3​0​5​2​c​9​c​ ​i​n​ ​_​_​a​s​s​e​r​t​_​r​t​n​ ​(​)​
#​3​ ​ ​0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​e​2​f​ ​i​n​ ​a​n​o​t​h​e​r​F​u​n​c​t​i​o​n​ ​(​o​o​k​=​0​x​7​f​f​f​5​f​b​f​f​6​9​9​ ​"​"​)​ ​a​t​ ​a​s​s​e​r​t​.​m​:​1​1​
#​4​ ​ ​0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​e​5​b​ ​i​n​ ​s​o​m​e​F​u​n​c​t​i​o​n​ ​(​b​l​a​h​=​0​x​7​f​f​f​5​f​b​f​f​6​9​9​ ​"​"​)​ ​a​t​ ​a​s​s​e​r​t​.​m​:​1​6​
#​5​ ​ ​0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​e​7​c​ ​i​n​ ​m​a​i​n​ ​(​a​r​g​c​=​2​,​ ​a​r​g​v​=​0​x​7​f​f​f​5​f​b​f​f​5​2​8​)​ ​a​t​ ​a​s​s​e​r​t​.​m​:​2​0​

Move to the third stack frame:

(​g​d​b​)​ ​f​r​a​m​e​ ​4​
#​3​ ​ ​0​x​0​0​0​0​0​0​0​1​0​0​0​0​0​e​2​f​ ​i​n​ ​a​n​o​t​h​e​r​F​u​n​c​t​i​o​n​ ​(​o​o​k​=​0​x​7​f​f​f​5​f​b​f​f​6​9​9​ ​"​"​)​ ​a​t​ ​a​s​s​e​r​t​.​m​:​1​1​
1​1​ ​ ​ ​ ​a​s​s​e​r​t​ ​(​s​t​r​l​e​n​(​o​o​k​)​ ​>​ ​0​)​;​

This is the assert, the smoking gun, so you know exactly what happened.

You can turn off core dumps by doing

$​ ​u​l​i​m​i​t​ ​-​c​ ​0​

One place where core files are very useful is for crashes that happen out in the field, where the user can send you the core file for later dissection. However, having them run a shell and set limits might not be practical. You can programmatically tell the system you want to drop a core file even if the shell limit is zero. Recall the discussion in Chapter 7: Memory of process resource limits. One of the resource limits is RLIMIT_CORE, the largest size (in bytes) of core files. You can use the resource limit calls to increase the coredump size. Example 8.3 is a modified assert.m:

Example 8.3. assert2.m

/​/​ ​a​s​s​e​r​t​2​.​m​ ​-​-​ ​m​a​k​e​ ​a​ ​f​a​l​s​e​ ​a​s​s​e​r​t​i​o​n​,​ ​t​h​e​r​e​b​y​ ​d​u​m​p​i​n​g​ ​c​o​r​e​

/​/​ ​g​c​c​ ​-​s​t​d​=​c​9​9​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​a​s​s​e​r​t​2​ ​a​s​s​e​r​t​2​.​m​

#​i​m​p​o​r​t​ ​<​a​s​s​e​r​t​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​a​s​s​e​r​t​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​l​e​n​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​y​s​/​r​e​s​o​u​r​c​e​.​h​>​ ​ ​/​/​ ​f​o​r​ ​s​e​t​r​l​i​m​i​t​
#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​

v​o​i​d​ ​a​n​o​t​h​e​r​F​u​n​c​t​i​o​n​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​o​o​k​)​ ​{​
 ​ ​ ​ ​a​s​s​e​r​t​ ​(​s​t​r​l​e​n​(​o​o​k​)​ ​>​ ​0​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​w​h​e​e​e​e​!​ ​G​o​t​ ​s​t​r​i​n​g​ ​%​s​\​n​"​,​ ​o​o​k​)​;​
}​ ​ ​/​/​ ​a​n​o​t​h​e​r​F​u​n​c​t​i​o​n​

v​o​i​d​ ​s​o​m​e​F​u​n​c​t​i​o​n​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​b​l​a​h​)​ ​{​
 ​ ​ ​ ​a​n​o​t​h​e​r​F​u​n​c​t​i​o​n​ ​(​b​l​a​h​)​;​
}​ ​ ​/​/​ ​s​o​m​e​F​u​n​c​t​i​o​n​

v​o​i​d​ ​e​n​a​b​l​e​C​o​r​e​D​u​m​p​s​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​s​t​r​u​c​t​ ​r​l​i​m​i​t​ ​r​l​ ​=​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​.​r​l​i​m​_​c​u​r​ ​=​ ​R​L​I​M​_​I​N​F​I​N​I​T​Y​,​
 ​ ​ ​ ​ ​ ​ ​ ​.​r​l​i​m​_​m​a​x​ ​=​ ​R​L​I​M​_​I​N​F​I​N​I​T​Y​
 ​ ​ ​ ​}​;​

 ​ ​ ​ ​i​f​ ​(​s​e​t​r​l​i​m​i​t​(​R​L​I​M​I​T​_​C​O​R​E​,​ ​&​r​l​)​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​ ​i​n​ ​s​e​t​r​l​i​m​i​t​ ​f​o​r​ ​R​L​I​M​I​T​_​C​O​R​E​:​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​}​
}​ ​ ​/​/​ ​e​n​a​b​l​e​C​o​r​e​D​u​m​p​s​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​e​n​a​b​l​e​C​o​r​e​D​u​m​p​s​(​)​;​
 ​ ​ ​ ​s​o​m​e​F​u​n​c​t​i​o​n​(​a​r​g​v​[​1​]​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​ ​/​/​ ​m​a​i​n​

If you run it now, you get a core file:

$​ ​.​/​a​s​s​e​r​t​2​ ​"​"​
A​s​s​e​r​t​i​o​n​ ​f​a​i​l​e​d​:​ ​(​s​t​r​l​e​n​(​o​o​k​)​ ​>​ ​0​)​,​ ​f​u​n​c​t​i​o​n​ ​a​n​o​t​h​e​r​F​u​n​c​t​i​o​n​,​
 ​ ​ ​ ​f​i​l​e​ ​a​s​s​e​r​t​2​.​m​,​ ​l​i​n​e​ ​1​6​.​
A​b​o​r​t​ ​t​r​a​p​ ​(​c​o​r​e​ ​d​u​m​p​e​d​)​

To use this in a real program, you might want to put in some secret way that the user can execute that function to allow core dumping. Like, “Command-Shift-Option-click the About box OK button, and you will get a debug panel. Check the Core Dump check box and then do whatever it is that crashes the program.”

For the More Curious: Stripping

Debugging symbols are pretty big and can bloat your executable. A simple Cocoa application of mine weighs in at 262K with debug symbols, whereas without the symbols it is about 30K. Depending on the application, keeping debug symbols around might not be a bad thing. For a high-traffic web server application, we kept the debug symbols to make diagnosing production problems easier (and it really came in handy sometimes). If your program has 500 megs of graphics and support files, a couple of hundred K of debug symbols probably is not too bad. On the other hand, if you are writing smaller downloadable applications, the extra hundred K or a meg could be a significant barrier to your program being used. Having debug symbols also makes it easier for people to reverse-engineer your code.

To strip the debug symbols, you can either rebuild your program using the Deployment target in Xcode, or you can just run the strip program against the executable. For instance:

$​ ​l​s​ ​-​l​ ​B​o​r​k​P​a​d​
-​r​w​x​r​-​x​r​-​x​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​2​6​2​7​8​0​ ​A​u​g​ ​1​9​ ​2​1​:​1​0​ ​B​o​r​k​P​a​d​
$​ ​s​t​r​i​p​ ​B​o​r​k​P​a​d​
$​ ​l​s​ ​-​l​ ​B​o​r​k​P​a​d​
-​r​w​x​r​-​x​r​-​x​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​3​0​6​0​8​ ​A​u​g​ ​2​0​ ​2​1​:​3​7​ ​B​o​r​k​P​a​d​

So what happens if your program crashes out in the field? If you can get a core file, you can load the core file into a gdb session with an unstripped version of your executable and be able to debug symbolically. For example, use the assert2 program, make a stripped copy, and generate a core file:

$​ ​c​p​ ​a​s​s​e​r​t​2​ ​s​t​r​i​p​p​e​d​
$​ ​s​t​r​i​p​ ​s​t​r​i​p​p​e​d​
$​ ​.​/​s​t​r​i​p​p​e​d​ ​"​"​
A​s​s​e​r​t​i​o​n​ ​f​a​i​l​e​d​:​ ​(​s​t​r​l​e​n​(​o​o​k​)​ ​>​ ​0​)​,​ ​f​u​n​c​t​i​o​n​ ​a​n​o​t​h​e​r​F​u​n​c​t​i​o​n​,​
 ​ ​ ​ ​f​i​l​e​ ​a​s​s​e​r​t​2​.​m​,​ ​l​i​n​e​ ​1​6​.​
A​b​o​r​t​ ​t​r​a​p​ ​(​c​o​r​e​ ​d​u​m​p​e​d​)​

If you gdb the stripped program, the stack traces are not very useful:

$​ ​g​d​b​ ​.​/​s​t​r​i​p​p​e​d​ ​/​c​o​r​e​s​/​c​o​r​e​.​2​3​4​2​
.​.​.​
(​g​d​b​)​ ​b​t​
#​0​ ​ ​0​x​9​0​0​1​b​5​2​c​ ​i​n​ ​k​i​l​l​ ​(​)​
#​1​ ​ ​0​x​9​0​0​5​c​e​e​c​ ​i​n​ ​a​b​o​r​t​ ​(​)​
#​2​ ​ ​0​x​0​0​0​0​1​d​1​8​ ​i​n​ ​d​y​l​d​_​s​t​u​b​_​e​x​i​t​ ​(​)​
#​3​ ​ ​0​x​0​0​0​0​1​c​2​0​ ​i​n​ ​?​?​ ​(​)​
#​4​ ​ ​0​x​0​0​0​0​1​c​6​4​ ​i​n​ ​?​?​ ​(​)​
#​5​ ​ ​0​x​0​0​0​0​1​c​a​4​ ​i​n​ ​?​?​ ​(​)​
#​6​ ​ ​0​x​0​0​0​0​1​9​7​8​ ​i​n​ ​?​?​ ​(​)​
#​7​ ​ ​0​x​0​0​0​0​1​7​f​8​ ​i​n​ ​?​?​ ​(​)​

But if you use the original, unstripped file, you have good stack traces:

$​ ​g​d​b​ ​a​s​s​e​r​t​2​ ​/​c​o​r​e​s​/​c​o​r​e​.​2​3​4​2​
.​.​.​
(​g​d​b​)​ ​b​t​
#​0​ ​ ​0​x​9​0​0​1​b​5​2​c​ ​i​n​ ​k​i​l​l​ ​(​)​
#​1​ ​ ​0​x​9​0​0​5​c​e​e​c​ ​i​n​ ​a​b​o​r​t​ ​(​)​
#​2​ ​ ​0​x​0​0​0​0​1​d​1​8​ ​i​n​ ​d​y​l​d​_​s​t​u​b​_​e​x​i​t​ ​(​)​
#​3​ ​ ​0​x​0​0​0​0​1​c​2​0​ ​i​n​ ​a​n​o​t​h​e​r​F​u​n​c​t​i​o​n​ ​(​o​o​k​=​0​x​b​f​f​f​f​b​8​7​ ​"​\​0​0​0​"​.​.​.​)​ ​
 ​ ​ ​ ​a​t​ ​a​s​s​e​r​t​2​.​m​:​1​9​
#​4​ ​ ​0​x​0​0​0​0​1​c​6​4​ ​i​n​ ​s​o​m​e​F​u​n​c​t​i​o​n​ ​(​b​l​a​h​=​0​x​b​f​f​f​f​b​8​7​ ​"​\​0​0​0​"​.​.​.​)​ ​
 ​ ​ ​ ​a​t​ ​a​s​s​e​r​t​2​.​m​:​2​7​
#​5​ ​ ​0​x​0​0​0​0​1​c​a​4​ ​i​n​ ​m​a​i​n​ ​(​a​r​g​c​=​2​,​ ​a​r​g​v​=​0​x​b​f​f​f​f​a​d​c​)​ ​a​t​ ​a​s​s​e​r​t​2​.​m​:​3​7​
#​6​ ​ ​0​x​0​0​0​0​1​9​7​8​ ​i​n​ ​_​s​t​a​r​t​ ​(​a​r​g​c​=​2​,​ ​a​r​g​v​=​0​x​b​f​f​f​f​a​d​c​,​ ​e​n​v​p​=​0​x​b​f​f​f​f​a​e​8​)​ ​
 ​ ​ ​ ​a​t​ ​/​S​o​u​r​c​e​C​a​c​h​e​/​C​s​u​/​C​s​u​-​4​5​/​c​r​t​.​c​:​2​6​7​
#​7​ ​ ​0​x​0​0​0​0​1​7​f​8​ ​i​n​ ​s​t​a​r​t​ ​(​)​

If you do not want to mess with gdb, or the user emails you a stack trace from the crash reporter, you can use atos to map the address to a symbol to see what function and line caused the problem.

$​ ​a​t​o​s​ ​-​o​ ​a​s​s​e​r​t​2​ ​0​x​0​0​0​0​1​c​a​4​
_​m​a​i​n​ ​(​a​s​s​e​r​t​2​.​m​:​3​8​)​

$​ ​a​t​o​s​ ​-​o​ ​a​s​s​e​r​t​2​ ​0​x​0​0​0​0​1​c​2​0​
_​a​n​o​t​h​e​r​F​u​n​c​t​i​o​n​ ​(​a​s​s​e​r​t​2​.​m​:​2​1​)​

More Advanced GDB Commands

Threads

gdb supports debugging threaded programs. We have lots to say about threading issues in Chapter 20: Multithreading, but for now, here are some useful commands relating to threads.

info threads

Shows information about all the currently active threads. Here is something from a simple Cocoa program while the Page Setup dialog is active:

(​g​d​b​)​ ​i​n​f​o​ ​t​h​r​e​a​d​s​
 ​3​ ​p​r​o​c​e​s​s​ ​1​2​8​ ​t​h​r​e​a​d​ ​0​x​2​1​3​ ​0​x​7​0​9​7​8​ ​i​n​ ​m​a​c​h​_​m​s​g​_​o​v​e​r​w​r​i​t​e​_​t​r​a​p​(​)​
 ​2​ ​p​r​o​c​e​s​s​ ​1​2​8​ ​t​h​r​e​a​d​ ​0​x​1​f​7​ ​0​x​7​0​9​7​8​ ​i​n​ ​m​a​c​h​_​m​s​g​_​o​v​e​r​w​r​i​t​e​_​t​r​a​p​(​)​
*​1​ ​p​r​o​c​e​s​s​ ​1​2​8​ ​t​h​r​e​a​d​ ​0​x​1​6​3​ ​0​x​7​0​9​7​8​ ​i​n​ ​m​a​c​h​_​m​s​g​_​o​v​e​r​w​r​i​t​e​_​t​r​a​p​(​)​

You can change between threads with the thread command and then poke around and see what it is doing:

(​g​d​b​)​ ​t​h​r​e​a​d​ ​3​
[​S​w​i​t​c​h​i​n​g​ ​t​o​ ​t​h​r​e​a​d​ ​3​ ​(​p​r​o​c​e​s​s​ ​1​2​0​8​ ​t​h​r​e​a​d​ ​0​x​2​1​2​3​)​]​
#​0​ ​ ​0​x​7​0​0​0​0​9​7​8​ ​i​n​ ​m​a​c​h​_​m​s​g​_​o​v​e​r​w​r​i​t​e​_​t​r​a​p​ ​(​)​

(​g​d​b​)​ ​b​t​
#​0​ ​ ​0​x​7​0​0​0​0​9​7​8​ ​i​n​ ​m​a​c​h​_​m​s​g​_​o​v​e​r​w​r​i​t​e​_​t​r​a​p​ ​(​)​
#​1​ ​ ​0​x​7​0​0​0​5​a​0​4​ ​i​n​ ​m​a​c​h​_​m​s​g​ ​(​)​
#​2​ ​ ​0​x​7​0​1​7​b​f​8​4​ ​i​n​ ​_​_​C​F​R​u​n​L​o​o​p​R​u​n​ ​(​)​
#​3​ ​ ​0​x​7​0​1​b​7​0​e​c​ ​i​n​ ​C​F​R​u​n​L​o​o​p​R​u​n​S​p​e​c​i​f​i​c​ ​(​)​
#​4​ ​ ​0​x​7​0​1​7​b​8​c​c​ ​i​n​ ​C​F​R​u​n​L​o​o​p​R​u​n​I​n​M​o​d​e​ ​(​)​
#​5​ ​ ​0​x​7​0​6​1​b​e​0​8​ ​i​n​ ​X​I​O​A​u​d​i​o​D​e​v​i​c​e​M​a​n​a​g​e​r​:​:​N​o​t​i​f​i​c​a​t​i​o​n​T​h​r​e​a​d​ ​(​)​
#​6​ ​ ​0​x​7​0​6​1​4​1​c​0​ ​i​n​ ​C​A​P​T​h​r​e​a​d​:​:​E​n​t​r​y​ ​(​)​
#​7​ ​ ​0​x​7​0​0​2​0​5​4​c​ ​i​n​ ​_​p​t​h​r​e​a​d​_​b​o​d​y​ ​(​)​

thread apply

Apply a command to a list of threads. You can use all to run the same command in all threads.

(​g​d​b​)​ ​t​h​r​e​a​d​ ​a​p​p​l​y​ ​a​l​l​ ​w​h​e​r​e​

.​.​.​

T​h​r​e​a​d​ ​2​ ​(​p​r​o​c​e​s​s​ ​1​2​0​8​ ​t​h​r​e​a​d​ ​0​x​1​f​0​7​)​:​
#​0​ ​ ​0​x​7​0​0​0​0​9​7​8​ ​i​n​ ​m​a​c​h​_​m​s​g​_​o​v​e​r​w​r​i​t​e​_​t​r​a​p​ ​(​)​
#​1​ ​ ​0​x​7​0​0​0​5​a​0​4​ ​i​n​ ​m​a​c​h​_​m​s​g​ ​(​)​
#​2​ ​ ​0​x​7​0​0​2​6​a​2​c​ ​i​n​ ​_​p​t​h​r​e​a​d​_​b​e​c​o​m​e​_​a​v​a​i​l​a​b​l​e​ ​(​)​
#​3​ ​ ​0​x​7​0​0​2​6​7​2​4​ ​i​n​ ​p​t​h​r​e​a​d​_​e​x​i​t​ ​(​)​
#​4​ ​ ​0​x​7​0​0​2​0​5​5​0​ ​i​n​ ​_​p​t​h​r​e​a​d​_​b​o​d​y​ ​(​)​

T​h​r​e​a​d​ ​1​ ​(​p​r​o​c​e​s​s​ ​1​2​0​8​ ​t​h​r​e​a​d​ ​0​x​1​6​0​3​)​:​
#​0​ ​ ​0​x​7​0​0​0​0​9​7​8​ ​i​n​ ​m​a​c​h​_​m​s​g​_​o​v​e​r​w​r​i​t​e​_​t​r​a​p​ ​(​)​
#​1​ ​ ​0​x​7​0​0​0​5​a​0​4​ ​i​n​ ​m​a​c​h​_​m​s​g​ ​(​)​
#​2​ ​ ​0​x​7​0​1​7​b​f​8​4​ ​i​n​ ​_​_​C​F​R​u​n​L​o​o​p​R​u​n​ ​(​)​
#​3​ ​ ​0​x​7​0​1​b​7​0​e​c​ ​i​n​ ​C​F​R​u​n​L​o​o​p​R​u​n​S​p​e​c​i​f​i​c​ ​(​)​
#​4​ ​ ​0​x​7​0​1​7​b​8​c​c​ ​i​n​ ​C​F​R​u​n​L​o​o​p​R​u​n​I​n​M​o​d​e​ ​(​)​
.​.​.​
#​2​3​ ​0​x​7​9​3​8​b​e​d​0​ ​i​n​ ​N​S​A​p​p​l​i​c​a​t​i​o​n​M​a​i​n​ ​(​)​
#​2​4​ ​0​x​0​0​0​0​3​6​b​4​ ​i​n​ ​_​s​t​a​r​t​ ​(​)​
#​2​5​ ​0​x​0​0​0​0​3​4​e​4​ ​i​n​ ​s​t​a​r​t​ ​(​)​
#​0​ ​ ​0​x​7​0​0​0​0​9​7​8​ ​i​n​ ​m​a​c​h​_​m​s​g​_​o​v​e​r​w​r​i​t​e​_​t​r​a​p​ ​(​)​

When debugging, one thread is always the focus of debugging, known as the current thread. You can break in particular threads if you wish, using thread apply thread# break .... When the program stops, all threads stop, and when the program starts (even just doing a step), all threads potentially start as well. Note that during the time of the single-step, the other threads can run full bore. The single-stepping only applies to the current thread.

You can change this behavior using the set scheduler-locking mode command. The default mode is off – threads run as just described. You can also set scheduler-locking on to run only the current thread during all gdb commands, or set scheduler-locking step to run only the current thread during step commands, but otherwise let the other threads run as normal. You can view the current mode using show scheduler-locking. When scheduler locking is in effect, you must be careful not to deadlock the application when calling functions from gdb.

9
DTrace

DTrace is a dynamic tracing facility developed by Sun Microsystems for the Solaris operating system. DTrace lets administrators and developers explore the system and understand how it works. It can help you track down program bugs and performance problems. DTrace was introduced with Mac OS X 10.5. Prior to 10.5, ktrace served a similar, but more limited, purpose.

Overview

The heart of DTrace is the probe, where a probe is a named point in executing code. There are probes corresponding to kernel events, function calls, Objective-C message sends, system calls, and many other interesting events, as shown in Figure 9.1. Mac OS X includes tens of thousands of probes, and the count grows with each new OS release. You can add probes to your own applications and libraries to help in profiling and debugging the actions of your own code.

Figure 9.1  DTrace probes

Probes are dynamically inserted as needed when you run an experiment. There is no impact on the system if probes are not engaged because the instrumentation is completely dynamic. This makes DTrace safe to use on a production system to diagnose runtime problems. Probes are implemented similarly to APE (Application Enhancer) or mach_inject(), where some slight of hand is performed to make a function branch to a different place by changing the first few instructions of the function. Therefore, there is no instrumentation code present for inactive probes and no performance degradation when not using DTrace. All probes are automatically disabled once your DTrace experiment ends. Probes are reference-counted, so you can run multiple experiments in parallel.

Providers are what provide the probes. For example, the syscall provider allows probes to be placed on system calls. The profile provider triggers probes at regular time intervals.

The command-line application dtrace is your gateway to all of DTrace’s features. It can list the probes available to you, generate a header file for new probes you wish to add to your code, and trace system behavior. During tracing it engages probes and can aggregate and display data that it has gathered during the tracing.

You can use probes to trigger context-sensitive actions of your own design. The actions you associate with a probe can include getting a stack trace, looking at a time stamp, examining the argument to a function, or examining the return value from a function. Thus, another way to look at probes is as programmable sensors scattered throughout the system. You can attach code to each of the sensors to analyze system activity. The DTrace “workflow” is shown in Figure 9.2, with a script being fed to dtrace, being run in the kernel, accumulating data, and eventually reporting it back to the user.

Figure 9.2  DTrace workflow

The D language

Actions are written using the “D” programming language. D is yet another scripting language, this time one which is heavily influenced by C. A large subset of C is supported along with a special set of functions specifically geared to analyzing system behavior. D programs actually run in kernel-land. Before being run, they are compiled into a safe intermediate form similar to Java bytecode and validated for safety. This is one reason why DTrace is safe to use in a production environment without danger of crashing or corrupting the system.

DTrace scripts conventionally use the filename extension .d. Apple supplies a number of sample scripts in the /usr/bin/ directory that are worth perusing.

Scripts

Example 9.1 is a sample DTrace script.

Example 9.1. keventwatch.d

/​*​ ​W​a​t​c​h​ ​e​n​t​r​y​ ​i​n​t​o​ ​k​e​v​e​n​t​(​)​ ​*​/​
s​y​s​c​a​l​l​:​:​k​e​v​e​n​t​:​e​n​t​r​y​
/​e​x​e​c​n​a​m​e​ ​=​=​ ​"​d​i​r​w​a​t​c​h​e​r​"​ ​|​|​ ​e​x​e​c​n​a​m​e​ ​=​=​ ​"​D​i​r​e​c​t​o​r​y​S​e​r​v​i​c​"​/​
{​
 ​ ​ ​ ​p​r​i​n​t​f​(​"​%​s​ ​c​a​l​l​e​d​ ​k​e​v​e​n​t​(​)​"​,​ ​e​x​e​c​n​a​m​e​)​;​
}​

You can see that D uses C-style /* comments */. Modern C / C++ style comments (//) are not supported.

Here is how you run it:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​s​ ​k​e​v​e​n​t​w​a​t​c​h​.​d​
d​t​r​a​c​e​:​ ​s​c​r​i​p​t​ ​'​k​e​v​e​n​t​w​a​t​c​h​.​d​'​ ​m​a​t​c​h​e​d​ ​1​ ​p​r​o​b​e​
C​P​U​ ​ ​ ​ ​ ​I​D​ ​ ​ ​F​U​N​C​T​I​O​N​:​N​A​M​E​
 ​ ​1​ ​ ​1​7​2​3​8​ ​ ​ ​k​e​v​e​n​t​:​e​n​t​r​y​ ​D​i​r​e​c​t​o​r​y​S​e​r​v​i​c​ ​c​a​l​l​e​d​ ​k​e​v​e​n​t​(​)​
 ​ ​0​ ​ ​1​7​2​3​8​ ​ ​ ​k​e​v​e​n​t​:​e​n​t​r​y​ ​d​i​r​w​a​t​c​h​e​r​ ​c​a​l​l​e​d​ ​k​e​v​e​n​t​(​)​
 ​ ​0​ ​ ​1​7​2​3​8​ ​ ​ ​k​e​v​e​n​t​:​e​n​t​r​y​ ​d​i​r​w​a​t​c​h​e​r​ ​c​a​l​l​e​d​ ​k​e​v​e​n​t​(​)​
 ​ ​1​ ​ ​1​7​2​3​8​ ​ ​ ​k​e​v​e​n​t​:​e​n​t​r​y​ ​D​i​r​e​c​t​o​r​y​S​e​r​v​i​c​ ​c​a​l​l​e​d​ ​k​e​v​e​n​t​(​)​
 ​ ​1​ ​ ​1​7​2​3​8​ ​ ​ ​k​e​v​e​n​t​:​e​n​t​r​y​ ​D​i​r​e​c​t​o​r​y​S​e​r​v​i​c​ ​c​a​l​l​e​d​ ​k​e​v​e​n​t​(​)​

Use sudo because DTrace requires root privileges to run. This makes it less convenient than ktrace, which could run as an ordinary user (useful when diagnosing program problems in the field on someone else’s machine). The -s flag tells dtrace to read a D program from a given file, similar to the -s flag you would give the shell or perl to execute the contents of a file. You can also use the Unix “shebang” technique and include #!/usr/sbin/dtrace -s at the top of a file and make the file executable to automatically invoke DTrace when the script is run.

This particular example watches all calls to kevent(), the primary kqueue interface, across the system. The script prints out the name of the program that called the function, provided it is either the program DirectoryServic[es], or the dirwatcher sample from Chapter 16: kqueue and FSEvents.

The DTrace output includes the CPU the particular kevent() was run on, the integer ID of the probe, the name of the probe, and then the output from our printf() statement. You can suppress the default output by giving dtrace the -q, for quiet, flag.

A D script is built from a collection of clauses. Example 9.1 has just one clause, but you can have arbitrarily many clauses. A clause is made up of three components:

The probe description, which describes what it is you want to trace. In this case, we’re tracing the entry into the kevent() system call.

A set of actions enclosed by curly braces. If there are no actions supplied, just an empty set of braces, the name of the probe is printed out whenever it fires. In this case, we are printing out a line of text that includes a process name.

An optional predicate, delimited by slashes. The actions are run only if the predicate evaluates to a true value. In this example, we are limiting the tracing to two programs. You can see all the programs across the system that call kevent() by removing the predicate.

Probes

A probe is a location or activity to which a set of actions can be bound. The syntax for specifying a probe is a colon specified list of up to four items:

Provider - the instrumentation to be used. Some providers you will encounter are syscall to monitor system calls, io for disk io, and many others.

Module - a specific program location. It could be the name of the module the probe is located in. It could also be the name of a kernel module or the name of a user library.

Function - A specific function name.

Name - An indication of the probe’s semantic meaning. entry and return are common ones.

In keventwatch.d, the only probe was syscall::kevent:entry. The provider is syscall, there is no module, the function is kevent, and the name is entry. Missing probe components are interpreted as wildcards, matching everything they can. The probe description is evaluated from right to left in case there are fewer than four components are specified. You can use shell-style globbing characters for wildcarding: *, ?, [^...]

For example,

syscall::open:entry will trace entries into the open() system call.

syscall::open*:entry will trace any entries into functions that start with “open.” This will match open, open_extended, and open_no_cancel.

syscall:::entry will trace the entry into any system call.

syscall::: will trace all probes published by syscall. There were 869 of these on an OS X 10.6.8 system.

There are a number of ways to explore the set of probes available on your system.

The command dtrace -l (dash ell) will print out every probe that is available in the kernel. On this system, there are a lot of them:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​l​ ​|​ ​w​c​ ​-​l​
 ​ ​ ​8​8​6​3​2​

This actually doesn’t count all the available probes. Some are dynamically generated, such as the profiling probes discussed later. You can set probes on functions and Objective-C methods in user-space, so there is no real upper bound to the number of probable spots. There are also a number of probes that exist per running program. At the time the previous command was run, there were thirteen Cocoa applications being run as well. Quitting one application reduced the total probe count to 87576.

You can redirect dtrace -l out to a file and search through it with your favorite text editor. You can also see what probes match a given description with the -i flag. Here you can see what matches the examples listed earlier:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​l​ ​-​i​ ​s​y​s​c​a​l​l​:​:​o​p​e​n​:​e​n​t​r​y​
 ​ ​ ​I​D​ ​ ​ ​P​R​O​V​I​D​E​R​ ​ ​ ​ ​ ​ ​M​O​D​U​L​E​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​F​U​N​C​T​I​O​N​ ​N​A​M​E​
1​8​6​4​9​ ​ ​ ​ ​s​y​s​c​a​l​l​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​p​e​n​ ​e​n​t​r​y​

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​l​ ​-​i​ ​"​s​y​s​c​a​l​l​:​:​o​p​e​n​*​:​e​n​t​r​y​"​
 ​ ​ ​I​D​ ​ ​ ​P​R​O​V​I​D​E​R​ ​ ​ ​ ​ ​ ​ ​M​O​D​U​L​E​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​F​U​N​C​T​I​O​N​ ​N​A​M​E​
1​8​6​4​9​ ​ ​ ​ ​s​y​s​c​a​l​l​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​p​e​n​ ​e​n​t​r​y​
1​9​1​9​3​ ​ ​ ​ ​s​y​s​c​a​l​l​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​p​e​n​_​e​x​t​e​n​d​e​d​ ​e​n​t​r​y​
1​9​4​3​5​ ​ ​ ​ ​s​y​s​c​a​l​l​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​p​e​n​_​n​o​c​a​n​c​e​l​ ​e​n​t​r​y​

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​l​ ​-​i​ ​s​y​s​c​a​l​l​:​:​:​e​n​t​r​y​ ​|​ ​w​c​ ​-​l​
 ​ ​ ​ ​ ​4​3​5​

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​l​ ​-​i​ ​s​y​s​c​a​l​l​:​:​:​ ​|​ ​w​c​ ​-​l​
 ​ ​ ​ ​ ​8​6​9​

syscall::open*:entry is quoted on the command line to prevent the shell from expanding the asterisk.

You can enable and manage thousands of probes. The more probes you enable, the longer it takes your script to start up because DTrace needs to enable each of the probes and some time to disable each of the probes when the experiment ends.

Providers

A provider is a facility that publishes the probes that you use in your D script. There are a number of built-in providers.

BEGIN and END providers

These are actually called dtrace:::BEGIN and dtrace:::END, but they are usually used as just BEGIN and END. The BEGIN probe fires when the D script begins execution, and the END probe fires when the script finishes. Example 9.2 shows using the BEGIN and END probes.

Example 9.2. begin-end.d

B​E​G​I​N​
{​
 ​ ​ ​ ​t​r​a​c​e​(​"​b​e​g​i​n​ ​t​h​e​ ​b​e​g​u​i​n​e​"​)​;​
 ​ ​ ​ ​e​x​i​t​(​0​)​;​
}​

E​N​D​
{​
 ​ ​ ​ ​t​r​a​c​e​ ​(​"​t​h​a​t​'​s​ ​a​l​l​,​ ​f​o​l​k​s​.​.​.​"​)​;​
}​

Run it:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​s​ ​b​e​g​i​n​-​e​n​d​.​d​
d​t​r​a​c​e​:​ ​s​c​r​i​p​t​ ​'​b​e​g​i​n​-​e​n​d​.​d​'​ ​m​a​t​c​h​e​d​ ​2​ ​p​r​o​b​e​s​
C​P​U​ ​ ​ ​ ​ ​I​D​ ​ ​ ​ ​ ​ ​ ​ ​F​U​N​C​T​I​O​N​:​N​A​M​E​
 ​ ​3​ ​ ​ ​ ​ ​ ​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​B​E​G​I​N​ ​ ​ ​b​e​g​i​n​ ​t​h​e​ ​b​e​g​u​i​n​e​
 ​ ​0​ ​ ​ ​ ​ ​ ​2​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​E​N​D​ ​ ​ ​t​h​a​t​'​s​ ​a​l​l​,​ ​f​o​l​k​s​.​.​.​

The trace function is like printf, but it does not take any formatting arguments. It also automatically generates a newline.

There is also a dtrace:::ERROR probe that fires when an error happens, such as attempting to dereference a NULL pointer.

pid provider

The pid provider lets you restrict a probe to a particular process ID. If you know that Safari is process ID 1313, you can get a list of all available probes with this command:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​l​ ​-​i​ ​"​p​i​d​1​3​1​3​:​:​:​"​ ​>​ ​s​a​f​a​r​i​.​t​x​t​

This might not actually work depending on how much RAM you have. You can try something similar on a smaller application like TextEdit.

DTrace allows you to access command-line arguments within the script. $1 is the first argument after all of arguments that dtrace will consume, $2 is the next one, and so on. Hard-coding a pid is unpleasant to do, so you will typically use $1 to pass a particular process ID on the command-line.

malloc-pid.d tells you when a particular application has called malloc() and how much memory was asked for.

Example 9.3. malloc-pid.d

p​i​d​$​1​:​l​i​b​S​y​s​t​e​m​.​B​.​d​y​l​i​b​:​m​a​l​l​o​c​:​e​n​t​r​y​
{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​m​a​l​l​o​c​ ​o​f​ ​%​d​ ​b​y​t​e​s​ ​f​o​r​ ​%​s​\​n​"​,​ ​a​r​g​0​,​ ​e​x​e​c​n​a​m​e​)​;​
}​

Run it like this:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​q​ ​-​s​ ​m​a​l​l​o​c​-​p​i​d​.​d​ ​1​3​1​3​
m​a​l​l​o​c​ ​o​f​ ​3​2​ ​b​y​t​e​s​ ​f​o​r​ ​S​a​f​a​r​i​
m​a​l​l​o​c​ ​o​f​ ​3​2​ ​b​y​t​e​s​ ​f​o​r​ ​S​a​f​a​r​i​
m​a​l​l​o​c​ ​o​f​ ​5​1​2​ ​b​y​t​e​s​ ​f​o​r​ ​S​a​f​a​r​i​
m​a​l​l​o​c​ ​o​f​ ​2​0​4​8​ ​b​y​t​e​s​ ​f​o​r​ ​S​a​f​a​r​i​
m​a​l​l​o​c​ ​o​f​ ​5​1​2​ ​b​y​t​e​s​ ​f​o​r​ ​S​a​f​a​r​i​
m​a​l​l​o​c​ ​o​f​ ​6​2​0​ ​b​y​t​e​s​ ​f​o​r​ ​S​a​f​a​r​i​
^​C​

The -q flag is used to suppress the usual output from dtrace. The first non-dtrace argument is “1313,” which is the value of $1 in the script. The probe actually becomes pid1313:libSystem.B.dylib:malloc:entry. Because dtrace’s default output has been suppressed, it was necessary to add a newline to Example 9.3’s printf statement.

Notice that the script never ends. You need to interrupt it with Control-C in the terminal to make it stop.

syscall provider

The syscall provider supplies system call entry and return probes. For example:

syscall::read:entry - probe whenever a process reads from a file descriptor

syscall::kill:entry - run the action whenever a process calls kill()

profile provider

profile provides timed event probes. These are frequently used to display accumulated state at intervals. The tick name is used to specify the time, with a number to indicate the time and a suffix to specify the time unit to use. Example 9.4 shows the timer in action.

Example 9.4. lubdub.d

p​r​o​f​i​l​e​:​:​:​t​i​c​k​-​5​s​e​c​
{​
 ​ ​t​r​a​c​e​(​"​f​i​v​e​ ​s​e​c​o​n​d​ ​t​i​m​e​r​"​)​;​
}​

p​r​o​f​i​l​e​:​:​:​t​i​c​k​-​1​m​i​n​
{​
 ​ ​t​r​a​c​e​(​"​o​n​e​ ​m​i​n​u​t​e​ ​t​i​m​e​r​"​)​;​
}​

p​r​o​f​i​l​e​:​:​:​t​i​c​k​-​8​0​0​m​s​e​c​
{​
 ​ ​t​r​a​c​e​(​"​8​0​0​ ​m​i​l​l​i​s​e​c​o​n​d​ ​t​i​m​e​r​"​)​;​
}​

This script prints out their messages after the indicated times. A sample run looks like this:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​s​ ​l​u​b​d​u​b​.​d​
d​t​r​a​c​e​:​ ​s​c​r​i​p​t​ ​'​l​u​b​d​u​b​.​d​'​ ​m​a​t​c​h​e​d​ ​3​ ​p​r​o​b​e​s​
C​P​U​ ​ ​ ​ ​ ​I​D​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​F​U​N​C​T​I​O​N​:​N​A​M​E​
 ​ ​0​ ​ ​1​7​3​7​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​t​i​c​k​-​8​0​0​m​s​e​c​ ​ ​ ​8​0​0​ ​m​i​l​l​i​s​e​c​o​n​d​ ​t​i​m​e​r​
 ​ ​0​ ​ ​1​7​3​7​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​t​i​c​k​-​8​0​0​m​s​e​c​ ​ ​ ​8​0​0​ ​m​i​l​l​i​s​e​c​o​n​d​ ​t​i​m​e​r​
 ​ ​0​ ​ ​1​7​3​7​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​t​i​c​k​-​8​0​0​m​s​e​c​ ​ ​ ​8​0​0​ ​m​i​l​l​i​s​e​c​o​n​d​ ​t​i​m​e​r​
 ​ ​0​ ​ ​1​7​3​7​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​t​i​c​k​-​8​0​0​m​s​e​c​ ​ ​ ​8​0​0​ ​m​i​l​l​i​s​e​c​o​n​d​ ​t​i​m​e​r​
 ​ ​0​ ​ ​1​7​3​7​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​t​i​c​k​-​8​0​0​m​s​e​c​ ​ ​ ​8​0​0​ ​m​i​l​l​i​s​e​c​o​n​d​ ​t​i​m​e​r​
 ​ ​0​ ​ ​1​7​3​7​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​t​i​c​k​-​8​0​0​m​s​e​c​ ​ ​ ​8​0​0​ ​m​i​l​l​i​s​e​c​o​n​d​ ​t​i​m​e​r​
 ​ ​0​ ​ ​1​7​3​6​8​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​t​i​c​k​-​5​s​e​c​ ​ ​ ​f​i​v​e​ ​s​e​c​o​n​d​ ​t​i​m​e​r​
 ​ ​0​ ​ ​1​7​3​7​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​t​i​c​k​-​8​0​0​m​s​e​c​ ​ ​ ​8​0​0​ ​m​i​l​l​i​s​e​c​o​n​d​ ​t​i​m​e​r​
 ​ ​0​ ​ ​1​7​3​7​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​:​t​i​c​k​-​8​0​0​m​s​e​c​ ​ ​ ​8​0​0​ ​m​i​l​l​i​s​e​c​o​n​d​ ​t​i​m​e​r​
^​C​

Valid suffixes are nsec, usec, msec, for nano-, micro-, and milli- seconds. (ns, us, and ms are valid abbreviations.) sec, min, hour, and day are for longer values. (s, m, h, d are valid abbreviations.) There is also hz for hertz, frequency per second.

proc provider

The proc provider gives you process-level information, such as program launches and exits. Example 9.5 shows you when new processes have been created:

Example 9.5. execs.d

p​r​o​c​:​:​:​e​x​e​c​-​s​u​c​c​e​s​s​
{​
 ​ ​ ​ ​t​r​a​c​e​(​e​x​e​c​n​a​m​e​)​;​
}​

A sample run looks like this:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​s​ ​e​x​e​c​s​.​d​
d​t​r​a​c​e​:​ ​s​c​r​i​p​t​ ​'​e​x​e​c​s​.​d​'​ ​m​a​t​c​h​e​d​ ​2​ ​p​r​o​b​e​s​
C​P​U​ ​ ​ ​ ​ ​I​D​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​F​U​N​C​T​I​O​N​:​N​A​M​E​
 ​ ​0​ ​ ​1​8​4​8​8​ ​ ​ ​ ​ ​ ​ ​ ​_​_​m​a​c​_​e​x​e​c​v​e​:​e​x​e​c​-​s​u​c​c​e​s​s​ ​ ​ ​l​s​
 ​ ​0​ ​ ​1​8​4​8​8​ ​ ​ ​ ​ ​ ​ ​ ​_​_​m​a​c​_​e​x​e​c​v​e​:​e​x​e​c​-​s​u​c​c​e​s​s​ ​ ​ ​p​w​d​
 ​ ​0​ ​ ​1​8​4​9​5​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​o​s​i​x​_​s​p​a​w​n​:​e​x​e​c​-​s​u​c​c​e​s​s​ ​ ​ ​i​C​h​a​t​
 ​ ​0​ ​ ​1​8​4​9​5​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​o​s​i​x​_​s​p​a​w​n​:​e​x​e​c​-​s​u​c​c​e​s​s​ ​ ​ ​i​C​h​a​t​A​g​e​n​t​
^​C​

ls and pwd were performed in a Terminal window while iChat was launched in the Dock.

fbt provider

Another provider is the unpronounceable fbt provider, which stands for function boundary transition. These are probes inside of the kernel. An fbt probe is triggered when a function is entered or exited. Use the probe name entry or return to run an action when a function is entered or exited. On this Snow Leopard system, there are 18 thousand fbt probes:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​l​ ​-​i​ ​f​b​t​:​:​:​ ​|​ ​w​c​ ​-​l​
 ​ ​ ​ ​1​8​5​3​1​

You can look at some of the available probes:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​l​ ​-​i​ ​f​b​t​:​:​:​ ​|​ ​h​e​a​d​ ​-​8​
 ​ ​ ​I​D​ ​ ​ ​P​R​O​V​I​D​E​R​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​M​O​D​U​L​E​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​F​U​N​C​T​I​O​N​ ​N​A​M​E​
 ​ ​ ​4​1​ ​ ​ ​ ​ ​ ​ ​ ​f​b​t​ ​ ​ ​ ​ ​ ​ ​m​a​c​h​_​k​e​r​n​e​l​ ​ ​ ​ ​ ​ ​A​l​l​o​c​a​t​e​N​o​d​e​ ​e​n​t​r​y​
 ​ ​ ​4​2​ ​ ​ ​ ​ ​ ​ ​ ​f​b​t​ ​ ​ ​ ​ ​ ​ ​m​a​c​h​_​k​e​r​n​e​l​ ​ ​ ​ ​ ​ ​A​l​l​o​c​a​t​e​N​o​d​e​ ​r​e​t​u​r​n​
 ​ ​ ​4​3​ ​ ​ ​ ​ ​ ​ ​ ​f​b​t​ ​ ​ ​ ​ ​ ​ ​m​a​c​h​_​k​e​r​n​e​l​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​A​s​s​e​r​t​ ​e​n​t​r​y​
 ​ ​ ​4​4​ ​ ​ ​ ​ ​ ​ ​ ​f​b​t​ ​ ​ ​ ​ ​ ​ ​m​a​c​h​_​k​e​r​n​e​l​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​A​s​s​e​r​t​ ​r​e​t​u​r​n​
 ​ ​ ​4​5​ ​ ​ ​ ​ ​ ​ ​ ​f​b​t​ ​ ​ ​ ​ ​ ​ ​m​a​c​h​_​k​e​r​n​e​l​ ​ ​ ​ ​ ​ ​ ​ ​B​F​_​d​e​c​r​y​p​t​ ​e​n​t​r​y​
 ​ ​ ​4​6​ ​ ​ ​ ​ ​ ​ ​ ​f​b​t​ ​ ​ ​ ​ ​ ​ ​m​a​c​h​_​k​e​r​n​e​l​ ​ ​ ​ ​ ​ ​ ​ ​B​F​_​d​e​c​r​y​p​t​ ​r​e​t​u​r​n​
 ​ ​ ​4​7​ ​ ​ ​ ​ ​ ​ ​ ​f​b​t​ ​ ​ ​ ​ ​ ​ ​m​a​c​h​_​k​e​r​n​e​l​ ​ ​ ​ ​ ​ ​ ​ ​B​F​_​e​n​c​r​y​p​t​ ​e​n​t​r​y​

Actions

Actions are the lines of code you write that execute whenever a probe is fired. You can assign values to variables, perform computations, and aggregate values over time.

D has no flow control, so there are no if statements and no loops. You can simulate if statements by using predicates, discussed below. The ternary operator (?:) is available, too.

The lack of loops is understandable. DTrace scripts, after they are compiled and verified, are run inside of the kernel. It would be a bad thing to have an unintentional infinite loop occur, locking up the system.

Variables

What is a programming language without variables? D has a number of kinds of variables. Simple (scalar) variables are familiar to those of us used to C, along with structures, associative arrays, aggregates, and even pointers.

Simple variables match C’s standard types: char, int, short, long, long long, and all of their unsigned variants. You can perform the usual C-style casting between them. The size of these depend on the OS kernel’s data model: a long on a 32 bit kernel would be 32 bits, and 64 bits on a 64 bit kernel. There are no floating point variables. You can display floating point values you get from probes, but you cannot perform floating point math. You cannot cast from a float to an integer value.

You don’t need to declare a variable before you use it, just assign it, and the variable will use the type of the first assignment. You cannot declare a variable and assign it a value at the same time.

Variables are global by default. Typically you will declare the variable, if you choose to, outside of any clauses and then assign it an initial value inside of a BEGIN clause. Variables are zeroed initially.

The standard C operators (+, -, *, /, ++, etc) all work as expected, as do comparison operators (<, >, !=, etc). && and || are available in logical statements, as is ^^, which is a logical XOR. XOR evaluates to true if exactly one operand is true. Logical OR and AND perform short-circuit evaluation, but XOR does not. Both expressions for an XOR are always evaluated.

Variables can have string values as well. Comparison operators with strings behave as if you had called strcmp() on the strings.

Example 9.6 sings a familiar song:

Example 9.6. beer.d

i​n​t​ ​b​o​t​t​l​e​s​;​ ​/​*​ ​o​p​t​i​o​n​a​l​ ​*​/​

B​E​G​I​N​
{​
 ​ ​b​o​t​t​l​e​s​ ​=​ ​9​9​;​
}​

p​r​o​f​i​l​e​:​:​:​t​i​c​k​-​1​s​e​c​
{​
 ​ ​p​r​i​n​t​f​(​"​%​d​ ​b​o​t​t​l​e​s​ ​o​f​ ​b​e​e​r​ ​o​n​ ​t​h​e​ ​w​a​l​l​\​n​"​,​ ​b​o​t​t​l​e​s​)​;​
 ​ ​p​r​i​n​t​f​(​"​%​d​ ​b​o​t​t​l​e​s​ ​o​f​ ​b​e​e​r​.​\​n​"​,​ ​b​o​t​t​l​e​s​)​;​
 ​ ​p​r​i​n​t​f​(​"​t​a​k​e​ ​o​n​e​ ​d​o​w​n​,​ ​p​a​s​s​ ​i​t​ ​a​r​o​u​n​d​\​n​"​)​;​
 ​ ​p​r​i​n​t​f​(​"​%​d​ ​b​o​t​t​l​e​s​ ​o​f​ ​b​e​e​r​ ​o​n​ ​t​h​e​ ​w​a​l​l​\​n​\​n​"​,​ ​b​o​t​t​l​e​s​)​;​

 ​ ​b​o​t​t​l​e​s​-​-​;​
}​

E​N​D​
{​
 ​ ​p​r​i​n​t​f​(​"​t​h​a​t​'​s​ ​a​l​l​,​ ​f​o​l​k​s​.​.​.​"​)​;​
}​

Run it like usual and interrupt it when you have had enough.

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​q​s​ ​b​e​e​r​.​d​
9​9​ ​b​o​t​t​l​e​s​ ​o​f​ ​b​e​e​r​ ​o​n​ ​t​h​e​ ​w​a​l​l​
9​9​ ​b​o​t​t​l​e​s​ ​o​f​ ​b​e​e​r​.​
t​a​k​e​ ​o​n​e​ ​d​o​w​n​,​ ​p​a​s​s​ ​i​t​ ​a​r​o​u​n​d​
9​9​ ​b​o​t​t​l​e​s​ ​o​f​ ​b​e​e​r​ ​o​n​ ​t​h​e​ ​w​a​l​l​

9​8​ ​b​o​t​t​l​e​s​ ​o​f​ ​b​e​e​r​ ​o​n​ ​t​h​e​ ​w​a​l​l​
9​8​ ​b​o​t​t​l​e​s​ ​o​f​ ​b​e​e​r​.​
t​a​k​e​ ​o​n​e​ ​d​o​w​n​,​ ​p​a​s​s​ ​i​t​ ​a​r​o​u​n​d​
9​8​ ​b​o​t​t​l​e​s​ ​o​f​ ​b​e​e​r​ ​o​n​ ​t​h​e​ ​w​a​l​l​
^​C​
t​h​a​t​'​s​ ​a​l​l​,​ ​f​o​l​k​s​.​.​.​

Scoped variables

There are two additional scopes for variables: per-thread, and per-clause. Thread-local variables are useful when you want to enable a probe and perform some work with individual thread. You prefix a thread-local variable with self->. A thread’s identity is unique over the lifetime of a system, so you won’t get any weird collisions that might happen if a kernel thread ID gets reused. A per-clause variable is like a static local variable in C. It is only in scope when a particular clause is being executed. Prefix clause-local variables with this->.

Built-in variables

D provides a number of built-in scalar variables. There are no built-in thread-local or clause-local variables.

int64_t arg0, arg1, ... arg9;

The first ten input arguments to a probe, as 64-bit integers. Undefined arguments are zero-filled.

args[]

Typed arguments to the current probe, using an integer index. For instance, the read system call is prototyped like

ssize_t read(int fildes, void *buf, size_t nbyte);

So args[0] will be an integer, the file descriptor; args[1] will be a pointer value, and args[2] is typed size_t, the size of the buffer.

cwd

Current working directory of the process that owns the thread that is triggering the probe.

errno

Error value returned by the last system call executed by the thread.

execname

Name of the executable that triggered the probe.

pid

Process id of the executable that triggered the probe.

stackdepth

Current thread’s stackframe depth.

timestamp, vtimestamp

Current values of a nanosecond timestamp. The absolute value have no real meaning, but you use them to calculate delta values. timestamp is wall-clock time, and vtimestamp is per-CPU time, minus DTtrace overhead.

probeprov, probemod, probefunc, probename

Names of the four parts of the probe specification.

These last four are handy for knowing which particular probe is firing, especially if you have one probe specification that matches multiple probes. Example 9.7 traces all system calls made across the system.

Example 9.7. syscalls.d

s​y​s​c​a​l​l​:​:​:​
/​e​x​e​c​n​a​m​e​ ​!​=​ ​"​d​t​r​a​c​e​"​/​
{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​s​ ​f​i​r​e​d​ ​i​n​ ​%​s​\​n​"​,​ ​p​r​o​b​e​f​u​n​c​,​ ​e​x​e​c​n​a​m​e​)​;​
}​

This produces a lot of output:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​q​s​ ​s​y​s​c​a​l​l​s​.​d​
s​i​g​p​r​o​c​m​a​s​k​ ​f​i​r​e​d​ ​i​n​ ​W​i​n​d​o​w​S​e​r​v​e​r​
_​_​s​e​m​w​a​i​t​_​s​i​g​n​a​l​ ​f​i​r​e​d​ ​i​n​ ​f​s​e​v​e​n​t​s​d​
l​s​t​a​t​ ​f​i​r​e​d​ ​i​n​ ​f​s​e​v​e​n​t​s​d​
g​e​t​a​t​t​r​l​i​s​t​ ​f​i​r​e​d​ ​i​n​ ​b​a​c​k​u​p​d​
_​_​s​e​m​w​a​i​t​_​s​i​g​n​a​l​ ​f​i​r​e​d​ ​i​n​ ​m​d​s​
g​e​t​t​i​m​e​o​f​d​a​y​ ​f​i​r​e​d​ ​i​n​ ​S​n​a​k​
g​e​t​t​i​m​e​o​f​d​a​y​ ​f​i​r​e​d​ ​i​n​ ​S​n​a​k​
g​e​t​t​i​m​e​o​f​d​a​y​ ​f​i​r​e​d​ ​i​n​ ​A​d​i​u​m​
g​e​t​t​i​m​e​o​f​d​a​y​ ​f​i​r​e​d​ ​i​n​ ​A​d​i​u​m​
k​e​v​e​n​t​ ​f​i​r​e​d​ ​i​n​ ​D​i​r​e​c​t​o​r​y​S​e​r​v​i​c​
g​e​t​r​u​s​a​g​e​ ​f​i​r​e​d​ ​i​n​ ​S​k​y​p​e​
g​e​t​r​u​s​a​g​e​ ​f​i​r​e​d​ ​i​n​ ​S​k​y​p​e​
^​C​

The /execname != "dtrace"/ predicate is there to filter out system calls from dtrace, of which there are many. The clause will only be run if the name of the executable that triggered the probe was not dtrace.

You can access kernel and application data structures from your D script. You can reference a symbol external to DTrace, such as a kernel data structure, by prefixing the name with the backtick: `. Example 9.8 displays the system load average every five seconds. Remember that there are no floating point calculations available, so the integer and decimal components of the load average, stored in the avrunnable structure, must be calculated independently.

Example 9.8. loadaverage.d

p​r​o​f​i​l​e​:​:​:​t​i​c​k​-​5​s​e​c​
{​
 ​ ​ ​ ​ ​t​h​i​s​-​>​f​s​c​a​l​e​ ​=​ ​`​a​v​e​r​u​n​n​a​b​l​e​.​f​s​c​a​l​e​;​
 ​ ​ ​ ​ ​t​h​i​s​-​>​l​o​a​d​I​n​t​e​g​e​r​ ​=​ ​`​a​v​e​r​u​n​n​a​b​l​e​.​l​d​a​v​g​[​0​]​ ​/​ ​t​h​i​s​-​>​f​s​c​a​l​e​;​
 ​ ​ ​ ​ ​t​h​i​s​-​>​l​o​a​d​D​e​c​i​m​a​l​ ​=​ ​(​(​`​a​v​e​r​u​n​n​a​b​l​e​.​l​d​a​v​g​[​0​]​ ​%​ ​t​h​i​s​-​>​f​s​c​a​l​e​)​ ​*​ ​1​0​0​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​ ​t​h​i​s​-​>​f​s​c​a​l​e​;​
 ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​d​.​%​d​\​n​"​,​ ​t​h​i​s​-​>​l​o​a​d​I​n​t​e​g​e​r​,​ ​t​h​i​s​-​>​l​o​a​d​D​e​c​i​m​a​l​)​;​
}​

A sample run:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​q​s​ ​l​o​a​d​a​v​e​r​a​g​e​.​d​
1​.​4​8​
1​.​3​6​
1​.​2​5​
1​.​1​5​
1​.​6​
0​.​9​7​
0​.​8​9​

The machine had been used to perform an end-to-end build of a product which raised the load average. Here you can see the system “cooling off.”

Functions

D provides a number of functions you can use in your action scripts. Here is a sampling.

printf | Standard C-style printf function
trace | Displays its argument, using a default format based on the type. Also appends a newline for you.
printa | Print aggregate values, described in more detail below.
ustack | Print the user level stack trace. If the process exits or is killed before the DTrace experiment has completed, DTrace might not be able to resolve the program counter, so you will get hex values rather than symbolic values.
exit | Terminate execution early. Otherwise, you have to type Control-C to quit.

copyin, copyinstr

DTrace scripts execute in kernel scope. Consider probing a system call like write(), which takes a pointer to a buffer with the data to be written. There is a problem if you want to use the contents of the buffer in your action. The pointer is to an address in user-space, and DTrace is running in kernel-space. Use one of these functions to get the data from user-space into kernel-space.

void *copyin (uintptr_t addr, size_t size)

Copies the specified size in bytes from the given user-space address into a DTrace scratch buffer and returns the address of the buffer. If there is insufficient scratch space available, or the given address is bad, an error is generated.

string copyinstr (uintptr_t addr)

Copies a zero-terminated C string from user-space into a scratch buffer and returns the buffer’s address. You can use the returned address as a string.

Arrays

D provides associative arrays that are similar to hashtables and dictionaries in other languages. The index into the array can be a n-tuple of values, separated by commas. arrayname[pid, probefunc, "bork"] = 23; is a valid assignment. The types of the array index must match for every assignment, and the assigned values must be uniform. You cannot nest arrays.

Example 9.9 calculates the wall-clock time it takes to execute the read() system call:

Example 9.9. readtime.d

s​y​s​c​a​l​l​:​:​r​e​a​d​:​e​n​t​r​y​
{​
 ​ ​ ​ ​s​e​l​f​-​>​t​s​[​p​i​d​,​ ​p​r​o​b​e​f​u​n​c​]​ ​=​ ​t​i​m​e​s​t​a​m​p​;​
}​

s​y​s​c​a​l​l​:​:​r​e​a​d​:​r​e​t​u​r​n​
/​s​e​l​f​-​>​t​s​[​p​i​d​,​ ​p​r​o​b​e​f​u​n​c​]​ ​!​=​ ​0​/​
{​
 ​ ​ ​ ​d​e​l​t​a​ ​=​ ​t​i​m​e​s​t​a​m​p​ ​-​ ​s​e​l​f​-​>​t​s​[​p​i​d​,​ ​p​r​o​b​e​f​u​n​c​]​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​r​e​a​d​ ​i​n​ ​%​s​ ​t​o​o​k​ ​%​d​ ​n​s​e​c​s​\​n​"​,​ ​e​x​e​c​n​a​m​e​,​ ​d​e​l​t​a​)​;​
}​

And a sample run:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​q​s​ ​.​/​r​e​a​d​t​i​m​e​.​d​
r​e​a​d​ ​i​n​ ​T​e​r​m​i​n​a​l​ ​t​o​o​k​ ​1​8​4​6​9​ ​n​s​e​c​s​
r​e​a​d​ ​i​n​ ​e​m​a​c​s​-​i​3​8​6​ ​t​o​o​k​ ​1​4​7​1​4​ ​n​s​e​c​s​
r​e​a​d​ ​i​n​ ​T​e​r​m​i​n​a​l​ ​t​o​o​k​ ​1​1​3​9​1​ ​n​s​e​c​s​
r​e​a​d​ ​i​n​ ​T​e​r​m​i​n​a​l​ ​t​o​o​k​ ​2​6​7​7​7​ ​n​s​e​c​s​
r​e​a​d​ ​i​n​ ​e​m​a​c​s​-​i​3​8​6​ ​t​o​o​k​ ​2​3​7​7​6​7​9​7​ ​n​s​e​c​s​
^​C​

The predicate is there to prevent the script from starting in the middle of a read and getting bad values in the timestamp calculation. If there was no corresponding start time, the time calculation code is skipped. The array is stored as a thead-local variable with self-> so that an application that is using read() in multiple threads won’t get confused.

C arrays

Interestingly enough, D supports C-style arrays – address plus offset. D can access kernel and application data structures, which include pointers and arrays. You can dereference pointers and access arrays with the usual C syntax. D makes sure that all array and pointer access is safe, so you do not have to worry about triggering a SEGV in kernel-land. Your script may be terminated, and the dtrace:::ERROR probe triggered.

Predicates

Predicates are logical expressions that are enclosed by slashes. The action code is run if the expression evaluates to a true value when a probe fires. You can have the same probe used with different predicates and different action blocks. Clauses are executed in the order they appear in the source file.

Aggregates

Aggregates are DTrace’s way of accumulating a lot of individual pieces of data into a more useful overall view. Seeing individual read() times can be useful, if a bit voluminous. Having a min / max / average of read times may be much more interesting.

The syntax for using a DTrace aggregate looks a bit like using array.

@​n​a​m​e​[​k​e​y​]​ ​=​ ​a​g​g​f​u​n​c​(​)​;​

The leading at-sign means that an aggregate is being used, and the assignment is a function rather than an expression. There are a number of aggregating functions:

count()

Keeps count of the number of times it is called.

sum(expression)

Accumulates the total value of the expression over time.

avg(expression)

Accumulates the arithmetic average of the expression over time.

min(expression)

Keeps the smallest expression that is seen over time.

max(expression)

Keeps the largest expression that is seen over time.

quantize(expression)

Keeps a power-of-two frequency distribution of the values of the given histogram. The value of the expression increments the value in a histogram bucket. When the program exits, dtrace prints an ASCII chart showing the bucket values.

lquantize(expression, lower-bound, upper-bound, step-value)

Similar to quantize, but uses a linear frequency distribution rather than a power-of-two distribution.

OK, so what does all that mean? Here are some examples. Example 9.10 counts the number of times the read() system call is made by any process.

Example 9.10. countread.d

s​y​s​c​a​l​l​:​:​r​e​a​d​:​e​n​t​r​y​
{​
 ​ ​ ​ ​@​c​a​l​l​s​[​e​x​e​c​n​a​m​e​]​ ​=​ ​c​o​u​n​t​(​)​;​
}​

A sample run:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​s​ ​.​/​c​o​u​n​t​r​e​a​d​.​d​
d​t​r​a​c​e​:​ ​s​c​r​i​p​t​ ​'​.​/​c​o​u​n​t​r​e​a​d​.​d​'​ ​m​a​t​c​h​e​d​ ​1​ ​p​r​o​b​e​
^​C​
 ​ ​V​o​o​d​o​o​P​a​d​ ​P​r​o​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​
 ​ ​s​o​c​k​e​t​f​i​l​t​e​r​f​w​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​
 ​ ​m​d​s​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​2​
 ​ ​D​i​r​e​c​t​o​r​y​S​e​r​v​i​c​ ​ ​ ​ ​ ​ ​ ​2​1​
 ​ ​f​s​e​v​e​n​t​s​d​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​2​2​
 ​ ​S​a​f​a​r​i​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​6​2​
 ​ ​T​e​r​m​i​n​a​l​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​8​6​
 ​ ​e​m​a​c​s​-​i​3​8​6​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​4​1​

dtrace helpfully displays aggregates for you when it exits. Unfortunately, this can make a mess if you have a couple of aggregates going. Example 9.11 counts the number of read calls and maintains the average calltime:

Example 9.11. avgread-1.d

s​y​s​c​a​l​l​:​:​r​e​a​d​:​e​n​t​r​y​
{​
 ​ ​ ​ ​@​c​a​l​l​s​[​e​x​e​c​n​a​m​e​]​ ​=​ ​c​o​u​n​t​(​)​;​
 ​ ​ ​ ​s​e​l​f​-​>​t​s​ ​=​ ​t​i​m​e​s​t​a​m​p​;​
}​

s​y​s​c​a​l​l​:​:​r​e​a​d​:​r​e​t​u​r​n​
/​s​e​l​f​-​>​t​s​/​
{​
 ​ ​ ​ ​d​e​l​t​a​ ​=​ ​t​i​m​e​s​t​a​m​p​ ​-​ ​s​e​l​f​-​>​t​s​;​
 ​ ​ ​ ​@​a​v​e​r​a​g​e​t​i​m​e​[​e​x​e​c​n​a​m​e​]​ ​=​ ​a​v​g​(​d​e​l​t​a​)​;​
}​

Some output from this script:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​q​s​ ​a​v​g​r​e​a​d​-​1​.​d​
^​C​
 ​ ​V​o​o​d​o​o​P​a​d​ ​P​r​o​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​
 ​ ​c​s​h​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​
 ​ ​s​e​c​u​r​i​t​y​d​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​2​
 ​ ​o​p​e​n​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​0​
 ​ ​P​r​e​v​i​e​w​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​4​0​
 ​ ​e​m​a​c​s​-​i​3​8​6​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​5​5​
 ​ ​T​e​r​m​i​n​a​l​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​2​2​
 ​ ​T​e​r​m​i​n​a​l​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​7​9​9​8​
 ​ ​e​m​a​c​s​-​i​3​8​6​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​0​0​6​2​
 ​ ​V​o​o​d​o​o​P​a​d​ ​P​r​o​ ​ ​ ​ ​ ​ ​1​0​2​3​2​
 ​ ​o​p​e​n​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​5​5​1​2​
 ​ ​s​e​c​u​r​i​t​y​d​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​7​8​0​7​
 ​ ​P​r​e​v​i​e​w​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​2​8​2​8​0​

There are counts and average times in there, but it is hard to tell where the counts stop and the average times begin. Example 9.12 controls the output by creating an END probe and using printa() to print out the aggregate.

Example 9.12. avgread-2.d

s​y​s​c​a​l​l​:​:​r​e​a​d​:​e​n​t​r​y​
{​
 ​ ​ ​ ​s​e​l​f​-​>​t​s​ ​=​ ​t​i​m​e​s​t​a​m​p​;​
}​

s​y​s​c​a​l​l​:​:​r​e​a​d​:​r​e​t​u​r​n​
/​s​e​l​f​-​>​t​s​/​
{​
 ​ ​ ​ ​d​e​l​t​a​ ​=​ ​t​i​m​e​s​t​a​m​p​ ​-​ ​s​e​l​f​-​>​t​s​;​
 ​ ​ ​ ​@​a​v​e​r​a​g​e​t​i​m​e​[​e​x​e​c​n​a​m​e​]​ ​=​ ​a​v​g​(​d​e​l​t​a​)​;​
 ​ ​ ​ ​@​c​a​l​l​c​o​u​n​t​[​e​x​e​c​n​a​m​e​]​ ​=​ ​c​o​u​n​t​(​)​;​
 ​ ​ ​ ​@​m​i​n​t​i​m​e​[​e​x​e​c​n​a​m​e​]​ ​=​ ​m​i​n​(​d​e​l​t​a​)​;​
 ​ ​ ​ ​@​m​a​x​t​i​m​e​[​e​x​e​c​n​a​m​e​]​ ​=​ ​m​a​x​(​d​e​l​t​a​)​;​
 ​ ​ ​ ​s​e​l​f​-​>​t​s​ ​=​ ​0​;​
}​

E​N​D​
{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​a​v​e​r​a​g​e​ ​t​i​m​e​\​n​"​)​;​
 ​ ​ ​ ​p​r​i​n​t​a​ ​(​"​%​2​0​s​ ​%​@​d​\​n​"​,​ ​@​a​v​e​r​a​g​e​t​i​m​e​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​n​c​a​l​l​ ​c​o​u​n​t​\​n​"​)​;​
 ​ ​ ​ ​p​r​i​n​t​a​ ​(​"​%​2​0​s​ ​%​@​d​\​n​"​,​ ​@​c​a​l​l​c​o​u​n​t​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​n​m​i​n​t​i​m​e​\​n​"​)​;​
 ​ ​ ​ ​p​r​i​n​t​a​ ​(​"​%​2​0​s​ ​%​@​d​\​n​"​,​ ​@​m​i​n​t​i​m​e​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​n​m​a​x​t​i​m​e​\​n​"​)​;​
 ​ ​ ​ ​p​r​i​n​t​a​ ​(​"​%​2​0​s​ ​%​@​d​\​n​"​,​ ​@​m​a​x​t​i​m​e​)​;​
}​

The script in action:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​q​s​ ​a​v​g​r​e​a​d​-​2​.​d​
^​C​
a​v​e​r​a​g​e​ ​t​i​m​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​T​e​r​m​i​n​a​l​ ​7​9​4​1​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​m​a​c​s​-​i​3​8​6​ ​9​9​8​5​
 ​ ​ ​ ​ ​ ​ ​m​D​N​S​R​e​s​p​o​n​d​e​r​ ​1​7​7​8​1​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​a​f​a​r​i​ ​2​4​6​6​6​
 ​ ​ ​ ​ ​ ​ ​V​o​o​d​o​o​P​a​d​ ​P​r​o​ ​5​5​3​3​9​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​s​e​v​e​n​t​s​d​ ​5​2​7​8​6​3​9​7​9​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​m​d​s​ ​5​5​1​1​6​4​9​3​9​

c​a​l​l​ ​c​o​u​n​t​
 ​ ​ ​ ​ ​ ​ ​m​D​N​S​R​e​s​p​o​n​d​e​r​ ​2​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​m​a​c​s​-​i​3​8​6​ ​3​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​T​e​r​m​i​n​a​l​ ​4​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​m​d​s​ ​6​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​s​e​v​e​n​t​s​d​ ​9​
 ​ ​ ​ ​ ​ ​ ​V​o​o​d​o​o​P​a​d​ ​P​r​o​ ​2​7​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​a​f​a​r​i​ ​6​2​2​
.​.​.​

Charts are always fun. Example 9.13 aggregates read times, but this time, the times are represented as a histogram on process exit.

Example 9.13. avgquant.d

s​y​s​c​a​l​l​:​:​r​e​a​d​:​e​n​t​r​y​
{​
 ​ ​ ​ ​s​e​l​f​-​>​t​s​ ​=​ ​t​i​m​e​s​t​a​m​p​;​
}​

s​y​s​c​a​l​l​:​:​r​e​a​d​:​r​e​t​u​r​n​
/​s​e​l​f​-​>​t​s​/​
{​
 ​ ​ ​ ​d​e​l​t​a​ ​=​ ​t​i​m​e​s​t​a​m​p​ ​-​ ​s​e​l​f​-​>​t​s​;​
 ​ ​ ​ ​@​q​u​a​n​t​t​i​m​e​[​e​x​e​c​n​a​m​e​]​ ​=​ ​q​u​a​n​t​i​z​e​(​d​e​l​t​a​)​;​
}​

This is what a sample run looks like:

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​q​s​ ​a​v​g​q​u​a​n​t​.​d​
P​a​s​s​w​o​r​d​:​
^​C​

 ​ ​s​e​c​u​r​i​t​y​d​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​v​a​l​u​e​ ​ ​-​-​-​-​-​-​-​-​ ​D​i​s​t​r​i​b​u​t​i​o​n​ ​-​-​-​-​-​-​-​-​ ​c​o​u​n​t​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​4​0​9​6​ ​|​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​8​1​9​2​ ​|​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​6​3​8​4​ ​|​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​3​2​7​6​8​ ​|​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​

 ​ ​o​p​e​n​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​v​a​l​u​e​ ​ ​-​-​-​-​-​-​-​-​ ​D​i​s​t​r​i​b​u​t​i​o​n​ ​-​-​-​-​-​-​-​-​ ​c​o​u​n​t​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​2​0​4​8​ ​|​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​4​0​9​6​ ​|​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​4​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​8​1​9​2​ ​|​@​@​@​@​@​@​@​@​@​@​@​@​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​3​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​6​3​8​4​ ​|​@​@​@​@​@​@​@​@​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​2​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​3​2​7​6​8​ ​|​@​@​@​@​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​6​5​5​3​6​ ​|​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​

 ​ ​P​r​e​v​i​e​w​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​v​a​l​u​e​ ​ ​-​-​-​-​-​-​-​-​ ​D​i​s​t​r​i​b​u​t​i​o​n​ ​-​-​-​-​-​-​-​-​ ​c​o​u​n​t​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​2​0​4​8​ ​|​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​4​0​9​6​ ​|​@​@​@​@​@​@​@​@​@​@​@​@​@​@​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​9​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​8​1​9​2​ ​|​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​@​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​3​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​6​3​8​4​ ​|​@​@​@​@​@​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​3​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​3​2​7​6​8​ ​|​@​@​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​ ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​6​5​5​3​6​ ​|​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​

Aggregate-related functions

There are several functions you can apply to an aggregate as a whole.

normalize, denormalize

Sometimes the values in an aggregate are not as useful as they could be. For example, you might have a sum of read times in an aggregate, but you may be more interested in read times over a period of time. The normalize function takes an aggregate and a scalar as arguments. The values of the aggregate are divided by the scalar value. The aggregate values are not permanently changed, so you can use denormalize to return to the original values.

clear | Removes all of the values but leaves the keys. Sometimes it may be interesting to know that a key has had a non-zero value in an aggregate.
trunc | Removes values as well as keys. trunc by itself will truncate the entire aggregate. trunc with a positive value will preserve the N largest values. trunc with a negative value will preserve the N smallest values.

Also, when a variable such as an array or an aggregate is assigned a zero value, DTrace will garbage collect the memory. When you are done with an array or an aggregate before the script ends, assign it to zero to conserve memory.

Random Leftovers

The C preprocessor

DTrace can run your script through the C preprocessor before the code is compiled. You can use all of the standard preprocessor features to make macros with common code and include C header files to include type definitions of library types. The D compiler automatically loads the set of C type descriptions for the kernel.

Pragmas

DTrace has a number of tunable parameters you can change via pragmas. Some useful ones:

#pragma D option quiet

same as passing -q on the command-line

#pragma D option flowindent

dtrace prints the name of the function if an action is empty. If you turn on this pragma, DTrace will indent function call names based on call depth, giving you a call trace.

Example 9.14 is a script that traces the flow of function calls in the kernel for the open() system call when invoked by the ls command-line tool:

Example 9.14. trace-ls.d

#​p​r​a​g​m​a​ ​D​ ​o​p​t​i​o​n​ ​f​l​o​w​i​n​d​e​n​t​

B​E​G​I​N​
{​
 ​ ​ ​ ​p​r​i​n​t​f​(​"​w​a​i​t​i​n​g​ ​f​o​r​ ​'​l​s​'​"​)​;​
}​

s​y​s​c​a​l​l​:​:​o​p​e​n​:​e​n​t​r​y​
/​e​x​e​c​n​a​m​e​ ​=​=​ ​"​l​s​"​ ​&​&​ ​g​u​a​r​d​+​+​ ​=​=​ ​0​/​
{​
 ​ ​ ​ ​s​e​l​f​-​>​t​r​a​c​e​I​t​ ​=​ ​1​;​
}​

f​b​t​:​:​:​
/​s​e​l​f​-​>​t​r​a​c​e​I​t​/​
{​
}​

s​y​s​c​a​l​l​:​:​:​r​e​t​u​r​n​
/​s​e​l​f​-​>​t​r​a​c​e​I​t​/​
{​
 ​ ​ ​ ​s​e​l​f​-​>​t​r​a​c​e​I​t​ ​=​ ​0​;​
 ​ ​ ​ ​e​x​i​t​ ​(​0​)​;​
}​

Below is a sample run, truncated for brevity. This script uses an “ambush” technique to wait for a program to start running. Run dtrace in one terminal and perform an ls in another one.

$​ ​s​u​d​o​ ​d​t​r​a​c​e​ ​-​s​ ​.​/​t​r​a​c​e​-​l​s​.​d​
d​t​r​a​c​e​:​ ​s​c​r​i​p​t​ ​'​.​/​t​r​a​c​e​-​l​s​.​d​'​ ​m​a​t​c​h​e​d​ ​1​6​8​3​4​ ​p​r​o​b​e​s​
C​P​U​ ​F​U​N​C​T​I​O​N​
 ​ ​1​ ​|​ ​:​B​E​G​I​N​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​a​i​t​i​n​g​ ​f​o​r​ ​'​l​s​'​

 ​ ​0​ ​ ​-​>​ ​o​p​e​n​
 ​ ​0​ ​ ​ ​ ​-​>​ ​_​_​p​t​h​r​e​a​d​_​t​e​s​t​c​a​n​c​e​l​
 ​ ​0​ ​ ​ ​ ​ ​ ​-​>​ ​o​p​e​n​_​n​o​c​a​n​c​e​l​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​-​>​ ​v​f​s​_​c​o​n​t​e​x​t​_​c​u​r​r​e​n​t​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​<​-​ ​v​f​s​_​c​o​n​t​e​x​t​_​c​u​r​r​e​n​t​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​-​>​ ​v​f​s​_​c​o​n​t​e​x​t​_​p​r​o​c​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​-​>​ ​g​e​t​_​b​s​d​t​h​r​e​a​d​t​a​s​k​_​i​n​f​o​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​-​ ​g​e​t​_​b​s​d​t​h​r​e​a​d​t​a​s​k​_​i​n​f​o​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​-​>​ ​f​a​l​l​o​c​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​-​>​ ​l​c​k​_​m​t​x​_​l​o​c​k​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​-​ ​l​c​k​_​m​t​x​_​l​o​c​k​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​-​>​ ​f​a​l​l​o​c​_​l​o​c​k​e​d​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​-​>​ ​f​d​a​l​l​o​c​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​-​ ​f​d​a​l​l​o​c​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​-​>​ ​p​r​o​c​_​u​c​r​e​d​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​-​ ​p​r​o​c​_​u​c​r​e​d​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​-​>​ ​m​a​c​_​f​i​l​e​_​c​h​e​c​k​_​c​r​e​a​t​e​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​-​>​ ​m​a​c​_​p​o​l​i​c​y​_​l​i​s​t​_​c​o​n​d​i​t​i​o​n​a​l​_​b​u​s​y​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​-​ ​m​a​c​_​p​o​l​i​c​y​_​l​i​s​t​_​c​o​n​d​i​t​i​o​n​a​l​_​b​u​s​y​
 ​ ​0​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​-​ ​m​a​c​_​f​i​l​e​_​c​h​e​c​k​_​c​r​e​a​t​e​
.​.​.​

Here is a dissection of the clauses of the script:

#​p​r​a​g​m​a​ ​D​ ​o​p​t​i​o​n​ ​f​l​o​w​i​n​d​e​n​t​

Indent the call graph

B​E​G​I​N​
{​
 ​ ​ ​ ​p​r​i​n​t​f​(​"​w​a​i​t​i​n​g​ ​f​o​r​ ​'​l​s​'​"​)​;​
}​

s​y​s​c​a​l​l​:​:​o​p​e​n​:​e​n​t​r​y​
/​e​x​e​c​n​a​m​e​ ​=​=​ ​"​l​s​"​ ​&​&​ ​g​u​a​r​d​+​+​ ​=​=​ ​0​/​
{​
 ​ ​ ​ ​s​e​l​f​-​>​t​r​a​c​e​I​t​ ​=​ ​1​;​
}​

This clause is interested in the open() system call. When an open() call is made in a process called “ls,” set the traceIt flag. The predicate makes us wait until we see ls before doing anything. The guard variable is to make sure we only trace one particular open call. This will protect the script from getting confused if there are multiple ls processes running, or if it uses open() in multiple threads.

f​b​t​:​:​:​
/​s​e​l​f​-​>​t​r​a​c​e​I​t​/​
{​
}​

This action is invoked any time a kernel function is entered or exited. The kernel does a lot of stuff, and we are only interested in these function call transitions if the script is actively tracing. Because of the predicate of the previous action, we will only have one thread in one process tracing through the execution of open()..

s​y​s​c​a​l​l​:​:​:​r​e​t​u​r​n​
/​s​e​l​f​-​>​t​r​a​c​e​I​t​/​
{​
 ​ ​ ​ ​s​e​l​f​-​>​t​r​a​c​e​I​t​ ​=​ ​0​;​
 ​ ​ ​ ​e​x​i​t​ ​(​0​)​;​
}​

This probe is triggered on any system call exit. For the great majority of system call exits in the system, we just not interested. If the traceme thread-local variable is set, then that means this is an interesting thread – the one that is tracing the open() system call of our ls process. If traceIt is a true value, execute the code. Clear out the traceIt flag, so that any subsequent system call exits in ls are triggered. Then finally terminate the script.

Objective-C

Apple has extended dtrace to trace Objective-C method invocations, using a special objc provider.

Some calls can be caught by the pid probe:

p​i​d​$​1​:​A​p​p​K​i​t​:​*​:​e​n​t​r​y​

traces the entry into any AppKit method.

p​i​d​$​1​:​A​p​p​K​i​t​:​*​N​S​C​o​n​t​r​o​l​*​:​e​n​t​r​y​

traces the entry into any NSControl method.

The objc probe is like the pid probe, which requires a process ID. You can specify the pid explicitly, or by using the $1 built-in variable to take the first argument for the script.

Trace an instance method:

o​b​j​c​$​1​:​N​S​C​o​n​t​r​o​l​:​-​c​e​l​l​:​r​e​t​u​r​n​

Trace an instance method that takes an argument:

o​b​j​c​$​1​:​N​S​C​o​n​t​r​o​l​:​-​s​e​t​E​n​a​b​l​e​d​?​:​r​e​t​u​r​n​

Both DTrace and Objective-C treat colons as important delimiters. Use the ?, match one character wildcard, to tell DTrace to match the Objective-C colons.

Trace an instance method that takes more than one argument:

o​b​j​c​$​1​:​N​S​V​i​e​w​:​-​c​o​n​v​e​r​t​R​e​c​t​?​t​o​V​i​e​w​?​:​e​n​t​r​y​

You can trace class messages by using a leading + on the method name.

Exercises

Make beer.d actually stop when it runs out of bottles.

The stack trace returned by ustack() can be used as an index in an array or an aggregate. See how many unique stack traces in an application of your choice end up calling malloc().

10
Performance Tuning

It has happened to all of us: you subject your program to real world data and discover that performance is sub-optimal, ranging from “could be faster” to “locks up instantly and CPU fans reach Mach 3.” Finding out what the performance problem is can be a difficult task. Many times we think we know where the problem is, but we turn out to be wrong. Luckily, there are a number of tools available to give definite metrics of where the program spends its time and what kind of pressure it puts on the OS in general.

The End of Free Performance

Over the last several decades, computer performance has been doubling about every 18 months, a figure attributable to Moore’s Law, which states that “The number of transistors in microchips will double about every 18 months.” For a long time, this doubling of transistors translated into doubling of performance and was helped along by increases in the clock frequency of the processors, which went from megahertz to gigahertz.

As software developers, we have had the luxury of writing sub-optimal code while improvements in computer hardware have masked inefficiencies. This has allowed us to tackle larger, more interesting problems without having to obsess over every cycle; and let us use simpler algorithms with more expensive orders of complexity.

The major chip manufacturers have hit a hard barrier in the race to crank up clock speeds due to problems with the speed of light and heat dissipation. Moore’s Law regarding those transistors is still in force, though. Manufacturers are still cramming more and more transistors into their processors, but these transistors are being used in other places. In particular, they are being used to increase parallelism.

CPUs have a great deal of internal parallelism, allowing multiple integer and floating point units to do calculations in parallel as well as vector units that operate on multiple pieces of data with a single instruction. In addition to separate processor units, CPUs these days are also multicore and hyperthreaded architectures. Multicore chips have several distinct CPUs on one physical chip. These processors operate in parallel to each other and have their own logic units. Hyperthread processors have two or more threads operating in parallel in a single CPU. Hyperthreads still share stuff like processor cache and math units, but it still allows the processor to chew through more instructions in a given time period. These can be combined, allowing an 8-core MacPro to appear to have 16 processors. Parallelism in GPUs, the graphical processing units found on graphics cards, is progressing even faster than with CPUs. It is not uncommon for a GPU to have 256 processors all running in parallel.

Parallel code is becoming more and more prevalent to take advantage of these multicore and hyperthreaded architectures. Parallel code usually is the domain of the more advanced programmers because of the difficulty in the writing correct concurrent programs. As you saw in Chapter 20: Multithreading, there are many issues that make parallel development difficult. More programmers will be needing to learn about this world and its problems so that we (as an industry) can continue developing software that behaves correctly and has the performance the users have come to expect. Tech such as Grand Central Dispatch helps alleviate some of the problems inherent in parallel programming, but it is still difficult to get right.

The design decisions occurring on the hardware side of the world have an impact on us living on the software side. Writing efficient code is coming back into style because we cannot count on processors “just getting faster” to help us.

The fact that users are dealing with ever-growing sets of data does not help the problem either. As computers get more powerful, users are using them to do more stuff to larger data sets. Consider iPhoto. People first used iPhoto to manage small libraries of relatively small photos. Now that we have terabyte hard drives, 16 core machines, and cheap high megapixel digital cameras, users are creating huge photo libraries of enormous images. Users will expect to be able to sling around ever larger sets of data as time goes on. They will not particularly care to hear any excuses about the gigahertz barrier.

Approaches To Performance

The key to keeping on top of your performance is to use profiling tools. Profile early, and profile often. Catch performance issues early in your development so you don’t build a lot of code around an inefficient core. If you use Instruments, Shark, or other performance tools regularly, you can see possible performance issues on the horizon before they come close and bite you.

Be sure to profile with each new revision of the OS and on new hardware as it comes out. As Apple changes Mac OS X under the hood, things that were optimal may now be suboptimal and vice-versa. Hardware changes can change the game performance-wise. Consider look-up tables, which are a common way to avoid doing calculation. On PowerPC G4 and older processors, using a look-up table was often a big win, but the G5 could do a lot of calculation in the time it took to load data from memory. Having situations like this can be a real problem if you have to support older versions of the OS or if you want to optimally target vastly different hardware.

Be careful to not totally contort your design early on in a noble quest for Optimal Performance. You might be addressing performance issues that do not have a real impact on your final product. Reports from profilers are not gospel. A report may highlight a performance problem, but the problem may be something that doesn’t need fixing. If a problem highlighted by a profiler will not affect the user experience of your program or if it is something that rarely happens, you can put your energies into optimizing something else.

Finally, do not just profile your development builds. If you use different compiler flags for deployment builds, especially with higher optimization levels, you will want to do some profiling on your final build so that you do not waste time fixing code paths that will change with compiler optimizations.

Major Causes of Performance Problems

Performance problems typically come from one or more of 5 major areas: algorithms, memory, CPU, disk, and graphics. Granted, that is pretty much everything your program interacts with in the machine. You can use performance tools to look at each aspect of computer performance in isolation to get a better handle on your overall performance issues, even if one problem is causing problems in several categories.

Memory

Even though modern machines have vast amounts of memory, RAM is still a scarce resource. Once your app or other apps on the system fill up memory, Mac OS X starts sending memory pages to disk, destroying performance. On iOS devices, your program may be killed outright in low-memory situations.

Typically if you optimize to reduce your memory usage (optimizing for space), you will often get reductions in execution time because the processor is not waiting for that extra data to arrive from memory. Also, because Mac OS X is a shared system with daemons running, with each user running lots of programs of their own, and potentially multiple users logged in, it is good to be conservative with your memory usage. This can be a tough discipline when each process has its own wide-open address space to play in, especially when using 64-bit addressing.

Locality of Reference

“Locality of reference” describes memory accesses that happen near each other. Reading a hundred bytes off one 4k page is faster than reading one byte off a hundred different pages scattered across the address space. When you ask for data from memory, the processor actually grabs a sequence of bytes, known as a cache line, under the assumption that you will be accessing memory in a contiguous manner. From the processor’s point of view, it is just as fast to grab a 64-byte cache line as it is to grab a 4-byte integer. So, if you set up your loops to operate on memory sequentially, you can see a performance boost.

Example 10.1 creates a large two dimensional global array and accesses it in two different ways.

Example 10.1. locality.m

/​/​ ​l​o​c​a​l​i​t​y​.​m​ ​-​-​ ​t​i​m​e​ ​l​o​c​a​l​i​t​y​ ​o​f​ ​r​e​f​e​r​e​n​c​e​

#​i​n​c​l​u​d​e​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​
#​i​n​c​l​u​d​e​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​n​c​l​u​d​e​ ​<​t​i​m​e​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​t​i​m​e​_​t​,​ ​t​i​m​e​(​)​

/​/​ ​g​c​c​ ​-​g​ ​-​s​t​d​=​c​9​9​ ​-​o​ ​l​o​c​a​l​i​t​y​ ​l​o​c​a​l​i​t​y​.​m​

#​d​e​f​i​n​e​ ​A​R​R​A​Y​S​I​Z​E​ ​2​0​0​0​0​
i​n​t​ ​a​[​A​R​R​A​Y​S​I​Z​E​]​[​A​R​R​A​Y​S​I​Z​E​]​;​ ​/​/​ ​m​a​k​e​ ​a​ ​h​u​g​e​ ​a​r​r​a​y​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​/​/​ ​W​a​l​k​ ​t​h​e​ ​a​r​r​a​y​ ​i​n​ ​r​o​w​-​m​a​j​o​r​ ​o​r​d​e​r​,​ ​s​o​ ​t​h​a​t​ ​o​n​c​e​ ​w​e​'​r​e​ ​d​o​n​e​
 ​ ​ ​ ​/​/​ ​w​i​t​h​ ​a​ ​p​a​g​e​ ​w​e​ ​n​e​v​e​r​ ​b​o​t​h​e​r​ ​w​i​t​h​ ​i​t​ ​a​g​a​i​n​.​

 ​ ​ ​ ​t​i​m​e​_​t​ ​s​t​a​r​t​t​i​m​e​ ​=​ ​t​i​m​e​(​N​U​L​L​)​;​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​A​R​R​A​Y​S​I​Z​E​;​ ​i​+​+​)​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​o​r​(​i​n​t​ ​j​ ​=​ ​0​;​ ​j​ ​<​ ​A​R​R​A​Y​S​I​Z​E​;​ ​j​+​+​)​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​[​i​]​[​j​]​ ​=​ ​1​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​t​i​m​e​_​t​ ​e​n​d​t​i​m​e​ ​=​ ​t​i​m​e​ ​(​N​U​L​L​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​(​"​r​o​w​-​m​a​j​o​r​:​ ​%​d​ ​o​p​e​r​a​t​i​o​n​s​ ​i​n​ ​%​l​d​ ​s​e​c​o​n​d​s​.​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​A​R​R​A​Y​S​I​Z​E​ ​*​ ​A​R​R​A​Y​S​I​Z​E​,​ ​e​n​d​t​i​m​e​ ​-​ ​s​t​a​r​t​t​i​m​e​)​;​

 ​ ​ ​ ​/​/​ ​W​a​l​k​ ​t​h​e​ ​a​r​r​a​y​ ​i​n​ ​c​o​l​u​m​n​-​m​a​j​o​r​ ​o​r​d​e​r​.​ ​ ​I​t​ ​e​n​d​s​ ​u​p​ ​t​o​u​c​h​i​n​g​ ​a​ ​b​u​n​c​h​ ​o​f​
 ​ ​ ​ ​/​/​ ​p​a​g​e​s​ ​m​u​l​t​i​p​l​e​ ​t​i​m​e​s​.​

 ​ ​ ​ ​s​t​a​r​t​t​i​m​e​ ​=​ ​t​i​m​e​(​N​U​L​L​)​;​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​j​ ​=​ ​0​;​ ​j​ ​<​ ​A​R​R​A​Y​S​I​Z​E​;​ ​j​+​+​)​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​o​r​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​A​R​R​A​Y​S​I​Z​E​;​ ​i​+​+​)​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​[​i​]​[​j​]​ ​=​ ​1​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​e​n​d​t​i​m​e​ ​=​ ​t​i​m​e​ ​(​N​U​L​L​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​(​"​c​o​l​u​m​n​-​m​a​j​o​r​:​ ​%​d​ ​o​p​e​r​a​t​i​o​n​s​ ​i​n​ ​%​l​d​ ​s​e​c​o​n​d​s​.​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​A​R​R​A​Y​S​I​Z​E​ ​*​ ​A​R​R​A​Y​S​I​Z​E​,​ ​e​n​d​t​i​m​e​ ​-​ ​s​t​a​r​t​t​i​m​e​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

Here is a sample run:

$​ ​.​/​l​o​c​a​l​i​t​y​
r​o​w​-​m​a​j​o​r​:​ ​4​0​0​0​0​0​0​0​0​ ​o​p​e​r​a​t​i​o​n​s​ ​i​n​ ​3​ ​s​e​c​o​n​d​s​.​
c​o​l​u​m​n​-​m​a​j​o​r​:​ ​4​0​0​0​0​0​0​0​0​ ​o​p​e​r​a​t​i​o​n​s​ ​i​n​ ​2​7​ ​s​e​c​o​n​d​s​.​

A simple reversal of the for loops can result in a 9x performance penalty! The first loop follows the way that C has the array’s memory organized, as shown in Figure 10.1. This loop accesses adjacent bytes, and as it works through the array, it has good locality of reference. Memory pages are accessed only once, and after the loop has stopped manipulating memory on a page, that page is no longer used.

Figure 10.1  Good memory access pattern

The second loop works “across the grain,” as shown in Figure 10.2. It ends up hitting every page used by the array every time through the loop. This puts a lot of pressure on the virtual memory system because every page stays “warm,” causing the kernel to keep shuffling its least-recently-used page lists. The first loop, because it does not touch a page once the work is done, is nicer to the kernel. Once the page ages out of the kernel’s data structures, it is never seen again.

Figure 10.2  Bad memory access pattern

Caches

One common way to boost performance is caching, where you keep around some loaded or calculated data in memory. If you are not careful, this technique can have drawbacks in a system that employs virtual memory and paging. Recall that memory that hasn’t been accessed recently can be paged out to disk and the space in RAM can be used by other processes. iOS devices do not page data to disk, so dirty pages are always resident.

If do you choose to cache information, it is best to split up your cache data and the metadata that describes the cached data. You don’t want to use an architecture like Figure 10.3, which mixes the cache data and the metadata.

Figure 10.3  Bad locality of reference

Instead, organize your data as shown in Figure 10.4. Keep your metadata together because you will have good locality of reference when walking through your cache looking for expired objects. You can even do your own form of virtual memory: if a cache entry has not been used in a while or if you get an iOS memory warning, you can remove your data blob from memory and then load it again when needed.

Figure 10.4  Good locality of reference

Memory is the New I/O

The motivation that drives programmers to cache data read from disk is that I/O from disk is hideously expensive. Waiting for one disk I/O can cost hundreds of thousands (or more) CPU cycles that could be put to better use.

With today’s processors, memory subsystems, and bus architectures, RAM has become like I/O. Sometimes accessing memory can be extremely slow compared to CPU speed. For example, according to an Apple tech note, a G5 could do 16 to 50 vector adds in the time it takes to load a cache line from memory. And the situation can get even worse with modern processors.

The “precalculate and store in memory” technique can become a bottleneck compared to brute-force calculations. The CPU can grind through some calculations faster than the fetch from memory, and the look-up table can force more important data out of the CPU caches. The tech note goes on to say “In one example, vector code that converts unsigned char data to float and then applies a 9th order polynomial to it is still marginally faster than hand tuned scalar code that does a lookup into a 256 entry lookup table containing floats.”

Level-1 Cache, the cache memory nearest the CPU logic units, has an area for instructions, but it is only 32 to 64 kilobytes large, per core. Optimizations that increase code length, like loop unrolling and 64-bit code in general, can blow out this cache, requiring code to be continually brought in from RAM. It becomes a balancing act between the size of code, what you calculate, and what you store and retrieve. Sometimes trial-and-error is the way to go to see what technique results in the best performance, especially if you have high-performance scientific modeling that will have long runtimes or if you are dealing with large volumes of data quickly, such as when processing video.

Semantics of the C language can get in the way, optimization-wise, especially with regards to memory. If the compiler knows how a particular chunk of memory is being accessed, it can cache the values in registers or even avoid loading data that has already been loaded. Because C has pointers, there can be aliasing problems. There may be a pointer elsewhere in the process that is pointing to (and could conceivably modify) a piece of memory that the compiler could otherwise optimize access to. This is why languages that do not have pointers, like FORTRAN, can perform much more aggressive optimizations.

When using a data structure through a pointer or a global variable in a loop, the compiler will emit code to reload that location in memory each time through the loop. It does this just in case the value was changed by someone else, either in another thread or by a function called inside the loop. Making a local variable to hold the global’s value lets the compiler figure out that this data is not going to change and thus avoids the memory hit each time through the loop.

CPU

CPU usage is the metric that most programmers think about first when confronted with an optimization issue. “My app is pegging the CPU, and I need to speed it up.” Typically when CPU usage becomes a dominant factor, the root cause is a slow algorithm. It might have a high level of complexity, or it might just be a poor implementation. In almost all cases, changing your algorithm will give you more speedups than most other kinds of code or system tweaking. The classic example is changing from a bubble-sort, an order O(N² algorithm), to a quicksort or merge sort, which is O(n log n).

Sometimes a bad implementation of an algorithm can wreak havoc. For instance, a programming error turned strstr() in one version of SunOS 4.1.x from an O(N) operation to a worthless O(N²) one:

 ​ ​ ​ ​w​h​i​l​e​ ​(​c​ ​<​ ​s​t​r​l​e​n​(​s​t​r​i​n​g​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​d​o​ ​s​t​u​f​f​ ​w​i​t​h​ ​s​t​r​i​n​g​[​c​]​
 ​ ​ ​ ​}​

Recall that C strings do not store their length. A string is just a sequence of bytes terminated by zero. strlen() has to traverse the entire string counting characters. There are tricks you can use to do the work in greater than one-byte chunks, but it’s still an O(N) operation. In this particular case, the length of the string is not going to change, so there is no reason to take the length every time through the loop.

Luckily, high CPU usage can be easily discovered by noticing that the CPU meter is pegged in Activity Monitor, that top is showing your app consuming 99% of the available CPU power, or that your laptop case has started glowing. The sampling and profiling tools discussed later in this chapter are ideal for tracking down the cause of these problems.

Disk

Disk access is very slow – many orders of magnitude slower than accessing memory. In general, if you can avoid disk I/O, do so. If you are planning on caching data from disk, remember that the virtual memory system also uses the disk. If you cache a large amount of data, you could end up causing the VM system to do disk I/O. This is a very bad situation because you have now exchanged one disk read (from disk into memory) into a read and a write to page it out and then another read to page it back in from the disk into memory.

Locality of reference plays a part when optimizing disk access when VM paging involved. With bad locality of reference, you end up touching lots of pages. These pages cause other pages to “age out” of the VM cache and get sent to disk. Eventually you will touch them again which could cause disk I/O to retrieve the data on those pages.

You can avoid some of the expense of disk I/O by not doing the work at all. Putting windows into different .nib files and loading them on demand is a common technique. If you do not need to show the window, there is no reason to load it in memory.

Similarly, if you have a large database of information, accessing it piecemeal can yield significant speedups over loading the whole thing into memory. Using memory-mapped files can avoid disk activity because only the parts of the file being touched will make their way into memory.

Graphics

The Quartz graphics engine in Mac OS X puts a lot of pressure on the memory system. Quartz uses large graphic buffers, one for each window visible on the screen. There are also compositing operations to render the user’s desktop. Quartz also uses the CPU to do some of its drawing effects, although many of these operations have been migrated to the graphics processing units on the graphics card. There are some operations that do not work well on the GPU, so these must be done on the CPU.

The key to optimizing graphics is to avoid drawing when you can. Use the Quartz Debug utility, shown in Figure 10.5, to see where you are doing unnecessary drawing. The most commonly used features are in the Drawing Information panel. Autoflush drawing causes drawing operations to appear on the screen as soon as they happen rather than being batched for the next update. Areas to the screen that are drawn to can be made to flash so you can see where you are drawing. Identical screen updates are highlighted in a different color so you can see where redundant work is happening.

Figure 10.5  Quartz Debug

NSView has some features that let you decide which parts of the view need to be redrawn and which ones do not. You can hit-test the rectangle that is passed to NSView’s drawRect: method and only perform drawing calls for items that live in that rectangle. This rectangle tends to be the union of all of the area that needs redrawing, so you can consult getRectsBeingDrawn: and needsToDrawRect: to hit-test against the areas that need to be redrawn.

One aspect of Quartz drawing that catches some programmers off guard is that overlapping lines in a single path can be very expensive. A lot of work happens at line crossings, such as anti-aliasing the intersections, as well as making sure that transparent colors do not get “painted” multiple times at the crossings and appear darker. If you need to draw lots of overlapping lines, especially if you are using opaque colors and do not care about antialiasing, you can get much better performance by drawing a bunch of small paths instead.

Before using any of the profiling tools

Forget any assumptions you may have about where the performance problems may be. Programmers are notoriously bad about predicting where performance problems are; otherwise, the problems would already be fixed. One programmer I worked with was convinced that file loading and disk I/O was the slow part of his program when loading files, and he was spending a lot of effort to optimize disk access. After a quick session with Shark, the problem actually turned out to be the marshaling of data into a tree so that NSOutlineView could use it. The time spent in actual file I/O was minimal.

Keep good notes on what you do and the measurements you make so that you can apply the optimizations to other situations. By keeping a record of execution times (for instance), you can tell if your optimization attempts are helping or are actually making the problem worse.

When tracking down performance problems, throw a large data set at your application. With the file-loading issue mentioned earlier, some of the test data were 5K files that took a second or two to load. That’s too small a window in which to figure anything out. If your application is designed to edit 50-page research papers, then throw a 500- or 5000-page document at it. The larger data sets should make O(N²) algorithms stand out like the proverbial sore thumb. If your program is responsive when editing 5000-page documents, it should give the user a really nice experience when they are using it to edit 50-page documents. Do not bother with more than two or three orders of magnitude more data, since that much more data will probably require a redesign of your data structures and may become suboptimal for smaller data sets.

There is some debate over when you should optimize. One school of thought is “premature optimization is the root of all evil,” and you should wait until the end of your development cycle to identify and fix performance problems. Unfortunately, that can require re-engineering large chunks of the product if there is a deeply-rooted bottleneck. Another school of thought is to act like you are on a diet and adopt a constant discipline about performance. The downside to that is that premature optimization can obscure the design and the code and make it harder to track down program errors before shipping.

As with most everything in life, the middle ground is a good place to live. Keep an eye out for algorithms that can be improved, but do not obfuscate code to trim every cycle you can too early in the development process. Throw large data sets at your program often. Do not wait until right before a trade show or a launch to subject your program to what the customer will throw at it. Keep an eye on your memory usage so that it does not grow too large too quickly. Also be sure to run the program in the user’s environment. If you are writing a desktop app, be sure to have Safari and iTunes running, since the user will probably be using those apps, too. If your application is a memory pig and makes iTunes skip, you will definitely get some user complaints.

Command-Line Tools

Mac OS X comes with a number of command-line tools for tracking down particular types of performance problems. The nice thing about the command line is that you can remotely log into a machine and watch things as they happen. They also don’t interfere with your application’s user interface.

time

The simplest tool is time. It times command execution and shows you clock time, CPU time in userspace, and CPU time spent in the kernel. Here is a run of /usr/bin/time on TextEdit. The time measured was the time starting TextEdit up, loading /usr/share/dict/words, and then scrolling from the top to the bottom.

$​ ​t​i​m​e​ ​/​A​p​p​l​i​c​a​t​i​o​n​s​/​T​e​x​t​E​d​i​t​.​a​p​p​/​C​o​n​t​e​n​t​s​/​M​a​c​O​S​/​T​e​x​t​E​d​i​t​
r​e​a​l​ ​ ​ ​0​m​1​4​.​6​1​9​s​
u​s​e​r​ ​ ​ ​0​m​1​.​2​5​7​s​
s​y​s​ ​ ​ ​ ​0​m​0​.​1​8​0​s​

This is 14 seconds of clock time, one second in user space, and less than a second in the kernel.

The C shell has its own version of time that gives more information:

%​ ​t​i​m​e​ ​/​A​p​p​l​i​c​a​t​i​o​n​s​/​T​e​x​t​E​d​i​t​.​a​p​p​/​C​o​n​t​e​n​t​s​/​M​a​c​O​S​/​T​e​x​t​E​d​i​t​
2​.​5​1​5​u​ ​0​.​2​2​6​s​ ​0​:​1​5​.​0​1​ ​1​8​.​1​%​	​0​+​0​k​ ​2​2​+​4​3​i​o​ ​0​p​f​+​0​w​

This is 2.5 seconds in user space, 0.2 seconds in kernel space, and fifteen seconds clock time. The 18.1% is a utilization percentage: the ratio of user + system times to real time. Following the time information is memory information: shared + unshared memory usage, input + output operations, number of pagefaults and swaps. OS X seems not to report the shared + unshared memory usage.

time is very handy when comparing optimizations. Run a baseline or two with time, make the optimization, then try time again. If you are optimizing CPU usage and discover CPU time figures going up, you should reconsider that particular optimization.

dtruss

Many Unix systems have a utility that will show all of the system calls a program makes. On Solaris, it is called truss; on Linux, it’s strace. Mac OS X 10.4 has ktrace (kernel tracing), and Mac OS X 10.5 and later have a similar utility, dtruss, based on DTrace.

dtruss requires root privileges to run because DTrace requires them. Run it like this:

$​ ​s​u​d​o​ ​d​t​r​u​s​s​ ​l​s​

This will generate a couple of hundred lines of output, like

S​Y​S​C​A​L​L​(​a​r​g​s​)​ ​	​	​ ​=​ ​r​e​t​u​r​n​
g​e​t​p​i​d​(​0​x​7​F​F​F​5​F​B​F​F​6​0​0​,​ ​0​x​7​F​F​F​F​F​E​0​0​0​5​0​,​ ​0​x​0​)​	​	​ ​=​ ​9​2​3​4​ ​0​
o​p​e​n​_​n​o​c​a​n​c​e​l​(​"​/​d​e​v​/​u​r​a​n​d​o​m​\​0​"​,​ ​0​x​0​,​ ​0​x​0​)​	​	​ ​=​ ​3​ ​0​
r​e​a​d​_​n​o​c​a​n​c​e​l​(​0​x​3​,​ ​"​.​.​.​"​,​ ​0​x​6​C​)​	​	​ ​=​ ​1​0​8​ ​0​
c​l​o​s​e​_​n​o​c​a​n​c​e​l​(​0​x​3​)​	​	​ ​=​ ​0​ ​0​
i​s​s​e​t​u​g​i​d​(​0​x​1​0​0​0​0​0​0​0​0​,​ ​0​x​7​F​F​F​5​F​B​F​F​8​C​8​,​ ​0​x​7​F​F​F​5​F​C​4​0​5​3​0​)​	​	​ ​=​ ​0​ ​0​
g​e​t​e​u​i​d​(​0​x​1​0​0​0​0​0​0​0​0​,​ ​0​x​7​F​F​F​5​F​B​F​F​8​C​8​,​ ​0​x​0​)​	​	​ ​=​ ​0​ ​0​
.​.​.​

The output is not terribly easy to read, but there is a lot of information there. As ls started up, it called getpid(), which returned the value 9234, the process ID for ls. After the function exited, errno was zero. If there was an error, dtruss would print a result like this:

s​t​a​t​6​4​(​"​g​r​a​u​s​e​n​s​t​e​i​n​\​0​"​,​ ​0​x​7​F​F​F​5​F​B​F​E​F​B​0​,​ ​0​x​1​)​	​	​ ​=​ ​-​1​ ​E​r​r​#​2​

with a return value of -1, and errno set to 2.

Being able to see the system call traffic can be a great debugging aid, especially if you have a program that will not start. You can see if the program is trying to load a missing shared library or if it needs some configuration file that is not supplied.

System call tracking can be a performance-tuning aid, too. You might discover a lot of one-byte writes that can be coalesced into a single operation, you may have given a bad timeout to kevent() so that it returns a lot sooner than you expect, or you can see why your program is blocking unexpectedly.

fs_usage and sc_usage

fs_usage and sc_usage are programs run as the root user that also show system call activity. fs_usage shows file system information, and sc_usage shows system call information.

Here is BigShow, the Big Nerd Ranch slide show application, about to start paging through slides:

$​ ​s​u​d​o​ ​f​s​_​u​s​a​g​e​
p​a​s​s​w​o​r​d​:​ ​
1​8​:​3​8​:​0​6​ ​o​p​e​n​ ​ ​/​P​r​e​f​e​r​e​n​c​e​s​/​c​o​m​.​a​p​p​l​e​.​d​o​c​k​.​p​l​i​s​t​ ​0​.​0​0​0​0​5​ ​ ​ ​B​i​g​S​h​o​w​ ​ ​ ​ ​ ​
1​8​:​3​8​:​0​6​ ​f​s​t​a​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​.​0​0​0​0​0​ ​ ​ ​B​i​g​S​h​o​w​ ​ ​ ​ ​ ​
1​8​:​3​8​:​0​6​ ​r​e​a​d​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​.​0​0​0​2​9​ ​ ​ ​B​i​g​S​h​o​w​ ​ ​ ​ ​ ​
1​8​:​3​8​:​0​6​ ​c​l​o​s​e​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​.​0​0​0​0​2​ ​ ​ ​B​i​g​S​h​o​w​ ​ ​ ​ ​ ​
1​8​:​3​8​:​0​6​ ​o​p​e​n​ ​ ​c​o​m​.​a​p​p​l​e​.​d​o​c​k​.​0​0​0​3​9​3​1​0​2​4​a​6​.​p​l​i​s​t​ ​0​.​0​0​0​1​5​ ​ ​ ​B​i​g​S​h​o​w​ ​ ​ ​ ​ ​
1​8​:​3​8​:​0​6​ ​P​A​G​E​_​I​N​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​.​0​0​0​7​0​ ​W​ ​B​i​g​S​h​o​w​ ​ ​ ​ ​ ​
1​8​:​3​8​:​0​6​ ​o​p​e​n​ ​ ​/​L​i​b​r​a​r​y​/​P​r​e​f​e​r​e​n​c​e​s​/​N​e​t​w​o​r​k​ ​ ​ ​ ​ ​ ​0​.​0​0​0​0​8​ ​ ​ ​B​i​g​S​h​o​w​ ​ ​ ​ ​ ​
1​8​:​3​8​:​0​6​ ​o​p​e​n​ ​ ​c​o​m​.​a​p​p​l​e​.​s​y​s​t​e​m​p​r​e​f​e​r​e​n​c​e​s​.​p​l​i​s​t​ ​0​.​0​0​0​0​5​ ​ ​ ​B​i​g​S​h​o​w​ ​ ​ ​ ​ ​

Part of Cocoa is looking at the plist for the dock, presumably for getting size and location information so that it can properly place a window. You can see the open, a stat to get the size of the file, the file being read, and its close. Unlike dtruss, there is not an easy way to correlate specific calls like a read() with the file descriptor it is using, but fs_usage does show you how much time it took. fs_usage can be run on a system-wide basis, which can be handy if you have a problem that is slowing the entire machine down. fs_usage is also useful when you have a program that accesses the hard drive unexpectedly and you want to track down who is responsible.

One really snazzy feature of fs_usage can be seen when used on applications that make Carbon file-system calls. If you set the environment variable DYLD_IMAGE_SUFFIX to _debug, fd_usage will show the Carbon calls being made. Here is a peek at an old copy of Mozilla running:

1​8​:​3​4​:​3​8​ ​G​e​t​C​a​t​I​n​f​o​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​.​0​0​0​1​7​4​ ​ ​L​a​u​n​c​h​C​F​M​A​p​p​
1​8​:​3​4​:​3​8​ ​P​B​M​a​k​e​F​S​S​p​e​c​ ​ ​(​0​,​ ​0​x​0​,​ ​0​x​0​,​ ​0​x​0​)​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​L​a​u​n​c​h​C​F​M​A​p​p​
1​8​:​3​4​:​3​8​ ​g​e​t​a​t​t​r​l​i​s​t​ ​ ​ ​.​v​o​l​/​2​8​0​7​6​3​/​M​o​z​i​l​l​a​.​a​p​p​ ​0​.​0​0​0​0​3​2​ ​ ​L​a​u​n​c​h​C​F​M​A​p​p​
1​8​:​3​4​:​3​8​ ​P​B​M​a​k​e​F​S​S​p​e​c​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​.​0​0​0​0​6​4​ ​ ​L​a​u​n​c​h​C​F​M​A​p​p​
1​8​:​3​4​:​3​8​ ​G​e​t​C​a​t​I​n​f​o​ ​ ​ ​ ​(​-​1​0​0​,​ ​0​x​0​,​ ​0​x​0​,​ ​0​x​0​)​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​L​a​u​n​c​h​C​F​M​A​p​p​
1​8​:​3​4​:​3​8​ ​g​e​t​a​t​t​r​l​i​s​t​ ​ ​ ​.​v​o​l​/​2​8​0​7​6​3​/​M​o​z​i​l​l​a​.​a​p​p​ ​0​.​0​0​0​0​4​6​ ​ ​L​a​u​n​c​h​C​F​M​A​p​p​

sc_usage shows system calls for a program in a manner like top, with a continually updating display. Here is a snapshot from Safari:

S​a​f​a​r​i​ ​ ​ ​ ​ ​ ​ ​1​2​ ​p​r​e​e​m​p​t​i​o​n​s​ ​ ​1​8​9​ ​c​o​n​t​e​x​t​ ​s​w​i​t​c​h​e​s​ ​ ​ ​ ​8​ ​t​h​r​e​a​d​s​ ​ ​ ​ ​1​6​:​0​5​:​4​2​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​ ​f​a​u​l​t​s​ ​ ​ ​ ​ ​ ​ ​7​0​6​ ​s​y​s​t​e​m​ ​c​a​l​l​s​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​:​0​0​:​0​7​

T​Y​P​E​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​U​M​B​E​R​ ​ ​ ​ ​ ​ ​ ​ ​C​P​U​_​T​I​M​E​ ​ ​ ​W​A​I​T​_​T​I​M​E​
-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
S​y​s​t​e​m​ ​ ​ ​ ​ ​ ​ ​ ​ ​I​d​l​e​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​0​:​0​6​.​6​0​8​(​0​0​:​0​0​.​9​2​2​)​
S​y​s​t​e​m​ ​ ​ ​ ​ ​ ​ ​ ​ ​B​u​s​y​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​0​:​0​0​.​3​6​8​(​0​0​:​0​0​.​0​6​5​)​
S​a​f​a​r​i​ ​ ​ ​ ​ ​ ​ ​ ​ ​U​s​e​r​m​o​d​e​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​0​:​0​0​.​1​0​9​

m​a​c​h​_​m​s​g​_​t​r​a​p​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​2​0​0​4​(​3​8​2​)​ ​ ​ ​ ​ ​0​0​:​0​0​.​0​1​3​ ​ ​0​0​:​1​7​.​8​7​8​(​0​0​:​0​2​.​9​7​8​)​ ​3​
k​e​v​e​n​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​2​0​(​3​)​ ​ ​ ​ ​ ​ ​ ​0​0​:​0​0​.​0​0​0​ ​ ​0​0​:​0​5​.​5​3​1​(​0​0​:​0​1​.​0​0​5​)​ ​W​
s​e​m​w​a​i​t​_​s​i​g​n​a​l​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​2​(​1​)​ ​ ​ ​ ​ ​ ​ ​0​0​:​0​0​.​0​0​0​ ​ ​0​0​:​0​5​.​4​9​6​(​0​0​:​0​1​.​0​0​3​)​ ​W​
s​e​l​e​c​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​3​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​0​:​0​0​.​0​0​0​ ​ ​0​0​:​0​5​.​0​7​6​(​0​0​:​0​1​.​0​0​4​)​ ​W​

C​U​R​R​E​N​T​_​T​Y​P​E​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​L​A​S​T​_​P​A​T​H​N​A​M​E​_​W​A​I​T​E​D​_​F​O​R​ ​ ​ ​ ​ ​C​U​R​_​W​A​I​T​_​T​I​M​E​ ​T​H​R​D​#​ ​P​R​I​
-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
m​a​c​h​_​m​s​g​_​t​r​a​p​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​0​:​0​0​.​0​1​6​ ​ ​ ​ ​ ​0​ ​ ​ ​ ​ ​4​6​
m​a​c​h​_​m​s​g​_​t​r​a​p​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​0​:​0​0​.​1​5​0​ ​ ​ ​ ​ ​1​ ​ ​ ​ ​ ​4​6​
s​e​m​w​a​i​t​_​s​i​g​n​a​l​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​0​:​0​0​.​4​9​6​ ​ ​ ​ ​ ​2​ ​ ​ ​ ​ ​4​7​
k​e​v​e​n​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​0​:​0​0​.​4​8​1​ ​ ​ ​ ​ ​3​ ​ ​ ​ ​ ​4​9​
w​o​r​k​q​_​o​p​s​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​0​:​0​0​.​4​7​8​ ​ ​ ​ ​ ​4​ ​ ​ ​ ​ ​4​7​
s​e​l​e​c​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​0​:​0​1​.​3​9​1​ ​ ​ ​ ​ ​5​ ​ ​ ​ ​ ​4​6​ ​
m​a​c​h​_​m​s​g​_​t​r​a​p​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​0​:​0​1​.​3​9​1​ ​ ​ ​ ​ ​6​ ​ ​ ​ ​ ​6​2​ ​
b​s​d​t​h​r​e​a​d​_​t​e​r​m​i​n​a​t​e​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​0​:​0​5​.​0​1​9​ ​ ​ ​ ​ ​7​ ​ ​ ​ ​ ​4​7​

The CPU_TIME column is the amount of CPU time consumed, and WAIT_TIME is the absolute time the process waits.

If you think you have I/O performance problems, these two programs can help you track down the specific calls that could be causing problems.

top

Unix systems are complex beasts composed of multiple programs interacting. Sometimes performance problems manifest as overall system slowness while each program looks just fine in isolation. The dtruss and sc_usage utilities are useful for monitoring system calls in a particular program. top, on the other hand, can be used to monitor all the programs on the system. Running top without arguments will show the familiar OS information (memory distributions, load average). By default, it orders programs by launch order (most recent program listed first). This is useful if you are monitoring a recently launched program. The -u flag will sort the list by CPU usage.

top can also count and show system-wide events. top -e shows VM (virtual memory), network activity, disk activity, and messaging stats:

$​ ​t​o​p​ ​-​e​

P​r​o​c​e​s​s​e​s​:​ ​7​0​ ​t​o​t​a​l​,​ ​4​ ​r​u​n​n​i​n​g​,​ ​6​6​ ​s​l​e​e​p​i​n​g​,​ ​2​6​0​ ​t​h​r​e​a​d​s​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​6​:​0​9​:​3​0​
L​o​a​d​ ​A​v​g​:​ ​0​.​1​9​,​ ​0​.​2​8​,​ ​0​.​2​4​ ​ ​C​P​U​ ​u​s​a​g​e​:​ ​6​.​2​7​%​ ​u​s​e​r​,​ ​5​.​5​1​%​ ​s​y​s​,​ ​8​8​.​2​0​%​ ​i​d​l​e​
S​h​a​r​e​d​L​i​b​s​:​ ​7​4​2​4​K​ ​r​e​s​i​d​e​n​t​,​ ​7​2​7​6​K​ ​d​a​t​a​,​ ​0​B​ ​l​i​n​k​e​d​i​t​.​
M​e​m​R​e​g​i​o​n​s​:​ ​9​3​6​1​ ​t​o​t​a​l​,​ ​4​5​1​M​ ​r​e​s​i​d​e​n​t​,​ ​1​7​M​ ​p​r​i​v​a​t​e​,​ ​2​9​0​M​ ​s​h​a​r​e​d​.​
P​h​y​s​M​e​m​:​ ​7​9​8​M​ ​w​i​r​e​d​,​ ​1​0​6​2​M​ ​a​c​t​i​v​e​,​ ​4​2​7​M​ ​i​n​a​c​t​i​v​e​,​ ​2​2​8​7​M​ ​u​s​e​d​,​ ​1​8​0​9​M​ ​f​r​e​e​.​
V​M​:​ ​1​6​8​G​ ​v​s​i​z​e​,​ ​1​0​4​1​M​ ​f​r​a​m​e​w​o​r​k​ ​v​s​i​z​e​,​ ​3​3​9​5​8​9​(​0​)​ ​p​a​g​e​i​n​s​,​ ​2​7​8​0​5​5​(​0​)​ ​p​a​g​e​o​u​t​s​.​
N​e​t​w​o​r​k​s​:​ ​p​a​c​k​e​t​s​:​ ​2​5​5​0​9​4​9​/​3​3​1​1​M​ ​i​n​,​ ​1​6​6​7​3​1​6​/​1​6​0​M​ ​o​u​t​.​
D​i​s​k​s​:​ ​5​6​1​0​4​8​/​8​2​1​4​M​ ​r​e​a​d​,​ ​9​7​6​9​1​1​/​2​1​G​ ​w​r​i​t​t​e​n​.​

P​I​D​ ​ ​ ​C​O​M​M​A​N​D​ ​ ​ ​ ​%​C​P​U​ ​T​I​M​E​ ​ ​ ​ ​ ​#​T​H​ ​ ​#​W​Q​ ​#​P​O​R​ ​#​M​R​E​ ​R​P​R​V​T​ ​ ​R​S​H​R​D​ ​ ​R​S​I​Z​E​ ​ ​V​P​R​V​T​
9​3​1​7​ ​ ​t​o​p​ ​ ​ ​ ​ ​ ​ ​ ​3​.​8​ ​ ​0​0​:​0​0​.​6​3​ ​1​/​1​ ​ ​0​ ​ ​ ​2​4​ ​ ​ ​3​3​ ​ ​ ​9​2​0​K​ ​ ​ ​2​4​4​K​ ​ ​ ​1​4​9​6​K​ ​ ​1​7​M​ ​
9​3​1​6​-​ ​W​e​b​K​i​t​P​l​u​g​ ​0​.​3​ ​ ​0​0​:​0​3​.​4​7​ ​6​ ​ ​ ​ ​2​ ​ ​ ​1​1​0​ ​ ​2​4​5​ ​ ​9​5​8​4​K​ ​ ​3​0​M​ ​ ​ ​ ​1​5​M​ ​ ​ ​ ​4​9​M​ ​
9​3​0​6​ ​ ​S​a​f​a​r​i​ ​ ​ ​ ​ ​0​.​2​ ​ ​0​0​:​1​2​.​0​2​ ​1​1​ ​ ​ ​2​ ​ ​ ​1​6​7​ ​ ​5​3​3​ ​ ​9​7​M​ ​ ​ ​ ​5​3​M​ ​ ​ ​ ​1​5​3​M​ ​ ​ ​2​6​5​M​ ​
9​2​9​9​ ​ ​c​s​h​ ​ ​ ​ ​ ​ ​ ​ ​0​.​0​ ​ ​0​0​:​0​0​.​0​4​ ​1​ ​ ​ ​ ​0​ ​ ​ ​1​5​ ​ ​ ​2​6​ ​ ​ ​6​1​2​K​ ​ ​ ​5​9​2​K​ ​ ​ ​1​1​6​0​K​ ​ ​1​7​M​ ​
9​2​6​8​ ​ ​m​d​w​o​r​k​e​r​ ​ ​ ​0​.​0​ ​ ​0​0​:​0​0​.​3​6​ ​3​ ​ ​ ​ ​1​ ​ ​ ​5​0​ ​ ​ ​7​7​ ​ ​ ​4​3​8​8​K​ ​ ​1​9​M​ ​ ​ ​ ​1​2​M​ ​ ​ ​ ​2​9​M​ ​
9​2​0​2​ ​ ​b​a​s​h​ ​ ​ ​ ​ ​ ​ ​0​.​0​ ​ ​0​0​:​0​0​.​0​3​ ​1​ ​ ​ ​ ​0​ ​ ​ ​1​7​ ​ ​ ​2​5​ ​ ​ ​3​2​0​K​ ​ ​ ​2​4​4​K​ ​ ​ ​9​7​6​K​ ​ ​ ​9​5​7​6​K​ ​
9​1​9​8​ ​ ​c​s​h​ ​ ​ ​ ​ ​ ​ ​ ​0​.​0​ ​ ​0​0​:​0​0​.​0​2​ ​1​ ​ ​ ​ ​0​ ​ ​ ​1​7​ ​ ​ ​2​6​ ​ ​ ​5​8​0​K​ ​ ​ ​5​9​2​K​ ​ ​ ​1​1​6​4​K​ ​ ​9​6​4​8​K​ ​
9​1​9​7​ ​ ​l​o​g​i​n​ ​ ​ ​ ​ ​ ​0​.​0​ ​ ​0​0​:​0​0​.​0​2​ ​1​ ​ ​ ​ ​0​ ​ ​ ​2​2​ ​ ​ ​5​4​ ​ ​ ​4​7​2​K​ ​ ​ ​3​1​2​K​ ​ ​ ​1​6​0​8​K​ ​ ​1​0​M​ ​
9​1​8​0​ ​ ​s​s​h​ ​ ​ ​ ​ ​ ​ ​ ​0​.​0​ ​ ​0​0​:​0​0​.​0​4​ ​1​ ​ ​ ​ ​0​ ​ ​ ​2​2​ ​ ​ ​2​5​ ​ ​ ​5​0​8​K​ ​ ​ ​2​4​4​K​ ​ ​ ​1​8​1​2​K​ ​ ​9​5​8​8​K​ ​
8​9​0​0​ ​ ​P​r​e​v​i​e​w​ ​ ​ ​ ​0​.​0​ ​ ​0​0​:​1​6​.​2​7​ ​2​ ​ ​ ​ ​1​ ​ ​ ​1​2​5​ ​ ​2​6​8​ ​ ​2​0​M​ ​ ​ ​ ​7​3​M​ ​ ​ ​ ​4​5​M​ ​ ​ ​ ​2​8​M​ ​ ​ ​
8​8​8​6​ ​ ​W​e​b​K​i​t​P​l​u​g​ ​0​.​0​ ​ ​0​0​:​0​0​.​0​1​ ​2​ ​ ​ ​ ​2​ ​ ​ ​2​8​ ​ ​ ​5​1​ ​ ​ ​5​4​4​K​ ​ ​ ​2​4​4​K​ ​ ​ ​9​8​8​K​ ​ ​ ​4​0​M​ ​
8​8​5​0​ ​ ​A​c​t​i​v​i​t​y​ ​M​ ​0​.​0​ ​ ​0​0​:​3​5​.​6​3​ ​2​ ​ ​ ​ ​1​ ​ ​ ​1​0​6​ ​ ​2​4​3​ ​ ​5​3​6​0​K​ ​ ​7​5​M​ ​ ​ ​ ​1​5​M​ ​ ​ ​ ​2​5​M​ ​ ​
8​7​0​0​ ​ ​V​D​C​A​s​s​i​s​t​a​ ​0​.​0​ ​ ​0​0​:​0​0​.​1​5​ ​4​ ​ ​ ​ ​1​ ​ ​ ​9​0​ ​ ​ ​7​3​ ​ ​ ​3​8​4​K​ ​ ​ ​1​9​M​ ​ ​ ​ ​3​1​0​4​K​ ​ ​2​3​M​ ​
8​6​9​7​-​ ​S​n​a​k​ ​ ​ ​ ​ ​ ​ ​1​.​5​ ​ ​0​7​:​0​3​.​5​1​ ​5​/​1​ ​ ​1​ ​ ​ ​1​9​2​ ​ ​2​3​2​ ​ ​3​7​8​8​K​ ​ ​4​3​M​ ​ ​ ​ ​1​1​M​ ​ ​ ​ ​4​0​M​ ​

There is a lot of information here. 70 processes, 260 threads system-wide. Shared libraries take about 7 megabytes of memory, 1.8 gigs of physical memory free, 168 gigs of virtual memory allocated, network and disk I/Os. Each process has information such as the number of threads, work queues, mach ports, and memory regions. Resize your terminal window to see more columns, such as virtual size, process state, page faults, bsd system calls made, etc.

top -e shows cumulative output, while top -d will show things in a delta mode. The update interval is one second. That can be changed by using the -s flag to control the number of seconds between intervals.

Stochastic profiling

One useful low-tech tool is “stochastic profiling,” where you run the program in the debugger and interrupt it occasionally to see what is on the call stack. If you see the same function(s) on the stack over and over again, you know where to start looking. This technique is handy if you are on a platform or in a situation where traditional performance tools are not available or do not work. Plus, it’s fast and easy, especially if you are already running your program in a debugger.

sample

You can do some profiling from the command-line to answer quick-and-dirty “what is happening here?” kinds of questions. The sample program will sample a process at 10-millisecond intervals and then build a snapshot of what the program was doing. You can give sample a pid or give it the partial name of a program:

$​ ​s​a​m​p​l​e​ ​i​T​u​n​e​s​ ​5​
S​a​m​p​l​i​n​g​ ​p​r​o​c​e​s​s​ ​2​1​6​ ​e​a​c​h​ ​1​0​ ​m​s​e​c​s​ ​5​0​0​ ​t​i​m​e​s​
S​a​m​p​l​e​ ​a​n​a​l​y​s​i​s​ ​o​f​ ​p​r​o​c​e​s​s​ ​2​1​6​ ​w​r​i​t​t​e​n​ ​f​i​l​e​ ​/​t​m​p​/​i​T​u​n​e​s​_​2​1​6​.​s​a​m​p​l​e​.​t​x​t​

The resulting trace file shows a bunch of call stacks, one for each thread, along with the number of times it found those particular functions on a call stack. Here’s an example of one thread that is waiting in a run loop.

4​3​4​ ​T​h​r​e​a​d​_​1​1​0​3​
 ​ ​4​3​4​ ​_​p​t​h​r​e​a​d​_​b​o​d​y​
 ​ ​ ​ ​4​3​4​ ​d​y​l​d​_​s​t​u​b​_​b​i​n​d​i​n​g​_​h​e​l​p​e​r​
 ​ ​ ​ ​ ​ ​4​3​4​ ​C​F​R​u​n​L​o​o​p​R​u​n​
 ​ ​ ​ ​ ​ ​ ​ ​4​3​4​ ​C​F​R​u​n​L​o​o​p​R​u​n​S​p​e​c​i​f​i​c​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​4​3​4​ ​_​_​C​F​R​u​n​L​o​o​p​R​u​n​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​4​3​4​ ​m​a​c​h​_​m​s​g​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​4​3​4​ ​m​a​c​h​_​m​s​g​_​t​r​a​p​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​4​3​4​ ​m​a​c​h​_​m​s​g​_​t​r​a​p​

This is the same output you get when sampling processes in Activity Monitor.

Precise Timing with mach_absolute_time()

Command-line tools are a great place to benchmark snippets of code, which is useful for those cases where you can isolate an algorithm or a programming technique out of your full application. A dozen-line or a couple-hundred-line command-line tool is a much more tractable problem than a million-line application. Not every problem can be put into a little benchmark, but enough of them can to make it a useful technique.

The nice thing about command-line programs is you can use the time command to get absolute figures of the running time of the program making it easy to compare and contrast changes you make to your target program.

But sometimes the time command does not have enough granularity. You might want more precise timing, or you may just be interested in timing a specific part of your program. You might not be interested in the time it takes to load the data to feed your algorithm. If loading the data takes 3 times as long as it takes the algorithm to run, you will want to do timing inside of the program yourself.

Mach, Mac OS X’s kernel, provides some functions you can use for precise timing. mach_absolute_time() reads the CPU time base register and reports the value back to you. This time base register serves as the basis for other time measurements in the OS:

uint64_t mach_absolute_time (void);

mach_absolute_time() returns values based on the CPU time, so it is not directly usable for getting time values because you do not know what time span each increment of the counter represents.

To translate mach_absolute_time()’s results to nanoseconds, use mach_timebase_info() to get the scaling of mach_absolute_time()’s values:

kern_return_t mach_timebase_info (mach_timebase_info_t info);

Where mach_timebase_info_t is a pointer to this struct:

s​t​r​u​c​t​ ​m​a​c​h​_​t​i​m​e​b​a​s​e​_​i​n​f​o​ ​{​
 ​ ​ ​ ​u​i​n​t​3​2​_​t​ ​ ​ ​ ​n​u​m​e​r​;​
 ​ ​ ​ ​u​i​n​t​3​2​_​t​ ​ ​ ​ ​d​e​n​o​m​;​
}​;​

mach_timebase_info() fills in the struct with the fraction to multiply the result of mach_absolute_time() by to calculate nanoseconds. Multiply the result of mach_absolute_time() by numer and divide by denom.

Example 10.2 shows how to use these two functions. The code times how long it takes to call mach_timebase_info() and printf(). For real-life code, you would want to put something more interesting in there to time.

Example 10.2. machtime.m

/​/​ ​m​a​c​h​t​i​m​e​.​m​ ​-​-​ ​e​x​e​r​c​i​s​e​ ​m​a​c​h​_​a​b​s​o​l​u​t​e​_​t​i​m​e​(​)​

#​i​m​p​o​r​t​ ​<​m​a​c​h​/​m​a​c​h​_​t​i​m​e​.​h​>​ ​ ​/​/​ ​f​o​r​ ​m​a​c​h​_​a​b​s​o​l​u​t​e​_​t​i​m​e​(​)​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​a​b​o​r​t​(​)​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​m​a​c​h​t​i​m​e​ ​m​a​c​h​t​i​m​e​.​m​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​u​i​n​t​6​4​_​t​ ​s​t​a​r​t​ ​=​ ​m​a​c​h​_​a​b​s​o​l​u​t​e​_​t​i​m​e​ ​(​)​;​

 ​ ​ ​ ​m​a​c​h​_​t​i​m​e​b​a​s​e​_​i​n​f​o​_​d​a​t​a​_​t​ ​i​n​f​o​;​

 ​ ​ ​ ​i​f​ ​(​m​a​c​h​_​t​i​m​e​b​a​s​e​_​i​n​f​o​ ​(​&​i​n​f​o​)​ ​=​=​ ​K​E​R​N​_​S​U​C​C​E​S​S​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​s​c​a​l​e​ ​f​a​c​t​o​r​ ​:​ ​%​u​ ​/​ ​%​u​\​n​"​,​ ​i​n​f​o​.​n​u​m​e​r​,​ ​i​n​f​o​.​d​e​n​o​m​)​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​m​a​c​h​_​t​i​m​e​b​a​s​e​_​i​n​f​o​ ​f​a​i​l​e​d​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​b​o​r​t​ ​(​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​u​i​n​t​6​4​_​t​ ​e​n​d​ ​=​ ​m​a​c​h​_​a​b​s​o​l​u​t​e​_​t​i​m​e​ ​(​)​;​
 ​ ​ ​ ​u​i​n​t​6​4​_​t​ ​e​l​a​p​s​e​d​ ​=​ ​e​n​d​ ​-​ ​s​t​a​r​t​;​
 ​ ​ ​ ​u​i​n​t​6​4​_​t​ ​n​a​n​o​s​ ​=​ ​e​l​a​p​s​e​d​ ​*​ ​i​n​f​o​.​n​u​m​e​r​ ​/​ ​i​n​f​o​.​d​e​n​o​m​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​e​l​a​p​s​e​d​ ​t​i​m​e​ ​w​a​s​ ​%​l​l​d​ ​n​a​n​o​s​e​c​o​n​d​s​\​n​"​,​ ​n​a​n​o​s​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​

}​ ​/​/​ ​m​a​i​n​

And here it is in action:

$​ ​.​/​m​a​c​h​t​i​m​e​
s​c​a​l​e​ ​f​a​c​t​o​r​ ​:​ ​1​ ​/​ ​1​
e​l​a​p​s​e​d​ ​t​i​m​e​ ​w​a​s​ ​5​5​0​5​5​ ​n​a​n​o​s​e​c​o​n​d​s​
$​ ​.​/​m​a​c​h​t​i​m​e​
s​c​a​l​e​ ​f​a​c​t​o​r​ ​:​ ​1​ ​/​ ​1​
e​l​a​p​s​e​d​ ​t​i​m​e​ ​w​a​s​ ​9​5​3​6​3​ ​n​a​n​o​s​e​c​o​n​d​s​
$​ ​.​/​m​a​c​h​t​i​m​e​
s​c​a​l​e​ ​f​a​c​t​o​r​ ​:​ ​1​ ​/​ ​1​
e​l​a​p​s​e​d​ ​t​i​m​e​ ​w​a​s​ ​4​6​8​3​9​ ​n​a​n​o​s​e​c​o​n​d​s​

On this system, a 2010 Macbook Pro, the numerator and denominator of the conversion are both one. Some older machines, such as the original TiBook, had the numerator of the conversion at 1,000,000,000 and the denominator at 24,965,716, resulting in a scale value of 40.05. So there were about 40 nanoseconds for each increment of mach_absolute_time().

Outside of the second run, it takes about 50,000 nanoseconds, or 50 microseconds to do the work between the two timings. So what’s up with that middle run being twice as long as the others? When you are dealing with time values this short, anything can perturb them. Maybe some dynamic library lookup was necessary for that second run. Maybe iTunes was running and was loading a new track. Maybe Time Machine kicked in. For a real benchmark, you would run it for a longer period of time to hide those small one-time-only blips. And of course, you would run the benchmark a couple of times to get a good average and iron out the noise.

GUI Tools

Mac OS X comes with a number of GUI performance tools which are a good deal more powerful and easier to use than the command-line tools.

Activity Monitor

Each Mac OS X system includes the Activity Monitor application in the system’s Utilities folder.

Figure 10.6  Activity Monitor

Activity Monitor shows basic information for each process in the table view, and you can pop up the contextual menu to dig into lower-level information. The pane at the bottom of the window lets you see colorful charts and graphs concerning the system as a whole. The Sample Process button runs the sample command and puts the output into a window. Sampling is handy for seeing what a locked-up process is blocked on.

Instruments

Instruments is Apple’s suite of profiling tools. The Instruments application is itself a generic program allowing you to configure sets of individual tools, called instruments, to trace whatever information you specifically want to learn about your application. Many of Instruments’ instruments can profile the system as a whole in addition to focusing on a single process.

You can run Instruments directly from the Finder, or you can choose to Profile in Xcode. You can edit the Profile scheme in Xcode to pick a default template to use when profiling, or you can have Instruments prompt you on launch. You can also run the instruments command-line tool to record a performance profile without running the GUI.

Window layout

The Instruments user interface was inspired by Garage Band but without the wood-grain window treatment (bummer). Figure 10.7 shows a blank template with the Leaks and Allocations instruments already dragged over from the Library window.

Figure 10.7  Instruments and Library windows

The left side of the Instruments window holds the different instruments that will be used to record profile activity. The track pane in the middle is where profile data will be displayed. The bottom part of the window shows more detailed information about a recorded trace, described a bit later.

The Toolbar

The toolbar at the top of the window has controls for recording a profile, controls for pausing it, and a loop control used when playing back user interface actions.

The Target menu selects the trace target for the document. The target tells Instruments which process or processes to gather information about. “All Processes” tells Instruments to collect system-wide information. Not all instruments support system-wide data collection.

The inspection range controls let you narrow your focus to a particular span of time. Perhaps you have to fiddle with your application a bit before you get to the part you really want to profile. You can adjust the inspection range to exclude the fiddly part. To set the inspection range, you move the playback head to the beginning of your interesting time range and click the left-hand Inspection Range button. Then move the playback head to the end of the range and click the right-hand button. Option-dragging in the trace area will also set the inspection range.

The LCD-style screen in the middle of the toolbar is the Time/Run control. It shows how much time has elapsed while recording or how long a completed trace is. The “Run X of Y” buttons let you navigate between multiple profiling runs done in the current session. The view buttons open and close various side panels, just like the similar-looking icons in Xcode do. From left to right, they control the instruments pane (left), the detail pane (bottom), and the extended detail pane (right).

Looking at memory leaks

Figure 10.8 shows the Instruments window after it has collected some data. The blank template was used with the Leaks and Allocations instruments dragged over from the library. The target application is Cycling Fusion’s ClassBuilder™ app, which I was working on at the time this chapter was being written.

Figure 10.8  Instruments window after running the application

Notice that the track pane has some content now. The Leaks instrument has a stacked bar chart with the number of leaks discovered (top) and the count of leaked bytes (bottom). The Allocations instrument shows the amount of allocated memory. The Leaks instrument takes memory snapshots at relatively large intervals, defaulting to ten seconds, which explains why the leak markers do not correspond to growth in the allocation trace.

Scrubbing the playhead back and forth in the timeline shows little “inspection flags,” tooltip windows that show data in the trace for that point in time. Figure 10.9 shows that the Leaks instrument found 29 individual leaks responsible for 4.62KB of leaked memory. The total application memory footprint due to memory allocation is about 2 megabytes.

Figure 10.9  Playhead detail popups.

The detail panel is visible at the bottom of the window. The Allocations tool is currently selected in Figure 10.8, so the details panel is showing information about the allocations, including some colored bar graphs that show current and total allocations for different sizes of memory blocks and different kinds of objects.

Now let’s figure out where the memory leaks actually happened. Figure 10.10 has the Leaks instrument selected. The details panel by default will show you everything it can, but you can narrow the timeline down with the Inspection Range to a particular span of time, and the details panel will update itself accordingly.

Figure 10.10  Detail panel showing leaked blocks

The bottom half of the window is broken up into two panes. The left pane holds various instrument-specific controls that can be used to modify the display in the details pane. They don’t modify the data that has been collected, just how it is presented.

There are two Xcode-style jump bars in the window. The one in the instrument pane lets you choose which instrument you are looking at. It currently displays the Leaks instrument, but you can get a list of all the instruments in the document by clicking on it. You can also click the instrument trace directly to change the contents of the detail pane. The detail pane on the right has a jump bar that lets you change the view of the data. As you dig deeper into the data, the jump bar will let you back out to a higher-level view.

The detail area shows a lot of information: the kind of leaked memory, whether it’s an Objective-C object or a chunk of memory, how many of those were leaked, how much memory was leaked, which library allocated the memory, and what the function or method actually caused the allocation.

Even though Foundation and CoreGraphics are listed as the responsible libraries, this does not mean the library itself is mishandling memory. It just means that Foundation or CoreGraphics allocated memory on behalf of the code that ultimately caused the leak. There are several graphics-related classes and libraries in the leak list: UIBezierPath, CGPath, and the CoreGraphics framework. The leak is probably graphics-related.

Figure 10.11 shows that the disclosure triangle next to UIBezierPath has been clicked, revealing a list of individually leaked objects. There are 11 objects for a total of 704 bytes. The arrow-in-a-circle button, properly called the focus button or the follow link button, replaces the contents of the detail pane with additional information about the object you are focusing on. Clicking on the focus button next to the first UIBezierPath takes you to Figure 10.12, which has the allocation history for memory located at address 0x1c3850. Over the course of time, some strings have lived here, an NSThread private class, a CFData, and some hash tables. The last allocation is most interesting one: a malloc() that was never freed. This is one of the leaks.

Figure 10.11  Individual leaked memory blocks

Figure 10.12  Memory allocation history

The extended detail panel was opened by clicking on the right-hand View icon. The panel slides in from the right and shows a stack trace relating to what is selected in the detail view. Selecting the bottom entry from the allocation history will show the stack trace in Figure 10.13. The stack trace has library classes grayed out with the application’s classes, or at least those classes and functions that have debug symbols, in black. It is pretty obvious who the culprit is.

Figure 10.13  Object leak stack trace

Most of the time you do not really care what toolbox classes are on the stack. They are often just implementation detail noise. You can use the slider at the bottom to pare down the stack to more interesting stack frames, typically those at the boundaries of libraries. Figure 10.14 shows the full stack trace on the left with the reduced stack trace on the right.

Figure 10.14  Full and reduced stack traces

-[GRRideProfileView updateCurveMetrics] is the source of the leak. To actually see the code, double-click its entry in the stack trace. The details panel will show you the code, as shown in Figure 10.15.

Figure 10.15  Leak with source code

The code allocates and initializes a UIBezierPath and then never releases it. This is a classic Cocoa memory leak, which oddly enough did not get caught by the static analyzer. Notice that Instruments highlights two lines in the source code: the alloc/init leaked 4.25KB, while -addLineToPoint: leaked another 1.56KB of memory. You may be able to see a tiny dot in the trough of the scrollbar, just below the thumb. That shows there is another leak in the source file.

Earlier, Figure 10.11 indicated that UIBezierPath leaked 704 bytes in 11 objects, and now the source listing is saying it has leaked 4.25KB. Why the discrepancy? The 704 bytes was memory leaked just for UIBezierPath instances, not for any helper objects or additional blocks of memory they might have used. Objects tend to use lots of other objects, so the actual amount of leaked memory from this kind of error is actually much greater.

More Memory: Heapshots

Apple has a lot of memory-related instruments and features, mainly because memory issues are common when using C-based languages. Heapshots are another way of looking at and analyzing memory usage patterns.

The Leaks instrument is useful for finding memory and object leaks in your code, as you saw earlier. But sometimes your program’s memory usage grows without objects being leaked in the classic sense: having a non-zero reference count and nobody pointing to them. Sometimes objects can still be retained and pointed to but are being held onto unnecessarily. Perhaps something is being cached too aggressively. Maybe an object was put into a collection and then forgotten. Apple calls these “abandoned” objects rather than “leaked” objects. In a way, they are worse than leaked objects because it is not obvious they are sitting around unused.

The Allocations instrument’s Mark Heap button lets you take a snapshot of the current state of the dynamic allocation heap, shown in figure Figure 10.16. The first click tells Instruments to take a baseline reading of your memory usage. Subsequent clicks will add an entry into the details pane, which shows any new objects added to the heap.

Figure 10.16  Heapshot button

To use heap shots, warm up your application and take a baseline. Then perform an operation, ideally one which should leave the state of the world untouched, and then mark the heap again. There should be good reasons for any leftover objects. If not, you probably have memory being abandoned.

Instruments puts a little flag into the timeline when you take a heapshot, shown in Figure 10.17. Clicking a flag displays a pop-up window containing more details, although the heapshot flags do not have any additional information. Other instruments also add flags to indicate interesting events, and you can add your own by Option-clicking in the timeline.

Figure 10.17  Heapshot flags in the timeline

Another use for heapshots is to sanity-check your memory usage. Take a baseline, navigate to part of your app, and take another snapshot. Evaluate memory usage to make sure it is sane. For example, you might have a screen that displays 20 different objects that your app edits, like your stuffed animal collection. A heapshot showing 20 or 40 new strings makes sense, with a title for each object, and maybe some extra description. If you end up having 200 or 400 new strings, you might have a problem.

Figure 10.18 shows a ClassBuilder run that has had a baseline and two heapshots made. These correspond to the three flags shown in Figure 10.17. The baseline was made after the application had launched and was sitting idle, and then an operation was performed and another heapshot taken. This had 373KB of heap growth and 8000 new objects (yikes!). The operation was performed again, another heapshot taken, with a growth of 6KB and 100 new objects.

Figure 10.18  List of heapshots

That is quite a disparity between the two operations.

Figure 10.19 shows the first heapshot expanded. 272 out of 373KB were allocated as “non-objects,” which are chunks of memory obtained by malloc(). Expanding the disclosure triangle shows a list of the blocks of memory that are new.

Figure 10.19  Blocks left over after first run

Clicking the focus button on the “<non-object>” line shows more detail, as shown in Figure 10.20. You can see that there are a lot of font-related calls. 12KB for some glyphs, 8KB for more glyphs, and so on. The bulk of the new memory being consumed looks like it is related to the fonts being used in the ClassBuilder feature that was being exercised. It’s good to look at your code (and run the static analyzer again) to make sure you’re not over-retaining font objects. In this case, it looks like that this memory growth is due to one-time font usage by the toolkit, so it is not an application problem.

Figure 10.20  List of leaked non-object blocks

The second heap shot is more reasonable, weighing in at about 6 KB. The complete set of new objects is shown in Figure 10.21. There are 96 new non-objects for 5.76 KB, a GSEvent which is a low-level version of a UIEvent, a couple of event-related hash tables, and some core animation layer stuff. Following the focus button on the non-objects shows the screen in Figure 10.22

Figure 10.21  Leftover blocks from second heapshot

Figure 10.22  Leaking layers stack trace

The top 16 objects come from the Quartz drawing frameworks, so they may be related. When you select multiple objects in a detail view, Instruments tries to determine a common stack trace, as shown on the right. Clicking each individual memory allocation shows a similar stack, with a lot of Core Animation calls lying around, and things that are called by a runloop timer function.

The particular feature exercised in the app does not directly use Core Animation, but there were animations happening as UINavigationController moved views around. So these new objects are considered to be leftover implementation details from the toolkit animations and not a sign of imminent doom. Perhaps the user took a heapshot before the animation had completed, or the toolkit is keeping layers around as an optimization for the next time an animation is made.

Time Profiler

The Time Profiler instrument measures CPU usage by your program and, optionally, across the entire system. The time profiler samples your program or the system on a regular basis, measured in milli- or micro- seconds, and records the stack traces it finds. Once you have accumulated enough of these stack traces, you can see what functions and methods appear on the stacks most often. This is the code that is taking the most time. If you are tracking down a performance bottleneck, this is a good place to start looking.

Launching Instruments from Xcode and choosing the Time Profiler template only profiles your application. A lot of times this is sufficient because performance problems, such as inefficient algorithms and just doing too much work, can be found. However, iOS, like Mac OS, is based on multiple cooperating processes, and many of Apple’s frameworks are actually just thin communication layers that talk to background daemons. Sometimes work you do in your application can lead to extra work done by other processes, and sampling system-wide is necessary track down problems. You select system-wide profiling with the Target menu, shown in Figure 10.23.

Figure 10.23  Profiling all processes

ClassBuilder once again is the victim, this time to the Time Profiler instrument. One of the features of the program is to build an exercise class based on an iTunes playlist, and the instructor building a class needs to know how long each playlist is in order to pick an appropriate one. Unfortunately, as of iOS 4, the Media Player framework does not provide playlist duration as a built-in attribute, so ClassBuilder has to iterate over every song in a playlist, get the song duration, and sum them.

This takes a considerable amount of time in practice, blocking the main thread long enough for the watchdog timer to kill the app if the user has a lot of playlists. The time profiler helped show what was going wrong. Figure 10.24 shows a run with the trace showing some CPU activity at the beginning as the ClassBuilder UI was used to get to the screen where the playlist durations were calculated. You can see the CPU is pegged after the playlists start calculating.

Figure 10.24  Time Profiler instrument sample run

The detail view has some very interesting information in it even before you look at stack traces. ClassBuilder was responsible for about 55% of the sampled time, and the MobileMusicPlayer process accounted for 40%. That was unexpected. SpringBoard took some time as well, which is understandable because SpringBoard is responsible for compositing graphics on the screen. (The playlists, and the percentage of the playlist being calculated, were being displayed in a UITableView as their durations were calculated.)

It is interesting that MobileMusicPlayer is consuming so much processing power. One hypothesis is that the information for each song is stored in that process, and the Media Player framework uses interprocess communication to fetch the duration. There is no symbol information available for system processes so we can really only get detailed information from our own process. Expanding ClassBuilder’s disclosure triangle shows the call tree as shown in Figure 10.25.

Figure 10.25  Time Profiler call tree

By default Instruments shows you the call tree “inverted.” You see a method or function that has been “charged” with samples. Expanding the disclosure triangle for a particular call, say objc_msgSend, shows you the functions which make that call.

Figure 10.26 shows objc_msgSend expanded. You see a list of methods that have called objc_msgSend(), such as one from UITableViewUpdateSupport and another from GRPlaylistOperation, one of the ClassBuilder classes. You can then see what methods called them, and so on.

Figure 10.26  Expanding objc_msgSend to see its callers

A regular, or “uninverted,” call tree shows the top-level functions, such as main, and gives you the calling hierarchy, as shown in Figure 10.27. Here you can see main() called UIApplicationMain, which called UIApplication’s _run, and so on. Typically, the inverted call tree is more useful for sussing out performance issues.

Figure 10.27  Regular call tree

There is a lot of noise in this function list and too much low-level detail. To remove some of the noise, you can apply data mining, which “charges” time spent in a function to its callers. This removes lower-level details while giving you a better high-level overview. The two most useful forms of data mining are Hide System Libraries and Show Obj-C Only.

Hide System Libraries charges all code run in libraries to the functions that called into the libraries. If -drawGroovyness ended up spending four seconds in Core Graphics over the life of the trace, then after you click Hide System Libraries, -drawGroovyness will be shown as having consumed four more seconds, and all the Core Graphics calls will disappear. You might not be interested in the gory details of what Core Graphics spent its time on, but knowing that -drawGroovyness consumed a lot of processor time is information you can use to figure out what to optimize. Perhaps the method is just drawing too often or drawing a larger area than needed.

Figure 10.28 shows this trace with system libraries hidden. What’s left is pretty much just application classes and functions. At the top is GRMediaItem’s -duration call. This jives with the hypothesis that fetching the song’s durations is what is causing the performance problems.

Figure 10.28  Data mining by hiding system libraries

The other primary data mining setting is Show Obj-C Only. It removes all regular function calls, leaving Objective-C method calls. These calls will be from your application and Apple’s frameworks. Figure 10.29 shows the call tree after turning on Show Obj-C Only, with a couple of entries having their disclosure triangles expanded. The top methods, the ones that take the most time, look to be IPC related: property list serialization called by a CPDistributedMessagingCenter. NSArchiver was called by MPMediaPropertyPredicates and MPMediaQueries, along with CPDistributedMessagingCenter. This last class is an Apple private class that deals with IPC, as indicated by the name.

Figure 10.29  Data mining by showing Objective-C only

The original hypothesis of the time being consumed by IPC appears to be correct. Even though it seems like the application’s hands are tied with regards to performance, further investigation showed that with real-life playlists created by instructors, there were a lot of repeated songs, e.g. favorite songs for warmup or cooldown. The durations of these songs were queried over and over again, triggering more IPC. The application already had a wrapper class around MPMediaItems to allow it to run in the simulator, so a cache was added for the song duration which reduced the amount of time calculating the playlists. Moving the calculations to a background thread using NSOperation made the user interface responsive again. As playlist durations were calculated, the main thread was informed, which then updated the UI.

Other Instruments

There are many more instruments that come with Instruments, and Apple is adding new ones regularly. Some instruments work on both iOS and Mac OS X, and some work on just one or the other. Instruments will complain if you try to use an instrument that is not appropriate for a given platform. Here is a sampling of some of the available instruments.

Memory

In addition to the Leaks and Allocations instruments you saw earlier, Instruments has a number of other memory-related instruments. These allow you to peek inside the garbage collector, see the virtual memory consumption of a process, track shared memory creation and destruction, and detect zombies, which are over-released objects that are subsequently messaged.

Automation

The Automation instrument is used to run test scripts against an application on a device. You write a JavaScript test script which drives the application, and then the Automation instrument uses the accessibility features of CocoaTouch to introspect and control the application. The scripts can test the running application and say “yes, the app passed this test” or “no, the app failed this test.” The test results will show up in the Instruments trace.

Power

Power consumption is very important for iOS users. A user is cut off from the outside world if the device’s battery dies in the middle of the day. Screen brightness, CPU usage, and the various radios (Cell, WiFi, BlueTooth, GPS) contribute to power drain on the device. The Energy Diagnostics template includes seven instruments, all of which are power-related. The Energy Usage instrument utilizes sensors close to the battery and shows the power drain. The CPU Usage instrument shows how much CPU is being used in four different classes of activity: graphics, audio processing, the foreground app, and the rest of the system. Graphics compositing happens in the Springboard process, and the media daemon handles audio.

The Display Brightness instrument shows how bright the screen is, based on user settings. You can use this to compare two different program runs and make sure the screen brightness is the same when comparing the power consumption. Sleep/Wake shows the transitions the device makes between sleeping and waking states.

Three radio-related instruments show the state of the Bluetooth, WiFi, and GPS radios. WiFi and Bluetooth show red when the user has configured them to be on, but these do not actually show when the radio is in use or not. GPS shows on/off state of the GPS receiver. If you run an app that uses Core Location and the framework decides to turn on the GPS, the GPS instrument will show red during those times. At this time, there is no instrument for the cellular network radio.

Important tip: you don’t want to try to get an energy usage trace while the device is connected to a computer over USB. The battery will be charging causing your energy usage numbers to look absurdly low. You should turn on Power Logging in the Developer section of the Settings app and exercise the device. When you’re done, hook up the device, open an Energy Diagnostics template, and choose Import Energy Diagnostics from Device from the File menu.

Figure 10.30 shows the various power-related instruments after running a trace. You can drag the playhead around to show the tooltip windows in the track area. You can see that the Bluetooth radio was turned off in the middle, the GPS was never on, and the display brightness was cranked all the way up.

Figure 10.30  The Power-oriented instruments

The Energy Usage instrument shows power consumption at the battery level, on a scale of 1 to 20. Apple states that the time to discharge a full battery at a power drain of 1 would be 20+ hours, a level of 10 would discharge a full battery in 10 hours, and a level of 20 would take less than one hour.

Finally the CPU Activity trace shows that where the playback head is positioned 0.3% of the CPU was spent on audio, 14.7% on graphics, the foreground app’s processing is another 24.7%, for a total of 39.7% of the CPU. The total activity on the system was 59.3%, so other processes on the system account for 19.6% of the CPU usage. You could use the Time Profiler to see what the other parts of the system were doing.

Graphics

The Core Animation instrument shows you the current frames per second (FPS) that Core Animation is currently producing. In general, you want your FPS to be high, from 30 FPS up to the maximum of 60 FPS. The instrument reports the number of frames actually completed, not the total frame rate. For example, if your app produces 30 frames in half a second (60 FPS) and then stops, it will be reported as 30 FPS because only 30 frames were completed. Figure 10.31 shows a trace where the application had good animation performance, then a fairly low level. In this case, the application wasn’t animating anything. Towards the end, you can see the app struggling at 15 FPS. The Core Animation template includes both the Core Animation and a Sampler instrument, allowing you to correlate CPU usage with animation speed issues.

Figure 10.31  Core Animation instrument

Core Animation also includes a Debug Options panel, as shown in Figure 10.32. The settings in this panel do not affect the data being collected but instead tell the device to do things to help you track down graphics performance issues. The first option, Color Blended Layers, is the option you will probably use most often. This puts a red overlay on layers that were drawn using blending, such as images with alpha and views that are marked as non-opaque. All other layers are drawn with a green overlay, as shown in Figure 10.33

Figure 10.32  Core Animation instrument debug options

Figure 10.33  Core Animation instrument blended layer overlays

It may be difficult to tell in black and white printing, but the majority of the UITableView in the figure does not have any drawing issues, while the header of the screen has a number of views that are drawn non-opaquely. UILabels are a pretty common source of blending warnings because they are non-opaque when dragged out of the Interface Builder library. You want to avoid non-opaque views in general on iOS. The system has to do extra compositing to draw them correctly. Opaque views can just be blasted straight to the screen without worrying about what is underneath them.

In addition to the Core Animation instrument, the OpenGL / OpenGLES Driver instruments give you a peek at your usage of OpenGL. Be sure to expand the extended detail panel on the right to get to all of the information the instruments are providing.

The OpenGLES Analyzer looks at your OpenGL usage, displays information in the track such as the number of GL calls, number of triangles rendered, and redundant state changes. The detail view has a set of warnings, telling you about things like redundant state calls, or if you are submitting vertices without using a VBO. The extended detail pane shows a textual recommendation, as well as the stack trace for the offending statement. This is shown in Figure 10.34.

Figure 10.34  The OpenGL Analyzer

File System

The File Activity instrument traces when files are opened, closed, or statted, as well as when sockets and pipes are created. The Reads/Writes instrument tells you where all your reads and writes happen as well as how many bytes are actually read or written. It records the path (or socket) where the activity happened. File Attributes lets you know when any attributes of the file changed with the chown(), chmod() calls, and when they are changed indirectly, such as open() changing the last accessed time. Directory I/O tells you about directory activity, such as directory creation and unlinking, renaming, etc. Disk Monitor shows a trace of bytes read/written per second, as well as read/write operations per second.

System Details

There are also a couple of instruments that monitor low-level OS details. The Activity Monitor instrument records system activity, much like the Activity Monitor application. The Scheduling instrument records context switches for threads and processes and tells you the reason the thread was scheduled or unscheduled, such as pre-emption, trapping into the kernel, yielding the CPU due to blocking, etc. The System Calls instrument records system calls and how long they took (cpu and wait time) as well as process interruptions. VM Operations tracks low-level memory operations such as copy on write, page cache hit, zero fill, paging in from a file, and so on.

These instruments include a highlight view which shows aggregate data in bar and pie chart format, shown in Figure 10.35. This trace was recorded while some photos were being edited in Adobe’s Lightroom and Photoshop CS5 applications. You can see information such as real memory usage by application, how many context switches and system calls were performed per application, the kinds of system calls, and so on. You can click on a larger chart to see the details for that particular instrument.

Figure 10.35  Highlight views

The Dispatch instrument tracks the lifetime of queues, as well as blocks dispatched on queues and how long they take to run. And finally, the Network Activity instrument shows how many bytes and packets the system is reading and writing.

Symbol Trace and DTrace

You can trace calls to an individual function or method as well. Choose Trace Symbol from the Instrument menu, and Instruments will ask you for a symbol to trace. Type a function or method name here. Then when you start recording a trace, Instruments will show the stack depth in the trace area every time that code is called.

Figure 10.36 shows a symbol trace for -[NSTableView drawRect:]. A vertical line was added to the trace as a table view was clicked in the program. You can see a call tree of when these were called, as well as seeing stack traces in the extended detail panel. Symbol traces are useful for answering questions like “is this function even getting called?” and “is this function getting called an insane number of times?”, and they can be used to establish visible markers in the trace area. If you know a particular function or method is called right before a performance problem, a symbol trace will give you visible markers where you can start narrowing down your focus. You can click on an entry in the event list to set the playhead to the point in time when that call was made. You can then set the beginning or ending of the inspection range to exactly the time of that call, helping cut out extraneous information with precision.

Figure 10.36  Tracing table view drawing

Symbol Trace is a shortcut for creating a DTrace instrument. Double-clicking the tableview symbol trace instrument drops the sheet shown in Figure 10.37, or you could choose Build New Instrument from the Instrument menu. The panels and text fields there should look familiar if you have used DTrace before. Symbol Trace has created a DTrace probe for objc:NSTableView:-drawRect*:entry that will trigger when a table view draws. The action sets a timestamp and jots down the receiver of the message. There is also a :::return probe that will record the object and stack trace, as well as the duration of the call back to Instruments for display and analysis.

Figure 10.37  DTrace instrument configuration

Much of what you can do in DTrace you can do in Instruments as well and have it appear in the same trace pane as Apple’s instruments. Unfortunately, DTrace is not available on iOS devices as of iOS 4, so symbol traces and DTrace instruments are not available on that platform.

Summary

Performance is a never-ending game. The rules change constantly as new OS revisions and new hardware comes out. We are nearing the end of free performance gains from hardware, at least for applications that can only take advantage of one CPU. Efficiency in coding and in algorithms, as well as parallel processing, will become more and more important as time goes on. Luckily, we have a number of good tools to profile our code and highlight the areas where we should focus our attention.

Exercises

Profile your own program and find any unexpected hotspots.

Fix any unexpected hotspots

11
Files, Part 1: I/O and Permissions

Files are the permanent storage for the data that your programs generate. The verbs of handling files are similar to most every other common platform: open, read, write, seek to a particular location, close, with some added nuances that live in the details.

The idea of a file is a fundamental Unix concept. Pretty much everything is treated as a file. Ordinary “write the bits for a jpeg photo” go to files in the file system. Network connections are treated as files. Manipulating some physical devices is done with file operations. Interacting with the terminal is done the same way. Many forms of interprocess communication happen through logical files. This is pretty nice, being able to use the same API for reading and writing data to all of these disparate machine entities. The downside is that there is some additional complexity in the API to handle the different corner cases involved with accessing all of these OS features.

Unbuffered I/O

The unbuffered I/O APIs are the fundamental system calls upon which other interfaces, like the buffered I/O ones, can be built. A system call is a function that goes through the kernel to do its work. System calls are much more expensive than regular function calls because of the travel necessary through the application / kernel boundary.

Opening a file

open opens a file:

int open (const char *path, int flags, mode_t mode);

path specifies the path in the file system that locates the file to be opened, such as /Users/markd/quotes.txt. There are a couple of limitations on the size of the path: the total length of the path cannot exceed the constant PATH_MAX (currently 1024 for Mac OS X 10.6 and earlier) and any individual component of the path cannot be longer than NAME_MAX (currently 255). If you exceed either of these limits, open() and any other system call that takes a path will return an error value and set errno to be ENAMETOOLONG.

The mode argument indicates the default permissions to use for newly created files, e.g., which person has authority to read and write to the file. The details will be discussed a little later. flags is a bitfield used to control the behavior when opening the file. Bitwise-OR in one of these flags:

O_RDONLY | Open the file read-only.
O_WRONLY | Open the file write-only.
O_RDWR | Open the file so you can read from it and write to it.

and any of these flags:

O_APPEND | Append on every write. This does an implicit seek to the end before writing, but it is atomic.
O_NONBLOCK | Do not block when opening and do not block when waiting for data.
O_CREAT | Create the file if it does not exist.
O_TRUNC | Truncate the file to zero bytes when it is opened.
O_EXCL | Return an error if the file already exists.
O_SHLOCK | Obtain a shared lock when opening the file.
O_EXLOCK | Obtain an exclusive lock (more on locks later).

On error, open() returns -1 and sets the errno global variable to the appropriate value. This “returning -1 and setting errno to an error” pattern is common to most of the file I/O functions but, unfortunately, not all.

A successful open() returns a non-negative integer called a file descriptor, frequently shortened to just fd. This integer value is the index into a per-process table that references open files. This file descriptor is used as a handle to the file for subsequent calls.

The three standard streams that shells establish for programs are located at file descriptor values zero (for standard in), one (for standard out), and two (for standard error). You can pass these numbers to the read() and write() functions, or you can use the symbolic constants STDIN_FILENO, STDOUT_FILENO, or STDERR_FILENO, as shown in Figure 11.1.

Figure 11.1  File descriptors

The file descriptor of newly-opened files is guaranteed to be the lowest numbered unused descriptor. This can be useful when you are reopening a file on one of the standard streams, although the dup2() call is better for that.

Example 11.1 closes stdout and reopens it to a logfile set in append-only mode. printf() writes to standard out, so it will log to this file.

Example 11.1. append.m

/​/​ ​a​p​p​e​n​d​.​m​ ​-​-​ ​S​h​o​w​ ​a​n​ ​o​p​e​n​i​n​g​ ​o​f​ ​a​ ​l​o​g​f​i​l​e​ ​r​e​p​l​a​c​i​n​g​ ​a​ ​s​t​a​n​d​a​r​d​ ​s​t​r​e​a​m​.​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​a​p​p​e​n​d​ ​a​p​p​e​n​d​.​m​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​ ​a​n​d​ ​s​t​r​e​r​r​o​r​(​)​
#​i​m​p​o​r​t​ ​<​f​c​n​t​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​o​p​e​n​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​(​)​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​S​T​D​O​U​T​_​F​I​L​E​N​O​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​c​l​o​s​e​ ​(​S​T​D​O​U​T​_​F​I​L​E​N​O​)​;​

 ​ ​ ​ ​/​/​ ​o​p​e​n​ ​a​ ​l​o​g​ ​f​i​l​e​,​ ​w​r​i​t​e​ ​o​n​l​y​,​ ​a​n​d​ ​t​o​ ​a​l​w​a​y​s​ ​a​u​t​o​m​a​t​i​c​a​l​l​y​ ​a​p​p​e​n​d​.​
 ​ ​ ​ ​/​/​ ​o​h​,​ ​a​n​d​ ​c​r​e​a​t​e​ ​t​h​e​ ​f​i​l​e​ ​i​f​ ​i​t​ ​d​o​e​s​n​'​t​ ​e​x​i​s​t​ ​a​l​r​e​a​d​y​
 ​ ​ ​ ​i​n​t​ ​f​d​ ​=​ ​o​p​e​n​ ​(​"​/​t​m​p​/​l​o​g​t​h​i​n​g​i​e​.​t​x​t​"​,​ ​O​_​W​R​O​N​L​Y​ ​|​ ​O​_​C​R​E​A​T​ ​|​ ​O​_​A​P​P​E​N​D​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​_​I​R​U​S​R​ ​|​ ​S​_​I​W​U​S​R​)​;​
 ​ ​ ​ ​i​f​ ​(​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​a​n​'​t​ ​o​p​e​n​ ​l​o​g​ ​f​i​l​e​.​ ​ ​E​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​w​h​e​e​e​,​ ​w​e​ ​h​a​v​e​ ​a​ ​l​o​g​ ​f​i​l​e​ ​o​p​e​n​\​n​"​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

Here are some runs of the program:

$​ ​l​s​ ​-​l​ ​/​t​m​p​/​l​o​g​t​h​i​n​g​i​e​.​t​x​t​
l​s​:​ ​/​t​m​p​/​l​o​g​t​h​i​n​g​i​e​.​t​x​t​:​ ​N​o​ ​s​u​c​h​ ​f​i​l​e​ ​o​r​ ​d​i​r​e​c​t​o​r​y​

$​ ​.​/​a​p​p​e​n​d​

$​ ​l​s​ ​-​l​ ​/​t​m​p​/​l​o​g​t​h​i​n​g​i​e​.​t​x​t​
-​r​w​-​-​-​-​-​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​w​h​e​e​l​ ​ ​3​1​ ​A​u​g​ ​1​0​ ​1​1​:​5​5​ ​/​t​m​p​/​l​o​g​t​h​i​n​g​i​e​.​t​x​t​*​

$​ ​c​a​t​ ​/​t​m​p​/​l​o​g​t​h​i​n​g​i​e​.​t​x​t​
w​h​e​e​e​,​ ​w​e​ ​h​a​v​e​ ​a​ ​l​o​g​ ​f​i​l​e​ ​o​p​e​n​

$​ ​.​/​a​p​p​e​n​d​
$​ ​.​/​a​p​p​e​n​d​

$​ ​c​a​t​ ​/​t​m​p​/​l​o​g​t​h​i​n​g​i​e​.​t​x​t​
w​h​e​e​e​,​ ​w​e​ ​h​a​v​e​ ​a​ ​l​o​g​ ​f​i​l​e​ ​o​p​e​n​
w​h​e​e​e​,​ ​w​e​ ​h​a​v​e​ ​a​ ​l​o​g​ ​f​i​l​e​ ​o​p​e​n​
w​h​e​e​e​,​ ​w​e​ ​h​a​v​e​ ​a​ ​l​o​g​ ​f​i​l​e​ ​o​p​e​n​

That’s a lot of header files to include for such a small program. Function prototypes and data types are pretty scattered around in the headers. Luckily, most of the manpages for functions tell you what headers to include to use particular features. For others, like finding where STDOUT_FILENO lives, you can resort to just grepping the files:

$​ ​g​r​e​p​ ​S​T​D​O​U​T​_​F​I​L​E​N​O​ ​/​u​s​r​/​i​n​c​l​u​d​e​/​*​.​h​
u​n​i​s​t​d​.​h​:​#​d​e​f​i​n​e​ ​S​T​D​O​U​T​_​F​I​L​E​N​O​ ​1​ ​/​*​ ​s​t​a​n​d​a​r​d​ ​o​u​t​p​u​t​ ​f​i​l​e​ ​*​/​

If you don’t find a value in /usr/include, look in /usr/include/sys. You can also Command-double-click most symbols in Xcode and see the corresponding definition.

Example 11.1 also writes directly to standard error. You could not use printf() to complain about the error opening the file because standard out has been redirected. After all, the file opening failed.

There are also some unpronounceable constants used to supply the initial file permissions to use for the newly created file. These will be discussed shortly.

Writing to a file

Use the write() system call to move bytes from memory to the disk:

ssize_t write (int fd, const void *buf, size_t nbytes);

Give it an address and a number of bytes to write, and the bytes will make it out to the file.

write() returns -1 in the case of an error, and it sets errno to whatever complaint the system is having. Otherwise, it returns the number of bytes written. The number of bytes written could be less than nbytes, such as writing a big block of data to a network connection or to a pipe, where you could be filling up limited-sized kernel buffers. The sample code in this chapter will just call write() without a loop since we will just be sending small chunks of data to files or to the terminal. Chapter 14: Network Programming With Sockets will demonstrate the paranoid way of calling write().

Each open file has an offset associated with it. This offset is the location within the file where the next read or write operation will happen. You can think of it like the read/write head of a cassette player or a Turing machine. The offset is automatically updated on each read or write and can be explicitly set by lseek(). The O_APPEND flag overrides the offset for writes, forcing all writing to happen at the end of the file.

Example 11.2 uses write() to write a string provided on the command line out to a file. To add a little interest, it writes out the length of the string first. Example 11.3 will read the files generated by this program.

Example 11.2. writestring.m

/​/​ ​w​r​i​t​e​s​t​r​i​n​g​.​m​ ​-​-​ ​t​a​k​e​ ​a​r​g​v​[​1​]​ ​a​n​d​ ​w​r​i​t​e​ ​i​t​ ​t​o​ ​a​ ​f​i​l​e​,​ ​p​r​e​p​e​n​d​i​n​g​ ​t​h​e​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​l​e​n​g​t​h​ ​o​f​ ​t​h​e​ ​s​t​r​i​n​g​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​w​r​i​t​e​s​t​r​i​n​g​ ​w​r​i​t​e​s​t​r​i​n​g​.​m​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​f​c​n​t​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​o​p​e​n​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​i​n​t​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​u​i​n​t​3​2​_​t​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​ ​e​t​.​ ​a​l​.​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​(​)​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​t​a​t​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​e​r​m​i​s​s​i​o​n​ ​f​l​a​g​s​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​w​r​i​t​e​(​)​ ​/​ ​c​l​o​s​e​(​)​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​!​=​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​s​a​g​e​:​ ​ ​%​s​ ​s​t​r​i​n​g​-​t​o​-​l​o​g​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​i​n​t​ ​f​d​ ​=​ ​o​p​e​n​ ​(​"​/​t​m​p​/​s​t​r​i​n​g​f​i​l​e​.​t​x​t​"​,​ ​O​_​W​R​O​N​L​Y​ ​|​ ​O​_​C​R​E​A​T​ ​|​ ​O​_​T​R​U​N​C​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​_​I​R​U​S​R​ ​|​ ​S​_​I​W​U​S​R​)​;​

 ​ ​ ​ ​i​f​ ​(​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​a​n​'​t​ ​o​p​e​n​ ​f​i​l​e​.​ ​ ​E​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​W​r​i​t​e​ ​t​h​e​ ​l​e​n​g​t​h​ ​o​f​ ​t​h​e​ ​s​t​r​i​n​g​ ​(​f​o​u​r​ ​b​y​t​e​s​)​.​
 ​ ​ ​ ​u​i​n​t​3​2​_​t​ ​s​t​r​i​n​g​L​e​n​g​t​h​ ​=​ ​s​t​r​l​e​n​ ​(​a​r​g​v​[​1​]​)​;​
 ​ ​ ​ ​s​i​z​e​_​t​ ​r​e​s​u​l​t​ ​=​ ​w​r​i​t​e​ ​(​f​d​,​ ​&​s​t​r​i​n​g​L​e​n​g​t​h​,​ ​s​i​z​e​o​f​(​s​t​r​i​n​g​L​e​n​g​t​h​)​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​a​n​'​t​ ​w​r​i​t​e​ ​t​o​ ​f​i​l​e​.​ ​ ​E​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​w​r​i​t​e​ ​(​f​d​,​ ​a​r​g​v​[​1​]​,​ ​s​t​r​i​n​g​L​e​n​g​t​h​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​a​n​'​t​ ​w​r​i​t​e​ ​t​o​ ​f​i​l​e​.​ ​ ​E​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​c​l​o​s​e​ ​(​f​d​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​a​n​'​t​ ​c​l​o​s​e​ ​t​h​e​ ​f​i​l​e​.​ ​ ​E​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

Here it is in action:

$​ ​.​/​w​r​i​t​e​s​t​r​i​n​g​ ​"​I​ ​s​e​e​m​ ​t​o​ ​b​e​ ​a​ ​f​i​s​h​"​
$​ ​

Looking at the size of the file:

$​ ​l​s​ ​-​l​ ​/​t​m​p​/​s​t​r​i​n​g​f​i​l​e​.​t​x​t​ ​
-​r​w​-​-​-​-​-​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​w​h​e​e​l​ ​ ​2​3​ ​A​u​g​ ​1​0​ ​1​2​:​2​4​ ​/​t​m​p​/​s​t​r​i​n​g​f​i​l​e​.​t​x​t​

The file is 23 bytes, which is the exact size it should be: 4 bytes for the length, plus 19 for the text.

The hexdump program is useful for seeing inside of files:

$​ ​h​e​x​d​u​m​p​ ​-​C​ ​/​t​m​p​/​s​t​r​i​n​g​f​i​l​e​.​t​x​t​ ​
0​0​0​0​ ​ ​1​3​ ​0​0​ ​0​0​ ​0​0​ ​4​9​ ​2​0​ ​7​3​ ​6​5​ ​ ​6​5​ ​6​d​ ​2​0​ ​7​4​ ​6​f​ ​2​0​ ​6​2​ ​6​5​ ​ ​|​.​.​.​.​I​ ​s​e​e​m​ ​t​o​ ​b​e​|​
0​0​1​0​ ​ ​2​0​ ​6​1​ ​2​0​ ​6​6​ ​6​9​ ​7​3​ ​6​8​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​|​ ​a​ ​f​i​s​h​|​
0​0​1​7​

Here’s how you parse the output: the left-hand column is the number of bytes into the file that the particular line starts on. The bytes of hex are the contents of the file, 16 bytes per line. The right-hand columns show the ASCII interpretation, with unprintable characters replaced by periods.

This file is not portable between PowerPC and Intel machines due to differences in the way integers are represented for those processors. This issue is covered in more detail in Chapter 14: Network Programming With Sockets.

So, the first couple of bytes:

0​0​0​0​0​0​0​0​ ​ ​1​3​ ​0​0​ ​0​0​ ​0​0​

Starting at offset zero are four bytes of 13000000, which is hexadecimal 13, decimal 19, the length of the string. Following that are 19 bytes of the actual string data.

Reading from a file

Reading is the inverse of writing: it moves bytes from the file into memory. read() is nearly identical to write():

ssize_t read (int d, void *buf, size_t nbytes);

buf is not a const void *, but just a void *, meaning that the function could change the contents of the buffer. Of course, that is the whole point of this function call.

Like write, read() returns -1 on an error, setting errno as appropriate. For a successful read, it returns the number of bytes actually read, returning zero on end of file (EOF). If you run a program from an interactive terminal and read from standard in, some magic happens under the hood causing read() to read entire lines. read() can return fewer bytes than what you asked for. This can happen when the end of file is reached or due to networking buffering, reading from the terminal, or when dealing with record-oriented devices like tape drives. read() also updates the current offset.

Example 11.3 is the counterpart to writestring. It takes the specially-formatted file and reads it back in.

Example 11.3. readstring.m

/​/​ ​r​e​a​d​s​t​r​i​n​g​.​m​ ​-​-​ ​o​p​e​n​ ​/​t​m​p​/​s​t​r​i​n​g​f​i​l​e​.​t​x​t​ ​a​n​d​ ​w​r​i​t​e​ ​o​u​t​ ​i​t​s​ ​c​o​n​t​e​n​t​s​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​r​e​a​d​s​t​r​i​n​g​ ​r​e​a​d​s​t​r​i​n​g​.​m​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​ ​a​n​d​ ​s​t​r​e​r​r​o​r​(​)​
#​i​m​p​o​r​t​ ​<​f​c​n​t​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​o​p​e​n​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​i​n​t​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​u​i​n​t​3​2​_​t​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​ ​e​t​.​ ​a​l​.​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​(​)​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​r​e​a​d​(​)​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​f​d​ ​=​ ​o​p​e​n​ ​(​"​/​t​m​p​/​s​t​r​i​n​g​f​i​l​e​.​t​x​t​"​,​ ​O​_​R​D​O​N​L​Y​)​;​

 ​ ​ ​ ​i​f​ ​(​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​a​n​'​t​ ​o​p​e​n​ ​f​i​l​e​.​ ​ ​E​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​u​i​n​t​3​2​_​t​ ​s​t​r​i​n​g​L​e​n​g​t​h​;​
 ​ ​ ​ ​s​s​i​z​e​_​t​ ​r​e​s​u​l​t​ ​=​ ​r​e​a​d​ ​(​f​d​,​ ​&​s​t​r​i​n​g​L​e​n​g​t​h​,​ ​s​i​z​e​o​f​(​s​t​r​i​n​g​L​e​n​g​t​h​)​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​a​n​'​t​ ​r​e​a​d​ ​f​i​l​e​.​ ​ ​E​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​+​1​ ​a​c​c​o​u​n​t​s​ ​f​o​r​ ​t​h​e​ ​t​r​a​i​l​i​n​g​ ​z​e​r​o​ ​b​y​t​e​ ​w​e​'​l​l​ ​b​e​ ​a​d​d​i​n​g​ ​t​o​ ​t​e​r​m​i​n​a​t​e​
 ​ ​ ​ ​/​/​ ​t​h​e​ ​s​t​r​i​n​g​.​
 ​ ​ ​ ​c​h​a​r​ ​*​b​u​f​f​e​r​ ​=​ ​m​a​l​l​o​c​ ​(​s​t​r​i​n​g​L​e​n​g​t​h​ ​+​ ​1​)​;​

 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​r​e​a​d​ ​(​f​d​,​ ​b​u​f​f​e​r​,​ ​s​t​r​i​n​g​L​e​n​g​t​h​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​a​n​'​t​ ​r​e​a​d​ ​f​i​l​e​.​ ​ ​E​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​b​u​f​f​e​r​[​s​t​r​i​n​g​L​e​n​g​t​h​]​ ​=​ ​'​\​0​0​0​'​;​

 ​ ​ ​ ​c​l​o​s​e​ ​(​f​d​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​t​h​e​ ​s​t​r​i​n​g​ ​i​s​ ​'​%​s​'​\​n​"​,​ ​b​u​f​f​e​r​)​;​

 ​ ​ ​ ​f​r​e​e​ ​(​b​u​f​f​e​r​)​;​ ​/​/​ ​c​l​e​a​n​ ​u​p​ ​o​u​r​ ​m​e​s​s​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

And the program in action:

$​ ​.​/​r​e​a​d​s​t​r​i​n​g​
t​h​e​ ​s​t​r​i​n​g​ ​i​s​ ​'​I​ ​s​e​e​m​ ​t​o​ ​b​e​ ​a​ ​f​i​s​h​'​

You may notice that open() is called with only two arguments. You do not need to supply permissions if you are not creating a file.

Closing files

To close a file, you use close():

int close (int fd);

This removes the file descriptor from the per-process file table and frees up system resources associated with this open file. This has a return value of -1 to indicate error. Otherwise, it returns a zero on successful completion.

In the above samples, readstring did not check the return value from close() because the tool had already done everything it wanted to with the file and its contents. The file being closed successfully (or not) is uninteresting. writestring checks for success on the close because some file systems, especially networked file systems, might not flush the last of the file’s data until the file is closed.

All open files are automatically closed when a process exits, and any data blocks waiting in kernel buffers are queued for writing.

Changing the read/write offset

The read/write offset is the number of bytes from the beginning of the file where the next read or write operation will take place. This offset is an attribute of the open file. Opening the same file twice will give you two file descriptors, each descriptor having an independent file offset. The initial offset is zero unless O_APPEND was used to open the file.

Use lseek() to change the offset:

off_t lseek (int fd, off_t offset, int whence);

offset, in combination with whence, is used to locate a particular byte in the file. whence can have one of three values:

SEEK_SET | offset is an absolute position.
SEEK_CUR | offset is a delta from the current location.
SEEK_END | offset is relative from the end of the file.

The offset can be negative for SEEK_CUR and SEEK_END. So, if you wanted to start writing five bytes from the end of the file, you would do:

b​l​a​h​ ​=​ ​l​s​e​e​k​ ​(​f​d​,​ ​-​5​,​ ​S​E​E​K​_​E​N​D​)​;​

Note that the offset itself can never go negative; it is clamped to zero. You can see different seeking scenarios in Figure 11.2.

Figure 11.2  lseek()

The return value from lseek() is the resulting offset location in bytes from the beginning of the file. Otherwise, a -1 return value and errno are used to communicate back an error.

You are quite welcome to seek off the end of the file. Any bytes not explicitly written will default to zero. Note also that some devices are incapable of seeking, like a pipe or a network connection. lseek() is a not a physical operation. It’s just updating a value in the kernel, so no actual I/O takes place until you perform a read or write.

Move a delta of zero from the current position to get the current offset:

o​f​f​_​t​ ​o​f​f​s​e​t​ ​=​ ​l​s​e​e​k​ ​(​f​d​,​ ​0​,​ ​S​E​E​K​_​C​U​R​)​;​

This will also tell you if the device is capable of seeking. If you get an error, you know you cannot seek with this kind of file.

Why is it called lseek()? Back in the misty past, the function to perform this work was originally just called seek, but it took an int parameter, which could be 16 or 32 bit. When seek was extended to support larger files, a long argument was used, hence the “L.”

Atomic operations

In manpages and around this chapter, you will see references to “atomic operations.” These are operations that perform multiple actions, but they all happen within one system call, thereby preventing race condition errors with other programs.

For example, open() has the O_APPEND flag, which makes appending data to the file an atomic operation. You just write() to the file descriptor, and the output appears at the end.

Without O_APPEND, you’d have to make two calls:

l​s​e​e​k​ ​(​f​d​,​ ​0​,​ ​S​E​E​K​_​E​N​D​)​;​ ​/​/​ ​s​e​e​k​ ​t​o​ ​t​h​e​ ​e​n​d​
w​r​i​t​e​ ​(​f​d​,​ ​b​u​f​f​e​r​,​ ​d​a​t​a​s​i​z​e​)​;​

The race condition happens if your program gets pre-empted by the kernel after the lseek() but before the write(), and some other process happens to be writing to the file as well.

Figure 11.3  Race condition start

Figure 11.4  Program A writes its data

Figure 11.5  Program B clobbers it

So say the end of log.txt looks like

f​i​n​i​s​h​e​d​ ​f​r​o​b​u​l​a​t​i​n​g​ ​t​h​e​ ​b​o​r​k​\​n​a​d​d​e​d​ ​a​ ​h​o​o​v​e​r​\​n​

and the last position is at offset 50,000.

Program A does an lseek() to 50,000 and gets pre-empted.

Program B does an lseek() to 50,000 and writes "removed the wikkit\n".

Program A is scheduled again and writes "ack ack\n".

The file now looks like

f​i​n​i​s​h​e​d​ ​f​r​o​b​u​l​a​t​i​n​g​ ​t​h​e​ ​b​o​r​k​\​n​a​d​d​e​d​ ​a​ ​h​o​o​v​e​r​\​n​a​c​k​ ​a​c​k​\​n​h​e​ ​w​i​k​k​i​t​\​n​

In short, it has been trashed. Atomic operations were introduced to prevent such errors.

Another example of an atomic operation is opening a file with O_CREAT and O_EXCL. The open will fail if the file exists. This is a handy way to check whether your program is already running, if you only want one copy to be active, like many internet server programs.

Scatter / Gather I/O

Frequently you will make multiple write() system calls for one logical piece of data. writestring in Example 11.2 calls write() twice: once for the size and once for the string. Web server software responds to a request with two distinct chunks of data: the reply headers and the actual data, which are usually kept in distinct data structures. In these cases, you can perform multiple writes, which have the overhead of multiple system calls, or you can copy all the data into a different buffer and then write it in one call. This has data copying overhead and possibly involves dynamic memory, which is another performance hit.

Or you can use the scatter/gather function writev() to gather up your data and have the kernel write it all in one operation. There is also the readv() that reads data scattered amongst different memory buffers.

ssize_t readv (int fd, const struct iovec *iov, int iovcnt);

ssize_t writev (int fd, const struct iovec *iov, int iovcnt);

Rather than taking a buffer, these two functions take an array of struct iovec

s​t​r​u​c​t​ ​i​o​v​e​c​ ​{​
 ​ ​ ​ ​v​o​i​d​ ​*​i​o​v​_​b​a​s​e​;​
 ​ ​ ​ ​s​i​z​e​_​t​ ​i​o​v​_​l​e​n​;​
}​;​

where iov_base is the buffer location and iov_len is how much data to read and write to that buffer (Figure 11.6).

Figure 11.6  Scatter / Gather I/O

Example 11.4 and Example 11.5 are updated to use scatter/gather I/O:

Example 11.4. writevecstring.m

/​/​ ​w​r​i​t​e​v​e​c​s​t​r​i​n​g​.​m​ ​-​-​ ​t​a​k​e​ ​a​r​g​v​[​1​]​ ​a​n​d​ ​w​r​i​t​e​ ​i​t​ ​t​o​ ​a​ ​f​i​l​e​,​ ​p​r​e​p​e​n​d​i​n​g​ ​t​h​e​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​l​e​n​g​t​h​ ​o​f​ ​t​h​e​ ​s​t​r​i​n​g​.​ ​ ​a​n​d​ ​u​s​i​n​g​ ​s​c​a​t​t​e​r​/​g​a​t​h​e​r​ ​I​/​O​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​w​r​i​t​e​v​e​c​s​t​r​i​n​g​ ​w​r​i​t​e​v​e​c​s​t​r​i​n​g​.​m​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​f​c​n​t​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​o​p​e​n​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​i​n​t​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​u​i​n​t​3​2​_​t​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​ ​e​t​.​ ​a​l​.​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​(​)​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​t​a​t​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​e​r​m​i​s​s​i​o​n​ ​f​l​a​g​s​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​s​s​i​z​e​_​t​
#​i​m​p​o​r​t​ ​<​s​y​s​/​u​i​o​.​h​>​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​w​r​i​t​e​v​(​)​ ​a​n​d​ ​s​t​r​u​c​t​ ​i​o​v​e​c​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​c​l​o​s​e​(​)​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​!​=​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​s​a​g​e​:​ ​ ​%​s​ ​s​t​r​i​n​g​-​t​o​-​l​o​g​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​i​n​t​ ​f​d​ ​=​ ​o​p​e​n​ ​(​"​/​t​m​p​/​s​t​r​i​n​g​f​i​l​e​.​t​x​t​"​,​ ​O​_​W​R​O​N​L​Y​ ​|​ ​O​_​C​R​E​A​T​ ​|​ ​O​_​T​R​U​N​C​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​_​I​R​U​S​R​ ​|​ ​S​_​I​W​U​S​R​)​;​

 ​ ​ ​ ​i​f​ ​(​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​a​n​'​t​ ​o​p​e​n​ ​f​i​l​e​.​ ​ ​E​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​u​i​n​t​3​2​_​t​ ​s​t​r​i​n​g​L​e​n​g​t​h​ ​=​ ​s​t​r​l​e​n​ ​(​a​r​g​v​[​1​]​)​;​

 ​ ​ ​ ​s​t​r​u​c​t​ ​i​o​v​e​c​ ​v​e​c​t​o​r​[​2​]​;​ ​/​/​ ​o​n​e​ ​f​o​r​ ​s​i​z​e​,​ ​o​n​e​ ​f​o​r​ ​s​t​r​i​n​g​
 ​ ​ ​ ​v​e​c​t​o​r​[​0​]​.​i​o​v​_​b​a​s​e​ ​=​ ​ ​&​s​t​r​i​n​g​L​e​n​g​t​h​;​
 ​ ​ ​ ​v​e​c​t​o​r​[​0​]​.​i​o​v​_​l​e​n​ ​=​ ​s​i​z​e​o​f​ ​(​s​t​r​i​n​g​L​e​n​g​t​h​)​;​
 ​ ​ ​ ​v​e​c​t​o​r​[​1​]​.​i​o​v​_​b​a​s​e​ ​=​ ​a​r​g​v​[​1​]​;​
 ​ ​ ​ ​v​e​c​t​o​r​[​1​]​.​i​o​v​_​l​e​n​ ​=​ ​s​t​r​i​n​g​L​e​n​g​t​h​;​

 ​ ​ ​ ​s​i​z​e​_​t​ ​r​e​s​u​l​t​ ​=​ ​w​r​i​t​e​v​ ​(​f​d​,​ ​v​e​c​t​o​r​,​ ​2​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​a​n​'​t​ ​w​r​i​t​e​ ​t​o​ ​f​i​l​e​.​ ​ ​E​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​c​l​o​s​e​ ​(​f​d​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​a​n​'​t​ ​w​r​i​t​e​ ​t​o​ ​f​i​l​e​.​ ​ ​E​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

Here is the corresponding reader:

Example 11.5. readvecstring.m

/​/​ ​r​e​a​d​v​e​c​s​t​r​i​n​g​.​m​ ​-​-​ ​o​p​e​n​ ​/​t​m​p​/​s​t​r​i​n​g​f​i​l​e​.​t​x​t​ ​a​n​d​ ​w​r​i​t​e​ ​o​u​t​ ​i​t​s​ ​c​o​n​t​e​n​t​s​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​i​n​g​ ​s​c​a​t​t​e​r​/​g​a​t​h​e​r​ ​r​e​a​d​s​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​r​e​a​d​v​e​c​s​t​r​i​n​g​ ​r​e​a​d​v​e​c​s​t​r​i​n​g​.​m​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​ ​a​n​d​ ​s​t​r​e​r​r​o​r​(​)​
#​i​m​p​o​r​t​ ​<​f​c​n​t​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​o​p​e​n​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​i​n​t​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​u​i​n​t​3​2​_​t​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​ ​e​t​.​ ​a​l​.​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​(​)​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​t​a​t​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​e​r​m​i​s​s​i​o​n​ ​f​l​a​g​s​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​s​s​i​z​e​_​t​
#​i​m​p​o​r​t​ ​<​s​y​s​/​u​i​o​.​h​>​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​r​e​a​d​v​(​)​ ​a​n​d​ ​s​t​r​u​c​t​ ​i​o​v​e​c​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​c​l​o​s​e​(​)​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​f​d​ ​=​ ​o​p​e​n​ ​(​"​/​t​m​p​/​s​t​r​i​n​g​f​i​l​e​.​t​x​t​"​,​ ​O​_​R​D​O​N​L​Y​)​;​

 ​ ​ ​ ​i​f​ ​(​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​a​n​'​t​ ​o​p​e​n​ ​f​i​l​e​.​ ​ ​E​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​u​i​n​t​3​2​_​t​ ​s​t​r​i​n​g​L​e​n​g​t​h​;​
 ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​4​0​9​6​]​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​i​o​v​e​c​ ​v​e​c​t​o​r​[​2​]​;​

 ​ ​ ​ ​v​e​c​t​o​r​[​0​]​.​i​o​v​_​b​a​s​e​ ​=​ ​&​s​t​r​i​n​g​L​e​n​g​t​h​;​
 ​ ​ ​ ​v​e​c​t​o​r​[​0​]​.​i​o​v​_​l​e​n​ ​=​ ​s​i​z​e​o​f​(​s​t​r​i​n​g​L​e​n​g​t​h​)​;​
 ​ ​ ​ ​v​e​c​t​o​r​[​1​]​.​i​o​v​_​b​a​s​e​ ​=​ ​b​u​f​f​e​r​;​
 ​ ​ ​ ​v​e​c​t​o​r​[​1​]​.​i​o​v​_​l​e​n​ ​=​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​ ​-​ ​1​;​

 ​ ​ ​ ​s​s​i​z​e​_​t​ ​r​e​s​u​l​t​ ​=​ ​r​e​a​d​v​ ​(​f​d​,​ ​v​e​c​t​o​r​,​ ​2​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​a​n​'​t​ ​r​e​a​d​ ​f​i​l​e​.​ ​ ​E​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​b​u​f​f​e​r​[​s​t​r​i​n​g​L​e​n​g​t​h​]​ ​=​ ​'​\​0​0​0​'​;​ ​/​/​ ​n​e​e​d​ ​t​o​ ​z​e​r​o​-​t​e​r​m​i​n​a​t​e​ ​i​t​

 ​ ​ ​ ​c​l​o​s​e​ ​(​f​d​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​o​u​r​ ​s​t​r​i​n​g​ ​i​s​ ​'​%​s​'​\​n​"​,​ ​b​u​f​f​e​r​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

Unfortunately, the reading side of things is not as pretty as the writing side. The size of the string is unknown, so a guess was made at the maximum size of the string. Hard-coded limits like this will be the source of errors in the future, as soon as someone writes a 4K+1 length string to one of these files. In general, the writev() will be the most convenient way to write scattered data like this, but you will still do multiple reads to pull apart the data – either that or read a lot of stuff into a buffer and process the data in-place.

creat()

Just a historical note, there is also a creat() function, which you might see in your travels through other people’s code:

int creat (const char *path, mode_t mode);

This creates empty files, but it has been superseded by open(). creat(path, mode) is equivalent to

o​p​e​n​ ​(​p​a​t​h​,​ ​O​_​C​R​E​A​T​ ​|​ ​O​_​T​R​U​N​C​ ​|​ ​O​_​W​R​O​N​L​Y​,​ ​m​o​d​e​)​;​

Ken Thompson, one of the inventors of Unix, was asked what he would do differently if he were redesigning the Unix system today. He said, “I’d spell creat with an e.”

Blocking I/O

File I/O is blocking by default. This means the system call will not return until the I/O completes. If you open a file with the O_NONBLOCK flag, reads will return immediately with an error (errno of EAGAIN). You can also use the select() or poll() functions (discussed in detail in Chapter 14: Network Programming With Sockets) to see if I/O is possible for a particular file descriptor.

Buffered I/O

The system calls we have discussed here are generally called “unbuffered I/O.” There is a little bit of buffering that happens in the kernel, probably no more than a couple of disk blocks, so that a physical I/O is not performed for every byte read or written. But for the most part, what you read and write is what you get in terms of physical I/O. If you want to read in large chunks of data and process the data out of that buffer, you will have to do the work yourself: refilling the buffer when it gets low, remembering your place in the buffer, handling reading and writing, and so on. Not hard work, just tedious.

The Standard C I/O functions are known as “buffered I/O.” The library handles the details of buffer allocation I/O in optimally-sized chunks, only doing real physical I/O when the buffer is empty (for reading), is full (for writing), or is explicitly flushed to the disk.

Rather than passing file descriptors around, buffered I/O has an opaque type called FILE, also known as a stream, and you pass pointers to FILEs around. The file descriptor is wrapped in that FILE, along with the buffer and other pieces of housekeeping.

There are three kinds of buffering that you can use:

Fully Buffered | Actual I/O only happens when the buffer gets full. This is the default for files associated with non-interactive devices.
Line Buffered | I/O happens when a newline character is encountered on input or output. You can use a function to write one character at a time to a stream, but the actual I/O only happens on a newline or when the buffer is getting full.

Unbuffered

No buffering happens; calls just turn around and invoke read() or write(). The standard error stream is usually unbuffered so that error messages appear instantly. The other two standard streams are either fully or line buffered, depending on whether they are attached to an interactive device like a terminal.

You can use setbuf() or setvbuf() to change the buffering behavior.

Opening files

The buffered I/O functions, with a few exceptions, begin with “f.” So, opening files is done with fopen():

FILE *fopen (char *path, char *mode);

fopen() returns a pointer to a newly-allocated FILE structure. You don’t need to worry about allocating it, but you are responsible for disposing of it with fclose(). It is an opaque type, but you can see what’s inside of it. You can look at the stdio.h header or print out a FILE structure in gdb.

fopen() takes a pathname to the file and a mode, which is a character string:

"r" | Open the file for reading.
"r+" | Open the file for reading and writing.
"w" | Truncate the file to zero length, or create it if it does not exist, and then open for writing.
"w+" | Truncate the file to zero length, or create it if it does not exist, and then open for reading and writing.
"a" | Create the file if it does not exist and then open it for writing in append mode.
"a+" | Create the file if it does not exist and then open it for reading and writing in append mode.

Or, put another way,

"r" | Open for reading; file must exist.
"w" | Truncate or create if necessary; file does not need to exist.
"a" | Create if necessary; file does not need to exist. Append mode.

And stick on a “+” to make both reading and writing possible.

There are some subtleties with “+” modes. Output cannot be followed by input without an explicit fflush() or something that flushes the buffer, like an fseek(), fsetpos(), or a rewind(). Similarly, input cannot be directly followed by output without calling of the above calls. This makes sense if you remember that there is one buffer in the FILE, and it can’t handle reading and writing at the same time.

There are buffered I/O versions of the three standard streams: stdin, stdout, stderr. You can pass any of these to the buffered I/O functions.

Because buffered I/O comes from the portable standard C library, it has support for a distinction between binary files and text files because some operating systems have that distinction. Unix makes no such distinction, so any special binary flags (sticking a “b” onto a/a+/w/w+, etc.) passed for the mode are ignored.

Text files are not automatically easy to deal with though. There are three different conventions for indicating the end of a line in a text file. Unix systems use the line feed character (LF / 0x0A) to terminate an end of line, Mac OS 9 used the carriage return (CR / 0x0D). Windows and MS-DOS uses both of them – a carriage return followed by a line feed (0x0D0A), commonly called “CRLF.” If you need to support text files created by non-Unix platforms, you will need to do some extra processing to handle the different line-ending conventions.

Files created with fopen() have permissions rw-rw-rw-. You cannot change that directly with fopen(). Instead, use the chmod() or fchmod() system calls or control the creation permissions with umask().

There are two companion calls to fopen():

FILE *fdopen (int fd, char *mode);

FILE *freopen (char *path, char *mode, FILE *stream);

fdopen() takes an existing file descriptor (from open() or from a networking call) and wraps a FILE stream around it.

freopen() closes and reopens a given file pointer using the new path instead of what was used before. freopen() is very handy if you are redirecting the standard streams. You can do stdin = freopen ("/my/new/stdin.file", "w+", stdin); to redirect the standard in stream to stdin.file.

Closing files

int fclose (FILE *stream);

This dissociates the stream from its underlying file. Any buffered data is queued to the kernel using fflush(), and the enclosed file descriptor is closed. If the program terminates abnormally, any buffered data will not be flushed to the kernel and will be lost. fclose() returns zero on successful completion. Otherwise, EOF is returned, and it sets errno to the reason for the error.

Text I/O

The buffered I/O API has two concepts of I/O when it comes to reading and writing: one is text oriented, the other is binary oriented.

Text I/O can be done a character-at-a-time or a line-at-a-time. Here are the character-at-a-time functions:

int getc (FILE *stream);

int getchar ();

int fgetc (FILE *stream);

Each will try to get the next input character from the given stream or from stdin for getchar(). Notice that these functions return an int rather than a char. The return value is overloaded so that it returns either the function data or a status value. Since char’s useful values cover its entire expressible range, some other bits are needed to hold unique result values. If end of file or a read error occurs, the return returns the constant EOF, usually -1. You need to use the routines feof() and ferror() to distinguish between end of file and an error. In the case of error, errno is set like in the unbuffered I/O functions. On a successful read, the return value is the character data. You can use the function clearerr() to clear the end of file and error indicators on the FILE stream.

The implementation of getc() and fgetc() are defined by the ANSI C standard: getc() is implemented by a macro and fgetc() is implemented as a function. The implications of this specification are that the argument to getc() should not have any side effects because the macro could evaluate its argument more than once. It also means that if you want to stash a character-getting function in a function pointer, you must use fgetc(). The actual time difference between getc() and fgetc() is negligible, so just use fgetc() unless you can measure that the function call overhead is a problem.

Example 11.6 is how to check for EOF and discriminate between error and end of file:

Example 11.6. buffread.m

/​/​ ​b​u​f​f​r​e​a​d​.​m​ ​-​-​ ​s​h​o​w​ ​h​o​w​ ​t​o​ ​r​e​a​d​ ​u​s​i​n​g​ ​b​u​f​f​e​r​e​d​ ​I​/​O​,​ ​i​n​c​l​u​d​i​n​g​ ​e​r​r​o​r​ ​/​ ​e​o​f​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​a​n​d​l​i​n​g​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​b​u​f​f​r​e​a​d​ ​b​u​f​f​r​e​a​d​.​m​

#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​,​ ​e​t​c​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​a​l​l​ ​t​h​e​ ​b​u​f​f​e​r​e​d​ ​I​/​O​ ​A​P​I​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​F​I​L​E​ ​*​f​i​l​e​ ​=​ ​f​o​p​e​n​ ​(​"​b​u​f​f​r​e​a​d​.​m​"​,​ ​"​r​"​)​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​f​g​e​t​c​ ​(​f​i​l​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​E​O​F​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​f​e​o​f​(​f​i​l​e​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​E​O​F​ ​f​o​u​n​d​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​f​e​r​r​o​r​(​f​i​l​e​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​e​r​r​o​r​ ​r​e​a​d​i​n​g​ ​f​i​l​e​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​g​o​t​ ​a​ ​c​h​a​r​a​c​t​e​r​:​ ​'​%​c​'​\​n​"​,​ ​(​c​h​a​r​)​ ​r​e​s​u​l​t​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​f​c​l​o​s​e​ ​(​f​i​l​e​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

Not only can you read characters from the stream, you can push them back onto the stream with ungetc():

int ungetc (int c, FILE *stream);

ANSI C only guarantees one character of pushback, but the OS X manpages say you can push back an arbitrary amount. Being able to push back characters is handy in some parsers. You can peek a character ahead to see if this particular chunk of text is interesting. If not, push that character back and let some other piece of code handle the parsing. ungetc() clears the EOF flag so you can unget a character on an EOF and then read it later. Note, however, that calls to a positioning function, like fseek(), fsetpos(), or rewind() will discard the pushed-back characters.

To write to a stream character-wise, use one of these:

int fputc (int c, FILE *stream);

int putchar(int c);

int putc (int c, FILE *stream);

These are exactly analogous to the reading functions. fputc() is a function, putc() is a macro, putchar() implicitly uses stdout.

For line-at-a-time I/O, use these functions:

char *fgets (char *str, int size, FILE *stream);

char *gets (char *str); // here be dragons

int fputs (const char *str, FILE *stream);

int puts (const char *str);

You give fgets() and gets() an already allocated character buffer. The functions will return when they see an end of line character or when they fill up the buffer. gets() does not accept a size, so there is no way to control how much data will be written into the buffer, making buffer overflows really easy; hence, you should avoid this call. fgets() stores the newline character in the buffer, while gets() does not. In both cases, a zero byte is appended to the end of the string. If an end of file or an error occurs, they return NULL, and you need to check feof() and ferror() to see what happened.

puts() writes to standard out. fputs() does not automatically put a newline to the stream, while puts() does.

Binary I/O

The problem with Text I/O is that zero-value bytes are significant, being the end-of-string indicator. Binary I/O treats all bytes the same. Binary I/O is not limited to just binary data: you can use it for blocks of text too. “Block I/O” would probably be a better name. These calls can be used to write arrays of structures to disk. Be aware of portability problems when blindly writing structures to disk. If any of the data types should change size or layout between platforms, you can end up breaking your file format.

size_t fread (void *ptr, size_t size, size_t nmemb, FILE *stream);

size_t fwrite (const void *ptr, size_t size, size_t nmemb, FILE *stream);

Each function has a pointer to memory, the size of the struct, and the number of elements in an array of these structs. The return value is the number of elements written, not the number of bytes written. Internally, it’s just doing the multiplication and writing that number of bytes. These functions don’t stop at zero bytes or newlines like the text I/O functions do. If the number of elements read or written is less than expected, check ferror() and feof().

Example 11.7 shows binary reading:

Example 11.7. fbinaryio.m

/​/​ ​f​b​i​n​a​r​y​i​o​.​m​ ​-​-​ ​d​o​ ​s​o​m​e​ ​b​i​n​a​r​y​ ​r​e​a​d​i​n​g​ ​a​n​d​ ​w​r​i​t​i​n​g​ ​u​s​i​n​g​ ​b​u​f​f​e​r​e​d​ ​i​/​o​

/​/​ ​g​c​c​ ​-​g​ ​-​s​t​d​=​c​9​9​ ​-​W​a​l​l​ ​-​o​ ​f​b​i​n​a​r​y​i​o​ ​f​b​i​n​a​r​y​i​o​.​m​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​s​t​d​i​n​t​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​u​i​n​t​3​2​_​t​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​t​h​e​ ​b​u​f​f​e​r​e​d​ ​I​/​O​ ​A​P​I​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​,​p​ ​e​t​c​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​(​)​

t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​T​h​i​n​g​ ​{​
 ​ ​ ​ ​u​i​n​t​3​2​_​t​ ​ ​ ​ ​t​h​i​n​g​1​;​
 ​ ​ ​ ​f​l​o​a​t​ ​ ​ ​ ​ ​ ​ ​t​h​i​n​g​2​;​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​ ​ ​ ​ ​t​h​i​n​g​3​[​8​]​;​
}​ ​T​h​i​n​g​;​

T​h​i​n​g​ ​t​h​i​n​g​s​[​]​ ​=​ ​{​
 ​ ​ ​ ​{​ ​3​,​ ​3​.​1​4​1​5​9​,​ ​"​h​e​l​l​o​"​ ​}​,​
 ​ ​ ​ ​{​ ​4​,​ ​4​.​2​9​3​0​1​,​ ​"​b​y​e​"​ ​}​,​
 ​ ​ ​ ​{​ ​2​,​ ​2​.​1​4​2​1​4​,​ ​"​b​o​r​k​"​ ​}​,​
 ​ ​ ​ ​{​ ​5​,​ ​5​.​5​5​5​5​6​,​ ​"​e​l​f​ ​u​p​"​ ​}​
}​;​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​s​i​z​e​_​t​ ​t​h​i​n​g​C​o​u​n​t​ ​=​ ​s​i​z​e​o​f​(​t​h​i​n​g​s​)​ ​/​ ​s​i​z​e​o​f​(​*​t​h​i​n​g​s​)​;​ ​/​/​ ​h​o​w​ ​m​a​n​y​ ​w​e​ ​h​a​v​e​
 ​ ​ ​ ​F​I​L​E​ ​*​f​i​l​e​ ​=​ ​f​o​p​e​n​ ​(​"​/​t​m​p​/​t​h​i​n​g​f​i​l​e​"​,​ ​"​w​"​)​;​

 ​ ​ ​ ​i​f​ ​(​f​i​l​e​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​ ​o​p​e​n​i​n​g​ ​f​i​l​e​:​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​s​i​z​e​_​t​ ​n​u​m​W​r​o​t​e​ ​=​ ​f​w​r​i​t​e​ ​(​t​h​i​n​g​s​,​ ​s​i​z​e​o​f​(​T​h​i​n​g​)​,​ ​t​h​i​n​g​C​o​u​n​t​,​ ​f​i​l​e​)​;​

 ​ ​ ​ ​i​f​ ​(​n​u​m​W​r​o​t​e​ ​!​=​ ​t​h​i​n​g​C​o​u​n​t​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​i​n​c​o​m​p​l​e​t​e​ ​w​r​i​t​e​ ​(​%​d​ ​o​u​t​ ​o​f​ ​%​d​)​.​ ​ ​E​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​i​n​t​)​n​u​m​W​r​o​t​e​,​ ​(​i​n​t​)​t​h​i​n​g​C​o​u​n​t​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​f​c​l​o​s​e​ ​(​f​i​l​e​)​;​

 ​ ​ ​ ​/​/​ ​n​o​w​ ​r​e​-​o​p​e​n​ ​a​n​d​ ​r​e​-​r​e​a​d​ ​a​n​d​ ​m​a​k​e​ ​s​u​r​e​ ​e​v​e​r​y​t​h​i​n​g​ ​i​s​ ​g​r​o​o​v​y​
 ​ ​ ​ ​f​i​l​e​ ​=​ ​f​o​p​e​n​ ​(​"​/​t​m​p​/​t​h​i​n​g​f​i​l​e​"​,​ ​"​r​"​)​;​

 ​ ​ ​ ​i​f​ ​(​f​i​l​e​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​ ​o​p​e​n​i​n​g​ ​f​i​l​e​:​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​w​e​ ​k​n​o​w​ ​w​e​'​r​e​ ​r​e​a​d​i​n​g​ ​i​n​ ​t​h​i​n​g​C​o​u​n​t​,​ ​s​o​ ​w​e​ ​c​a​n​ ​g​o​ ​a​h​e​a​d​ ​a​n​d​
 ​ ​ ​ ​/​/​ ​a​c​q​u​i​r​e​ ​t​h​a​t​ ​m​u​c​h​ ​s​p​a​c​e​
 ​ ​ ​ ​T​h​i​n​g​ ​r​e​a​d​T​h​i​n​g​s​[​s​i​z​e​o​f​(​t​h​i​n​g​s​)​ ​/​ ​s​i​z​e​o​f​(​*​t​h​i​n​g​s​)​]​;​

 ​ ​ ​ ​s​i​z​e​_​t​ ​n​u​m​R​e​a​d​ ​=​ ​f​r​e​a​d​ ​(​r​e​a​d​T​h​i​n​g​s​,​ ​s​i​z​e​o​f​(​T​h​i​n​g​)​,​ ​t​h​i​n​g​C​o​u​n​t​,​ ​f​i​l​e​)​;​

 ​ ​ ​ ​i​f​ ​(​n​u​m​R​e​a​d​ ​!​=​ ​t​h​i​n​g​C​o​u​n​t​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​s​h​o​r​t​ ​r​e​a​d​.​ ​ ​G​o​t​ ​%​d​,​ ​e​x​p​e​c​t​e​d​ ​%​d​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​i​n​t​)​n​u​m​R​e​a​d​,​ ​(​i​n​t​)​t​h​i​n​g​C​o​u​n​t​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​f​e​o​f​(​f​i​l​e​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​w​e​ ​g​o​t​ ​a​n​ ​e​n​d​ ​o​f​ ​f​i​l​e​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​f​e​r​r​o​r​(​f​i​l​e​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​w​e​ ​g​o​t​ ​a​n​ ​e​r​r​o​r​:​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​J​u​s​t​ ​f​o​r​ ​f​u​n​,​ ​c​o​m​p​a​r​e​ ​t​h​e​ ​n​e​w​l​y​ ​r​e​a​d​ ​o​n​e​s​ ​w​i​t​h​ ​t​h​e​ ​o​n​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​w​e​ ​h​a​v​e​ ​s​t​a​t​i​c​a​l​l​y​ ​d​e​c​l​a​r​e​d​
 ​ ​ ​ ​ ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​t​h​i​n​g​C​o​u​n​t​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​(​t​h​i​n​g​s​[​i​]​.​t​h​i​n​g​1​ ​!​=​ ​r​e​a​d​T​h​i​n​g​s​[​i​]​.​t​h​i​n​g​1​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​|​|​ ​(​t​h​i​n​g​s​[​i​]​.​t​h​i​n​g​2​ ​!​=​ ​r​e​a​d​T​h​i​n​g​s​[​i​]​.​t​h​i​n​g​2​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​|​|​ ​(​s​t​r​c​m​p​(​t​h​i​n​g​s​[​i​]​.​t​h​i​n​g​3​,​ ​r​e​a​d​T​h​i​n​g​s​[​i​]​.​t​h​i​n​g​3​)​ ​!​=​ ​0​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​m​i​s​m​a​t​c​h​ ​w​i​t​h​ ​e​l​e​m​e​n​t​ ​%​d​\​n​"​,​ ​i​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​s​u​c​c​e​s​s​f​u​l​l​y​ ​c​o​m​p​a​r​e​d​ ​e​l​e​m​e​n​t​ ​%​d​\​n​"​,​ ​i​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​f​c​l​o​s​e​ ​(​f​i​l​e​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

Here is a sample run:

$​ ​.​/​f​b​i​n​a​r​y​i​o​
s​u​c​c​e​s​s​f​u​l​l​y​ ​c​o​m​p​a​r​e​d​ ​e​l​e​m​e​n​t​ ​0​
s​u​c​c​e​s​s​f​u​l​l​y​ ​c​o​m​p​a​r​e​d​ ​e​l​e​m​e​n​t​ ​1​
s​u​c​c​e​s​s​f​u​l​l​y​ ​c​o​m​p​a​r​e​d​ ​e​l​e​m​e​n​t​ ​2​
s​u​c​c​e​s​s​f​u​l​l​y​ ​c​o​m​p​a​r​e​d​ ​e​l​e​m​e​n​t​ ​3​

And just for fun, look at the file itself:

$​ ​h​e​x​d​u​m​p​ ​-​C​ ​/​t​m​p​/​t​h​i​n​g​f​i​l​e​
0​0​0​0​ ​ ​0​3​ ​0​0​ ​0​0​ ​0​0​ ​d​0​ ​0​f​ ​4​9​ ​4​0​ ​ ​6​8​ ​6​5​ ​6​c​ ​6​c​ ​6​f​ ​0​0​ ​0​0​ ​0​0​ ​ ​|​.​.​.​.​.​.​I​@​h​e​l​l​o​.​.​.​|​
0​0​1​0​ ​ ​0​4​ ​0​0​ ​0​0​ ​0​0​ ​5​7​ ​6​0​ ​8​9​ ​4​0​ ​ ​6​2​ ​7​9​ ​6​5​ ​0​0​ ​0​0​ ​0​0​ ​0​0​ ​0​0​ ​ ​|​.​.​.​.​W​`​.​@​b​y​e​.​.​.​.​.​|​
0​0​2​0​ ​ ​0​2​ ​0​0​ ​0​0​ ​0​0​ ​d​2​ ​1​8​ ​0​9​ ​4​0​ ​ ​6​2​ ​6​f​ ​7​2​ ​6​b​ ​0​0​ ​0​0​ ​0​0​ ​0​0​ ​ ​|​.​.​.​.​.​.​.​@​b​o​r​k​.​.​.​.​|​
0​0​3​0​ ​ ​0​5​ ​0​0​ ​0​0​ ​0​0​ ​2​6​ ​c​7​ ​b​1​ ​4​0​ ​ ​6​5​ ​6​c​ ​6​6​ ​2​0​ ​7​5​ ​7​0​ ​0​0​ ​0​0​ ​ ​|​.​.​.​.​&​.​.​@​e​l​f​ ​u​p​.​.​|​

You can see that there are 4 bytes of integer, 4 bytes of float, and 8 bytes of character data. It just so happens to line up with the struct Thing definition. One word of warning: this code and data file are not portable between PowerPC and Intel Macintoshes. Here is the file from a PowerPC machine:

$​ ​h​e​x​d​u​m​p​ ​-​C​ ​/​t​m​p​/​t​h​i​n​g​f​i​l​e​
0​0​0​0​ ​ ​0​0​ ​0​0​ ​0​0​ ​0​3​ ​4​0​ ​4​9​ ​0​f​ ​d​0​ ​ ​6​8​ ​6​5​ ​6​c​ ​6​c​ ​6​f​ ​0​0​ ​0​0​ ​0​0​ ​ ​|​.​.​.​.​@​I​.​.​h​e​l​l​o​.​.​.​|​
0​0​1​0​ ​ ​0​0​ ​0​0​ ​0​0​ ​0​4​ ​4​0​ ​8​9​ ​6​0​ ​5​7​ ​ ​6​2​ ​7​9​ ​6​5​ ​0​0​ ​0​0​ ​0​0​ ​0​0​ ​0​0​ ​ ​|​.​.​.​.​@​.​`​W​b​y​e​.​.​.​.​.​|​
0​0​2​0​ ​ ​0​0​ ​0​0​ ​0​0​ ​0​2​ ​4​0​ ​0​9​ ​1​8​ ​d​2​ ​ ​6​2​ ​6​f​ ​7​2​ ​6​b​ ​0​0​ ​0​0​ ​0​0​ ​0​0​ ​ ​|​.​.​.​.​@​.​.​.​b​o​r​k​.​.​.​.​|​
0​0​3​0​ ​ ​0​0​ ​0​0​ ​0​0​ ​0​5​ ​4​0​ ​b​1​ ​c​7​ ​2​6​ ​ ​6​5​ ​6​c​ ​6​6​ ​2​0​ ​7​5​ ​7​0​ ​0​0​ ​0​0​ ​ ​|​.​.​.​.​@​.​.​&​e​l​f​ ​u​p​.​.​|​

See the section about network byte order in Chapter 14: Network Programming With Sockets for more details.

Positioning

Like unbuffered I/O, the buffered I/O FILE streams have a current position.

long ftell (FILE *stream);

int fseek (FILE *stream, long offset, int whence);

ftell() returns the current location in the file. fseek() is like lseek(). It has the same use of offset and whence (SEEK_SET, SEEK_CUR, SEEK_END). The return value of fseek() is zero on successful completion. Otherwise, -1 is returned and errno is set appropriately. Note that the offset type of these functions is long, which can limit file sizes. There are also versions that take off_t types, which could be larger than longs:

off_t ftello (FILE *stream);

int fseeko (FILE *stream, off_t offset, int whence);

An alternate interface is

int fgetpos (FILE *stream, fpos_t *pos);

int fsetpos (FILE *stream, const fpos_t *pos);

using the opaque fpos_t type. On some platforms, it’s the same as an off_t. On other platforms, it’s an 8-byte array.

void rewind (FILE *stream);

will reset the current location in the file to the very beginning, also clearing the error flag.

Formatted I/O

The printf family of calls lives under standard / buffered I/O because they write out to FILE streams, either explicitly or implicitly. sprintf and snprintf write to a buffer, but they have the same syntax as their I/O companions.

int printf (const char *format, ...);

int fprintf (FILE *stream, const char *format, ...);

int sprintf (char *str, const char *format, ...);

int snprintf (char *str, size_t size, const char *format, ...);

int asprintf (char **ret, const char *format, ...);

printf() writes to stdout. fprintf() writes to any FILE. sprintf() writes into a buffer, while snprintf() writes into a buffer but is given the amount of space it can write in. Always use snprintf() instead of sprintf(). There is no prevention of buffer overruns in sprintf(), especially if any user-entered data gets fed into the function. With snprintf(), buffer overruns will not happen. asprintf() takes snprintf() to the next level by automatically allocating a buffer for you that is the correct size. You will need to free() this buffer once you are done with it.

The specifics of the printf-style of formatting are covered in opaque detail in the manpage and in most every C 101 book out there.

You can do formatted input as well, using

int scanf (const char *format, ...);

int fscanf (FILE *stream, const char *format, ...);

int sscanf (const char *str, const char *format, ...);

These take format strings like printf(), but instead of just regular arguments in the ... section of the parameter list, you need to provide pointers to appropriate-sized areas of memory. The scanf() functions will not allocate memory for you. There is not much in the way of error detection or recovery when using the functions. Outside of toy programs, you will probably want to write your own parsing code, use a regular expression library, or use lexer/parser generator tools.

Misc Functions

Here are some miscellaneous buffered I/O functions.

int fileno (FILE *stream);

This returns the file descriptor that the stream is wrapped around. This is handy if you need to use fcntl(), fchmod(), fstat(), or the dup() functions.

int getw (FILE *stream);

int putw (int w, FILE *stream);

These are like getc()/putc() but read or write an integer. The integers are not written in a canonical form (discussed in Chapter 14: Network Programming With Sockets), so if you want to be portable, you will need to put the bytes into a known byte order.

Buffered I/O vs. Unbuffered I/O

With buffered I/O, there is a lot of data copying happening:

program structures –> FILE buffer –> kernel buffer –> disk.

With unbuffered I/O, the copy to the FILE buffer is avoided, and with scatter/gather I/O, the kernel might be able to avoid a copy into its own buffers.

Since read() and write() are not buffered (except for a disk block or two in the kernel data structures), they can be slow when dealing with lots of smaller reads and writes. Buffered I/O would be a win here.

When doing big reads and writes, say dozens of K of image data, there is a win to using plain read() and write() calls to avoid the buffering step in between.

Fundamentally, everything boils down to the file descriptor. Even FILEs have a file descriptor at their heart. Be careful when mixing buffered and unbuffered I/O with the same file descriptor. The FILE part of the world does not get notified if you do I/O with the file descriptor, leading to some confusing synchronization errors.

There is some magic that happens in both the buffered and unbuffered I/O cases when input or output is going to a terminal device. read() takes on per-line semantics, and the buffered I/O calls take on line buffering behavior.

Lastly, when both buffered and unbuffered I/O perform a write operation, the data does not necessarily end up on disk immediately. The kernel will buffer the writing to reduce the number of physical I/O operations it has to do. This means that you can dutifully write your data, have the OS crash, and your data is lost. If you are truly paranoid about having data written to disk (like in a database system), you can use one of the sync calls:

void sync (void);

This forces the queuing of modified buffers in the block buffer for I/O. This call will return immediately, and the kernel will write the blocks at its leisure. This will force writing for all modified blocks system-wide.

int fsync (int fd);

This causes all modified data and metadata for a file to be flushed out to the physical disk. It applies to just one file. The call will block for all I/O to complete before returning.

Removing Files

Removing files is pretty easy. Use the unlink() system call:

int unlink (const char *path);

unlink() returns zero on successful completion or -1 if there was an error, with errno set to the error that actually happened. There can be multiple references to a file via hardlinks. unlink() removes a reference from a particular dictionary, but the file’s contents are only removed when all references to it have been eliminated.

There is one subtlety when unlinking files that are currently open. The directory entry for the file is removed immediately. If you do an ls on the directory, you won’t see the file. But the file’s contents still exist on the disk, still consume space, and are still available to programs that have the file open. The actual space is reclaimed by the OS once all programs close the file. This is useful for temporary files, ones you do not want to hang around after the program goes away. By unlinking the temporary file immediately after opening it, you don’t need to worry about deleting the file once your program is over. The space occupied by the file will be reclaimed even if your program crashes.

This behavior can occasionally lead to puzzling system administration issues. Imagine the scenario where a web server has a 500-meg log file open and the disk it is on is filling up. Someone deletes the file hoping to reclaim the space. Because the log file is still open, those 500 megs are still being used, but they will not show up using any command (like du).

Rather than deleting the file, you can truncate it with the command:

$​ ​s​u​d​o​ ​c​p​ ​/​d​e​v​/​n​u​l​l​ ​/​t​h​e​/​l​o​g​/​f​i​l​e​/​n​a​m​e​

The sudo command lets you run a command as the superuser.

If you have already deleted the file, you can use the command lsof to “ls Open Files” and see what is there. lsof is pretty handy anyway for peeking inside the system and seeing what is going on.

$​ ​s​u​d​o​ ​l​s​o​f​
(​H​u​g​e​ ​a​m​o​u​n​t​ ​o​f​ ​o​u​t​p​u​t​.​ ​ ​H​e​r​e​ ​a​r​e​ ​a​ ​c​o​u​p​l​e​ ​l​i​n​e​s​.​)​

p​b​s​ ​ ​ ​2​6​6​ ​m​a​r​k​d​ ​c​w​d​ ​ ​ ​V​D​I​R​ ​ ​ ​1​4​,​9​ ​ ​ ​ ​ ​ ​1​4​8​6​ ​ ​ ​ ​ ​ ​2​ ​/​ ​(​/​d​e​v​/​d​i​s​k​0​s​9​)​
p​b​s​ ​ ​ ​2​6​6​ ​m​a​r​k​d​ ​ ​ ​0​u​ ​ ​V​C​H​R​ ​ ​ ​ ​0​,​0​ ​ ​ ​ ​0​t​4​0​1​5​ ​2​6​2​6​2​8​ ​/​d​e​v​/​c​o​n​s​o​l​e​
p​b​s​ ​ ​ ​2​6​6​ ​m​a​r​k​d​ ​ ​ ​1​u​ ​ ​V​C​H​R​ ​ ​ ​ ​0​,​0​ ​ ​ ​ ​0​t​4​0​1​5​ ​2​6​2​6​2​8​ ​/​d​e​v​/​c​o​n​s​o​l​e​
p​b​s​ ​ ​ ​2​6​6​ ​m​a​r​k​d​ ​ ​ ​2​u​ ​ ​V​C​H​R​ ​ ​ ​ ​0​,​0​ ​ ​ ​ ​0​t​4​0​1​5​ ​2​6​2​6​2​8​ ​/​d​e​v​/​c​o​n​s​o​l​e​

This is the pasteboard server, process ID 266, owned by user markd. It has three files open on fd 0, 1, 2, all open for read/write, and all going to /dev/console. It also has the directory / (the Unix root directory) open, as the current working directory.

A​d​i​u​m​ ​ ​ ​ ​1​2​6​9​ ​m​a​r​k​d​ ​ ​ ​ ​7​u​ ​ ​i​n​e​t​ ​0​x​0​2​4​0​d​7​b​c​ ​ ​ ​ ​0​t​0​ ​ ​ ​ ​ ​T​C​P​ ​
1​0​.​0​.​1​.​1​4​2​:​4​9​2​4​9​-​>​t​o​c​-​m​0​4​.​b​l​u​e​.​a​o​l​.​c​o​m​:​9​8​9​8​ ​(​E​S​T​A​B​L​I​S​H​E​D​)​

Adium, an AIM client that is running with pid 1269, has fd 7 being an internet connection. On the local side, the IP address 10.0.1.142 port 49249 is connected to toc-m04.blue.aol.com, port 9898.

l​s​o​f​ ​ ​ ​1​3​6​1​ ​m​a​r​k​d​ ​ ​ ​3​r​ ​ ​V​C​H​R​ ​ ​ ​ ​3​,​0​ ​ ​ ​ ​ ​ ​ ​ ​0​t​0​ ​2​6​2​3​4​3​7​2​ ​/​d​e​v​/​m​e​m​
l​s​o​f​ ​ ​ ​1​3​6​1​ ​m​a​r​k​d​ ​ ​ ​4​r​ ​ ​V​C​H​R​ ​ ​ ​ ​3​,​1​ ​0​t​2​6​4​3​2​6​1​2​ ​2​6​2​3​4​2​4​4​ ​/​d​e​v​/​k​m​e​m​
l​s​o​f​ ​ ​ ​1​3​6​1​ ​m​a​r​k​d​ ​ ​ ​5​r​ ​ ​V​R​E​G​ ​ ​ ​1​4​,​9​ ​ ​ ​ ​3​1​6​9​8​2​4​ ​ ​ ​ ​6​6​8​6​2​ ​/​m​a​c​h​_​k​e​r​n​e​l​

Finally, this run of lsof has file descriptors 3, 4, 5 open, reading /dev/mem, /dev/kmem (kernel memory), and it also has the mach_kernel file open so it can resolve the data it finds in the memory devices.

Temporary Files

Temporary files, as the name implies, are files that are used by a program as it is running and then are not useful once the program goes away. Unix systems typically store temp files in a well known location so that people know where to clean out files that are left over accidentally. Some systems will clear out the directory that holds the temp files on startup.

A program needs a unique name when it wants to create temporary file so that it does not clash with another file already in the temp directory. If two programs ended up using the same temporary file, one or the other would probably fail in weird and wonderful ways.

Where is this temporary directory? There are a number of calls available for finding a temporary directory and for generating a file name inside of that directory. Most of them have security issues. You may have noticed that some examples in this book put things into /tmp. This is the “classic” Unix temporary directory. For simple one-offs test, or example code, it’s ok because they will not be used as attack vectors.

Mac OS X provides temporary directories for each user, so you do not have to worry about another user snooping your temporary files. Use NSTemporaryDirectory() to get the directory, followed by mkdtemp() to make sure it exists. This second call will create any intervening directories with permissions that are only accessible by the current user. There are other functions you can use that will automatically open a temporary file for you in the temp directory.

NSString *NSTemporaryDirectory (void);

This is the path of the temporary directory for the current user. It is possible for this to return nil if the temporary directory cannot be determined for this user.

char *mktemp (char *template);

This takes the filename template and replaces part of it to create the filename you should use. This is guaranteed not to exist at the time of the function invocation, but it’s entirely possible another application could create and open that file first. The template can be any path with some number of X’s in it. The trailing X’s are replaced with a unique alphanumeric combination. The more X’s you have, the less chance of the file name being guessed or an accidental collision happening. It returns a pointer to the modified template on success and returns NULL on failure.

int mkstemp (char *template);

This makes the same replacement to the template as mktemp(), but it also creates and opens the file and returns a file descriptor. This is an atomic operation and avoids the race condition with mktemp(). It returns -1 on error and fills in errno.

int mkstemps (char *template, int suffixlen);

This works like mkstemp(), except it permits a suffix to exist in the template. The template should be of the form "/path/to/tmp/dir/blahXXXXXXXsuffix". This allows you to use a file extension for your temporary file. It also returns -1 on error and fills in errno.

char *mkdtemp (char *template);

This makes the same replacement to the template as in mktemp() and creates the template directory with mode 0700 (read, write, execute only for the owner). It returns a pointer to the template on success and returns NULL on failure.

The temporary directories get cleared out at boot time, but if you store a lot of data in a temporary directory, you should somehow arrange for its cleanup before then because multi-week uptimes are not unheard of in Mac OS X. The automatically-opened files do not delete the temporary file. You may want to unlink the file after opening to reclaim the disk space when your program exits.

A common error that results in a crash is trying to pass a literal "string" for the template. These calls modify the template, which is not possible on a literal const string.

Example 11.8 shows mkdtemp() and mkstemp() in action. The other temp dir calls work similarly.

Example 11.8. tempfun.m

/​/​ ​t​e​m​p​f​u​n​.​m​ ​-​-​ ​s​e​e​ ​h​o​w​ ​d​i​f​f​e​r​e​n​t​ ​t​e​m​p​ ​f​i​l​e​ ​n​a​m​e​s​ ​a​r​e​ ​g​e​n​e​r​a​t​e​d​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​o​ ​t​e​m​p​f​u​n​ ​t​e​m​p​f​u​n​.​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​t​h​e​ ​t​e​m​p​ ​n​a​m​e​ ​f​u​n​c​t​i​o​n​s​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​,​ ​e​t​c​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​c​p​y​(​)​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​m​k​[​s​]​t​e​m​p​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​t​e​m​p​D​i​r​ ​=​ ​N​S​T​e​m​p​o​r​a​r​y​D​i​r​e​c​t​o​r​y​ ​(​)​;​
 ​ ​ ​ ​i​f​ ​(​t​e​m​p​D​i​r​ ​=​=​ ​n​i​l​)​ ​t​e​m​p​D​i​r​ ​=​ ​@​"​/​t​m​p​"​;​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​t​e​m​p​D​i​r​ ​i​s​ ​%​@​"​,​ ​t​e​m​p​D​i​r​)​;​

 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​t​e​m​p​l​a​t​e​ ​=​ ​[​t​e​m​p​D​i​r​ ​s​t​r​i​n​g​B​y​A​p​p​e​n​d​i​n​g​P​a​t​h​C​o​m​p​o​n​e​n​t​:​ ​@​"​b​o​r​k​.​X​X​X​X​X​X​"​]​;​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​t​e​m​p​l​a​t​e​ ​i​s​ ​%​@​"​,​ ​t​e​m​p​l​a​t​e​)​;​

 ​ ​ ​ ​c​h​a​r​ ​*​m​u​t​a​b​l​e​T​e​m​p​l​a​t​e​ ​=​ ​s​t​r​d​u​p​ ​(​[​t​e​m​p​l​a​t​e​ ​f​i​l​e​S​y​s​t​e​m​R​e​p​r​e​s​e​n​t​a​t​i​o​n​]​)​;​
 ​ ​ ​ ​c​h​a​r​ ​*​p​a​t​h​ ​=​ ​m​k​d​t​e​m​p​ ​(​m​u​t​a​b​l​e​T​e​m​p​l​a​t​e​)​;​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​a​f​t​e​r​ ​m​k​d​t​e​m​p​:​ ​%​s​"​,​ ​p​a​t​h​)​;​
 ​ ​ ​ ​f​r​e​e​ ​(​m​u​t​a​b​l​e​T​e​m​p​l​a​t​e​)​;​

 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​t​e​m​p​l​a​t​e​2​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​t​e​m​p​D​i​r​ ​s​t​r​i​n​g​B​y​A​p​p​e​n​d​i​n​g​P​a​t​h​C​o​m​p​o​n​e​n​t​:​ ​@​"​g​r​e​e​b​l​e​X​X​X​X​X​X​.​t​x​t​"​]​;​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​t​e​m​p​l​a​t​e​2​ ​i​s​ ​%​@​"​,​ ​t​e​m​p​l​a​t​e​2​)​;​

 ​ ​ ​ ​m​u​t​a​b​l​e​T​e​m​p​l​a​t​e​ ​=​ ​s​t​r​d​u​p​ ​(​[​t​e​m​p​l​a​t​e​2​ ​f​i​l​e​S​y​s​t​e​m​R​e​p​r​e​s​e​n​t​a​t​i​o​n​]​)​;​
 ​ ​ ​ ​i​n​t​ ​f​d​ ​=​ ​m​k​s​t​e​m​p​s​ ​(​m​u​t​a​b​l​e​T​e​m​p​l​a​t​e​,​ ​4​)​;​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​a​f​t​e​r​ ​m​k​s​t​e​m​p​s​:​ ​f​d​ ​%​d​,​ ​p​a​t​h​ ​%​s​"​,​ ​f​d​,​ ​m​u​t​a​b​l​e​T​e​m​p​l​a​t​e​)​;​
 ​ ​ ​ ​f​r​e​e​ ​(​m​u​t​a​b​l​e​T​e​m​p​l​a​t​e​)​;​

 ​ ​ ​ ​c​l​o​s​e​ ​(​f​d​)​;​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​
 ​ ​ ​ ​s​l​e​e​p​(​5​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

And some output:

$​ ​.​/​t​e​m​p​f​u​n​
t​e​m​p​D​i​r​ ​i​s​ ​/​v​a​r​/​f​o​l​d​e​r​s​/​S​u​/​S​u​2​3​N​V​9​7​E​2​C​t​g​l​s​y​3​d​n​u​E​U​+​+​+​T​I​/​-​T​m​p​-​/​
t​e​m​p​l​a​t​e​ ​i​s​ ​/​v​a​r​/​f​o​l​d​e​r​s​/​S​u​/​S​u​2​3​N​V​9​7​E​2​C​t​g​l​s​y​3​d​n​u​E​U​+​+​+​T​I​/​-​T​m​p​-​/​b​o​r​k​.​X​X​X​X​X​X​
a​f​t​e​r​ ​m​k​d​t​e​m​p​:​ ​/​v​a​r​/​f​o​l​d​e​r​s​/​S​u​/​S​u​2​3​N​V​9​7​E​2​C​t​g​l​s​y​3​d​n​u​E​U​+​+​+​T​I​/​-​T​m​p​-​/​b​o​r​k​.​0​i​Q​v​X​D​
t​e​m​p​l​a​t​e​2​ ​i​s​ ​/​v​a​r​/​f​o​l​d​e​r​s​/​S​u​/​S​u​2​3​N​V​9​7​E​2​C​t​g​l​s​y​3​d​n​u​E​U​+​+​+​T​I​/​-​T​m​p​-​/​g​r​e​e​b​l​e​X​X​X​X​X​X​.​t​x​t​
a​f​t​e​r​ ​m​k​s​t​e​m​p​s​:​ ​f​d​ ​3​,​ ​
 ​ ​ ​ ​p​a​t​h​ ​/​v​a​r​/​f​o​l​d​e​r​s​/​S​u​/​S​u​2​3​N​V​9​7​E​2​C​t​g​l​s​y​3​d​n​u​E​U​+​+​+​T​I​/​-​T​m​p​-​/​g​r​e​e​b​l​e​C​u​S​F​W​z​.​t​x​t​

In older versions of OS X, the temporary file name was related to the process ID of the running program. This made it easier for malicious programs to guess the name of the file, so a randomly generated string is used nowadays.

File Permissions

Because of OS X’s Unix heritage, it has a multi-user permission model on the file system.

Users and groups

Every user on the system has an integer user ID. Each user belongs to one or more named groups, and each group has an ID. For instance:

$​ ​l​s​ ​-​l​ ​c​h​a​p​t​e​r​.​t​x​t​
-​r​w​-​r​-​-​r​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​4​8​8​2​7​ ​A​u​g​ ​1​1​ ​1​4​:​1​4​ ​c​h​a​p​t​e​r​.​t​x​t​

The user is markd, the group is staff.

$​ ​l​s​ ​-​l​ ​/​b​i​n​/​l​s​
-​r​-​x​r​-​x​r​-​x​ ​ ​1​ ​r​o​o​t​ ​ ​w​h​e​e​l​ ​ ​8​0​6​8​8​ ​F​e​b​ ​1​1​ ​ ​2​0​1​0​ ​/​b​i​n​/​l​s​*​

ls is owned by the user root, the group wheel.

You can see what users and groups are configured on your machine with the Directory Services tools dscl and dscacheutil. You can get a list of users and their user ID:

$​ ​d​s​c​l​ ​.​ ​-​l​i​s​t​ ​/​U​s​e​r​s​ ​U​n​i​q​u​e​I​D​
_​a​m​a​v​i​s​d​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​8​3​
_​a​p​p​o​w​n​e​r​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​8​7​
_​a​p​p​s​e​r​v​e​r​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​7​9​
.​.​.​
b​o​r​k​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​5​0​2​
d​a​e​m​o​n​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​
m​a​r​k​d​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​5​0​1​
n​o​b​o​d​y​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​-​2​
r​o​o​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​

The accounts markd and bork are user accounts that were added to the machine.

You can see groups, and members of each group, with dscacheutil:

$​ ​d​s​c​a​c​h​e​u​t​i​l​ ​-​q​ ​g​r​o​u​p​
n​a​m​e​:​ ​_​a​m​a​v​i​s​d​
p​a​s​s​w​o​r​d​:​ ​*​
g​i​d​:​ ​8​3​

n​a​m​e​:​ ​_​a​p​p​o​w​n​e​r​
p​a​s​s​w​o​r​d​:​ ​*​
g​i​d​:​ ​8​7​

n​a​m​e​:​ ​_​a​p​p​s​e​r​v​e​r​a​d​m​
p​a​s​s​w​o​r​d​:​ ​*​
g​i​d​:​ ​8​1​
u​s​e​r​s​:​ ​m​a​r​k​d​ ​b​o​r​k​ ​
.​.​.​
n​a​m​e​:​ ​a​d​m​i​n​
p​a​s​s​w​o​r​d​:​ ​*​
g​i​d​:​ ​8​0​
u​s​e​r​s​:​ ​r​o​o​t​ ​m​a​r​k​d​ ​b​o​r​k​ ​

Groups are a way to aggregate users that should have similar access permissions on sets of files. You would want your developers to be able edit files on a shared documentation tree. Specifically, the user markd is in group admin, so that user can administer the computer. You can see what groups you are in by using the id command:

%​ ​i​d​
u​i​d​=​5​0​1​(​m​a​r​k​d​)​ ​g​i​d​=​2​0​(​s​t​a​f​f​)​ ​g​r​o​u​p​s​=​2​0​(​s​t​a​f​f​)​,​
4​0​3​(​c​o​m​.​a​p​p​l​e​.​s​h​a​r​e​p​o​i​n​t​.​g​r​o​u​p​.​2​)​,​2​0​4​(​_​d​e​v​e​l​o​p​e​r​)​,​1​0​0​(​_​l​p​o​p​e​r​a​t​o​r​)​,​
9​8​(​_​l​p​a​d​m​i​n​)​,​8​1​(​_​a​p​p​s​e​r​v​e​r​a​d​m​)​,​8​0​(​a​d​m​i​n​)​,​7​9​(​_​a​p​p​s​e​r​v​e​r​u​s​r​)​,​
6​1​(​l​o​c​a​l​a​c​c​o​u​n​t​s​)​,​1​2​(​e​v​e​r​y​o​n​e​)​,​4​0​2​(​c​o​m​.​a​p​p​l​e​.​s​h​a​r​e​p​o​i​n​t​.​g​r​o​u​p​.​1​)​,​
4​0​1​(​c​o​m​.​a​p​p​l​e​.​a​c​c​e​s​s​_​s​c​r​e​e​n​s​h​a​r​i​n​g​)​

On other systems that do not use Directory Services (which would be just about every other flavor of Unix out there), the files /etc/passwd and /etc/group contain user and group information.

You use the chown command to change the ownership of a file. You must have superuser privilege to give away one of your own files to another user. Otherwise, you could use that to defeat disk space quotas or trick another user into opening something malicious.

For instance:

$​ ​t​o​u​c​h​ ​s​p​o​o​n​

$​ ​l​s​ ​-​l​ ​s​p​o​o​n​
-​r​w​-​r​-​-​r​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​1​ ​1​4​:​4​0​ ​s​p​o​o​n​

$​ ​c​h​o​w​n​ ​b​o​r​k​ ​s​p​o​o​n​
c​h​o​w​n​:​ ​s​p​o​o​n​:​ ​O​p​e​r​a​t​i​o​n​ ​n​o​t​ ​p​e​r​m​i​t​t​e​d​

$​ ​s​u​d​o​ ​c​h​o​w​n​ ​b​o​r​k​ ​s​p​o​o​n​

$​ ​l​s​ ​-​l​ ​s​p​o​o​n​
-​r​w​-​r​-​-​r​-​-​ ​ ​1​ ​b​o​r​k​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​1​ ​1​4​:​4​0​ ​s​p​o​o​n​

You can change groups with chgrp:

$​ ​c​h​g​r​p​ ​w​h​e​e​l​ ​s​p​o​o​n​
c​h​g​r​p​:​ ​s​p​o​o​n​:​ ​O​p​e​r​a​t​i​o​n​ ​n​o​t​ ​p​e​r​m​i​t​t​e​d​

$​ ​s​u​d​o​ ​c​h​g​r​p​ ​w​h​e​e​l​ ​s​p​o​o​n​

$​ ​l​s​ ​-​l​ ​s​p​o​o​n​
-​r​w​-​r​-​-​r​-​-​ ​ ​1​ ​b​o​r​k​ ​ ​w​h​e​e​l​ ​ ​0​ ​A​u​g​ ​1​1​ ​1​4​:​4​0​ ​s​p​o​o​n​

You can combine these two operations into one chown command if you wish:

$​ ​s​u​d​o​ ​c​h​o​w​n​ ​m​a​r​k​d​:​s​t​a​f​f​ ​s​p​o​o​n​

$​ ​l​s​ ​-​l​ ​s​p​o​o​n​
-​r​w​-​r​-​-​r​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​1​ ​1​4​:​4​0​ ​s​p​o​o​n​

There are system calls for manually changing the owner and group of a file:

int chown (const char *path, uid_t owner, gid_t group);

int lchown (const char *path, uid_t owner, gid_t group);

int fchown(int fd, uid_t owner, gid_t group);

chown() changes the owner and group of a file, lchown() changes it for a symbolic link, and fchown() operates on a file that is already open.

If you need to look up the owner and group IDs:

struct passwd *getpwent (void);

struct passwd *getpwnam (const char *login);

struct passwd *getpwuid (uid_t uid);

There are equivalent operations (getgrent(), getgrnam(), getgrgid()) for group information.

If you are familiar with the /etc/password file format, struct passwd will look familiar:

s​t​r​u​c​t​ ​p​a​s​s​w​d​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​*​p​w​_​n​a​m​e​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​u​s​e​r​ ​n​a​m​e​ ​*​/​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​*​p​w​_​p​a​s​s​w​d​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​e​n​c​r​y​p​t​e​d​ ​p​a​s​s​w​o​r​d​ ​*​/​
 ​ ​ ​ ​u​i​d​_​t​ ​ ​ ​ ​p​w​_​u​i​d​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​u​s​e​r​ ​u​i​d​ ​*​/​
 ​ ​ ​ ​g​i​d​_​t​ ​ ​ ​ ​p​w​_​g​i​d​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​u​s​e​r​ ​g​i​d​ ​*​/​
 ​ ​ ​ ​_​_​d​a​r​w​i​n​_​t​i​m​e​_​t​ ​p​w​_​c​h​a​n​g​e​;​ ​ ​ ​/​*​ ​p​a​s​s​w​o​r​d​ ​c​h​a​n​g​e​ ​t​i​m​e​ ​*​/​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​*​p​w​_​c​l​a​s​s​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​u​s​e​r​ ​a​c​c​e​s​s​ ​c​l​a​s​s​ ​*​/​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​*​p​w​_​g​e​c​o​s​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​H​o​n​e​y​w​e​l​l​ ​l​o​g​i​n​ ​i​n​f​o​ ​*​/​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​*​p​w​_​d​i​r​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​h​o​m​e​ ​d​i​r​e​c​t​o​r​y​ ​*​/​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​*​p​w​_​s​h​e​l​l​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​d​e​f​a​u​l​t​ ​s​h​e​l​l​ ​*​/​
 ​ ​ ​ ​_​_​d​a​r​w​i​n​_​t​i​m​e​_​t​ ​p​w​_​e​x​p​i​r​e​;​ ​ ​ ​/​*​ ​a​c​c​o​u​n​t​ ​e​x​p​i​r​a​t​i​o​n​ ​*​/​
}​;​

getpwnam() will give you the information for one user, mapping a login name to the corresponding passwd entry. getpwent() can be used to iterate through the password list. getpwuid() maps a numeric userID to the appropriate passwd entry.

File permissions

There are a number of bits associated with each file and directory. The nine most important of those bits are the file permissions. When you ls -l a file, you will see them:

$​ ​l​s​ ​-​l​ ​c​h​a​p​t​e​r​.​t​x​t​
-​r​w​-​r​-​-​r​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​5​2​6​5​5​ ​A​u​g​ ​1​1​ ​1​5​:​1​1​ ​c​h​a​p​t​e​r​.​t​x​t​

In particular, look at rw- r-- r-- three sets of three bits. The first set is permissions for the user who owns the file: markd, who can read from and write to the file. The second set is permissions for the group of the file: staff, who can read the file. Anyone who is a member of the staff group can read this file. The last set of bits is the permissions for all the others: anyone who is not markd and is not in the staff group.

Each of owner, group, and world can have these bits set:

r | Read
w | Write
x | Execute

The executable bit is the clue to the OS that a file is actually an executable program. There is not a magic file name suffix, like .EXE, that tells the OS that this file is executable.

Here are the permissions from some arbitrarily picked files from my file system:

$​ ​l​s​ ​-​l​ ​/​b​i​n​/​l​s​
-​r​-​x​r​-​x​r​-​x​ ​ ​1​ ​r​o​o​t​ ​ ​w​h​e​e​l​ ​ ​8​0​6​8​8​ ​J​u​l​ ​1​4​ ​ ​2​0​0​9​ ​/​b​i​n​/​l​s​*​

The user (root), group (wheel), and others can all read and execute the file. Nobody can write to it. The wheel group is used to designate system administrators on BSD systems, coming from the term “Big Wheel.”

$​ ​l​s​ ​-​l​ ​/​D​e​v​e​l​o​p​e​r​/​D​o​c​u​m​e​n​t​a​t​i​o​n​/​A​c​k​n​o​w​l​e​d​g​e​m​e​n​t​s​.​r​t​f​
-​r​w​-​r​w​-​r​-​-​ ​ ​1​ ​r​o​o​t​ ​ ​a​d​m​i​n​ ​ ​4​9​2​4​9​4​ ​M​a​y​ ​1​1​ ​ ​2​0​0​9​ ​\​
 ​ ​ ​ ​/​D​e​v​e​l​o​p​e​r​/​D​o​c​u​m​e​n​t​a​t​i​o​n​/​A​c​k​n​o​w​l​e​d​g​e​m​e​n​t​s​.​r​t​f​

The user (root) and group (admin) can each read and write the file. All others can only read it.

You will sometimes see file permissions expressed numerically. Each chunk of permissions is three bits, usually expressed in octal.

r | 100
w | 010
x | 001

So a permission of rw is binary 110, which has an octal value of 6. A permission of rwx is binary 111, which has an octal value of 7.

rwx rw- r-- is the same as 111 110 100, which is 764 in octal. Figure 11.7 shows some permission values, their octal representation, and what ls -l would show you.

Figure 11.7  Permission bits

The chmod command lets you change these permission bits, either in octal or symbolically:

$​ ​t​o​u​c​h​ ​t​e​s​t​
$​ ​l​s​ ​-​l​ ​t​e​s​t​
-​r​w​-​r​-​-​r​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​1​ ​1​5​:​2​3​ ​t​e​s​t​

$​ ​c​h​m​o​d​ ​7​7​5​ ​t​e​s​t​
$​ ​l​s​ ​-​l​ ​t​e​s​t​
-​r​w​x​r​-​x​r​-​x​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​1​ ​1​5​:​2​3​ ​t​e​s​t​

$​ ​c​h​m​o​d​ ​4​0​0​ ​t​e​s​t​
$​ ​l​s​ ​-​l​ ​t​e​s​t​
-​r​-​-​-​-​-​-​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​1​ ​1​5​:​2​3​ ​t​e​s​t​

There are symbols available for those folks who do not think in octal.

u | Modify user permissions.
g | Modify group permissions.
o | Modify other permissions.
+ | Set the bits that follow.
- | Clear the bits that follow.
r | Read.
w | Write.
x | Execute.

$​ ​c​h​m​o​d​ ​u​g​o​+​r​w​ ​t​e​s​t​

For user, group, and other, turn on the read and write bits.

$​ ​l​s​ ​-​l​ ​t​e​s​t​
-​r​w​-​r​w​-​r​w​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​1​ ​1​5​:​2​3​ ​t​e​s​t​

$​ ​c​h​m​o​d​ ​+​x​ ​t​e​s​t​

This is shorthand for everything.

$​ ​l​s​ ​-​l​ ​t​e​s​t​
-​r​w​x​r​w​x​r​w​x​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​1​ ​1​5​:​2​3​ ​t​e​s​t​*​

$​ ​c​h​m​o​d​ ​g​-​w​x​ ​t​e​s​t​

Turn off write and execute for group.

$​ ​l​s​ ​-​l​ ​t​e​s​t​
-​r​w​x​r​-​-​r​w​x​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​1​ ​1​5​:​2​3​ ​t​e​s​t​*​

There is also an API for affecting the permission bits:

int chmod (const char *path, mode_t mode);

int fchmod (int fd, mode_t mode);

where chmod() affects a file in the file system and fchmod() changes the permissions on a file you already have open.

There’s a whole slew of unpronounceable constants for specifying the mode:

#​d​e​f​i​n​e​ ​S​_​I​R​W​X​U​ ​0​0​0​0​7​0​0​ ​ ​ ​ ​/​*​ ​R​W​X​ ​m​a​s​k​ ​f​o​r​ ​o​w​n​e​r​ ​*​/​
#​d​e​f​i​n​e​ ​S​_​I​R​U​S​R​ ​0​0​0​0​4​0​0​ ​ ​ ​ ​/​*​ ​R​ ​f​o​r​ ​o​w​n​e​r​ ​*​/​
#​d​e​f​i​n​e​ ​S​_​I​W​U​S​R​ ​0​0​0​0​2​0​0​ ​ ​ ​ ​/​*​ ​W​ ​f​o​r​ ​o​w​n​e​r​ ​*​/​
#​d​e​f​i​n​e​ ​S​_​I​X​U​S​R​ ​0​0​0​0​1​0​0​ ​ ​ ​ ​/​*​ ​X​ ​f​o​r​ ​o​w​n​e​r​ ​*​/​

#​d​e​f​i​n​e​ ​S​_​I​R​W​X​G​ ​0​0​0​0​0​7​0​ ​ ​ ​ ​/​*​ ​R​W​X​ ​m​a​s​k​ ​f​o​r​ ​g​r​o​u​p​ ​*​/​
#​d​e​f​i​n​e​ ​S​_​I​R​G​R​P​ ​0​0​0​0​0​4​0​ ​ ​ ​ ​/​*​ ​R​ ​f​o​r​ ​g​r​o​u​p​ ​*​/​
#​d​e​f​i​n​e​ ​S​_​I​W​G​R​P​ ​0​0​0​0​0​2​0​ ​ ​ ​ ​/​*​ ​W​ ​f​o​r​ ​g​r​o​u​p​ ​*​/​
#​d​e​f​i​n​e​ ​S​_​I​X​G​R​P​ ​0​0​0​0​0​1​0​ ​ ​ ​ ​/​*​ ​X​ ​f​o​r​ ​g​r​o​u​p​ ​*​/​

#​d​e​f​i​n​e​ ​S​_​I​R​W​X​O​ ​0​0​0​0​0​0​7​ ​ ​ ​ ​/​*​ ​R​W​X​ ​m​a​s​k​ ​f​o​r​ ​o​t​h​e​r​ ​*​/​
#​d​e​f​i​n​e​ ​S​_​I​R​O​T​H​ ​0​0​0​0​0​0​4​ ​ ​ ​ ​/​*​ ​R​ ​f​o​r​ ​o​t​h​e​r​ ​*​/​
#​d​e​f​i​n​e​ ​S​_​I​W​O​T​H​ ​0​0​0​0​0​0​2​ ​ ​ ​ ​/​*​ ​W​ ​f​o​r​ ​o​t​h​e​r​ ​*​/​
#​d​e​f​i​n​e​ ​S​_​I​X​O​T​H​ ​0​0​0​0​0​0​1​ ​ ​ ​ ​/​*​ ​X​ ​f​o​r​ ​o​t​h​e​r​ ​*​/​

Notice that these are octal constants because they begin with zero.

So, to specify rwxrw-r--, you would assemble the permissions like this:

 ​ ​u​s​e​r​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​r​o​u​p​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​t​h​e​r​
 ​ ​ ​r​w​x​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​w​-​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​-​-​
 ​ ​ ​ ​7​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​6​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​4​
S​_​I​R​W​X​U​ ​|​ ​ ​(​S​_​I​R​G​R​P​ ​|​ ​S​_​I​W​G​R​P​)​ ​ ​|​ ​S​_​I​R​O​T​H​

There is one more complication that gets thrown into the mix – the umask set by the user. The user has control over what permissions are used when creating new files by using the umask command to specify a numeric (octal) value to specify the bits that should be left unset, as shown in Figure 11.8.

Figure 11.8  umask

For example:

$​ ​u​m​a​s​k​
2​2​
$​ ​t​o​u​c​h​ ​t​e​s​t​
$​ ​l​s​ ​-​l​ ​t​e​s​t​
-​r​w​-​r​-​-​r​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​1​ ​1​5​:​4​7​ ​t​e​s​t​

The umask “22” is binary 000 010 010. The middle bit is the “w” write bit. So a umask of 22 will remove the write bit for group and others. If you change the umask

$​ ​u​m​a​s​k​ ​0​0​2​
$​ ​u​m​a​s​k​
2​
$​ ​r​m​ ​t​e​s​t​
$​ ​t​o​u​c​h​ ​t​e​s​t​
$​ ​l​s​ ​-​l​ ​t​e​s​t​
-​r​w​-​r​w​-​r​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​1​ ​1​5​:​4​8​ ​t​e​s​t​

now only the “other” group of permissions has the write bit stripped out. If you are truly paranoid, you can use a very restrictive umask:

$​ ​u​m​a​s​k​ ​0​7​7​
$​ ​u​m​a​s​k​
7​7​
$​ ​r​m​ ​t​e​s​t​
$​ ​t​o​u​c​h​ ​t​e​s​t​
$​ ​l​s​ ​-​l​ ​t​e​s​t​
-​r​w​-​-​-​-​-​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​1​ ​1​5​:​4​9​ ​t​e​s​t​

077, which is all bits being set for group and other, causes all of those permission bits to be stripped out, so now only the owner has any access to the files.

A umask of 002 is most friendly if you are using groups to control permissions. You will not be creating files that cannot be written to by the group.

The umask() system call lets you change your processes’ umask on the fly.

mode_t umask (mode_t numask);

The umask is inherited by child processes, which is useful if you want to make the umask more restrictive before spawning off a subprocess.

chmod does not change the modification date of the file. The file itself is not affected, just the metadata.

There is one last commonly used permission bit associated with files, the “set-uid” bit. There are some operations that can only be performed with the permission of root, the superuser, such as modifying the password file. But some of these operations (like changing a password) you want ordinary mortals to be able to do. The way traditional Unix works around this problem is having a bit that says, “when you run this program, run it as if the user were the same user as the file’s owner, rather than running the program as the logged in user.”

If you look at /usr/bin/passwd:

$​ ​l​s​ ​-​l​ ​/​u​s​r​/​b​i​n​/​p​a​s​s​w​d​
-​r​-​s​r​-​x​r​-​x​ ​ ​1​ ​r​o​o​t​ ​ ​w​h​e​e​l​ ​ ​1​1​1​9​6​8​ ​J​u​n​ ​2​3​ ​ ​2​0​0​9​ ​/​u​s​r​/​b​i​n​/​p​a​s​s​w​d​*​

Notice the “s” in the user execute bit. That means when /usr/bin/passwd is run, it runs as root rather than as the logged-in user markd.

If you look at, say, one of the program files in the Oracle database:

$​ ​c​d​ ​$​O​R​A​C​L​E​_​H​O​M​E​/​b​i​n​
$​ ​l​s​ ​-​l​ ​o​r​a​c​l​e​
-​r​w​s​r​-​s​-​-​x​ ​ ​1​ ​ ​o​r​a​c​l​e​ ​ ​o​i​n​s​t​a​l​l​ ​2​3​3​8​9​2​6​1​ ​M​a​y​ ​1​3​ ​1​9​9​7​ ​o​r​a​c​l​e​*​

This is suid-oracle. When you run this program, it runs as if the logged in user were oracle. Also notice the second “s” in the group execute bit. That means the program will run as the group oinstall.

When a program runs, there are actually 6 (or more) IDs associated with it:

The real user ID and real group ID

This is who you really are, who you were when you logged in. These typically do not change during the life of a running program.

The effective user ID, effective group ID, supplementary group IDs

These are used for file permission checks. When you run an suid-root binary, your effective user ID is root.

The saved set-group-ID and saved set-user-ID

These are used by the exec functions, which will be discussed in Chapter 18: Multiprocessing.

Example 11.9. uid.m

/​/​ ​u​i​d​.​m​ ​-​-​ ​e​x​p​e​r​i​m​e​n​t​ ​w​i​t​h​ ​u​s​e​r​ ​a​n​d​ ​g​r​o​u​p​ ​i​d​s​.​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​u​n​ ​a​s​ ​n​o​r​m​a​l​,​ ​t​h​e​n​ ​c​h​a​n​g​e​ ​t​h​i​s​ ​t​o​ ​b​e​ ​s​u​i​d​,​ ​a​n​d​ ​r​u​n​ ​a​g​a​i​n​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​u​i​d​ ​u​i​d​.​m​

#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​u​c​t​ ​g​r​o​u​p​/​s​t​r​u​c​t​ ​p​a​s​s​w​d​
#​i​m​p​o​r​t​ ​<​g​r​p​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​g​e​t​g​r​g​i​d​
#​i​m​p​o​r​t​ ​<​p​w​d​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​g​e​t​p​w​u​i​d​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​p​r​i​n​t​f​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​g​e​t​u​i​d​(​)​,​ ​e​t​c​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​u​i​d​_​t​ ​u​s​e​r​_​i​d​ ​=​ ​g​e​t​u​i​d​ ​(​)​;​
 ​ ​ ​ ​u​i​d​_​t​ ​e​f​f​e​c​t​i​v​e​_​u​s​e​r​_​i​d​ ​=​ ​g​e​t​e​u​i​d​ ​(​)​;​

 ​ ​ ​ ​g​i​d​_​t​ ​g​r​o​u​p​_​i​d​ ​=​ ​g​e​t​g​i​d​ ​(​)​;​
 ​ ​ ​ ​g​i​d​_​t​ ​e​f​f​e​c​t​i​v​e​_​g​r​o​u​p​_​i​d​ ​=​ ​g​e​t​e​g​i​d​ ​(​)​;​

 ​ ​ ​ ​s​t​r​u​c​t​ ​p​a​s​s​w​d​ ​*​u​s​e​r​ ​=​ ​g​e​t​p​w​u​i​d​ ​(​u​s​e​r​_​i​d​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​r​e​a​l​ ​u​s​e​r​ ​I​D​ ​i​s​ ​'​%​s​'​\​n​"​,​ ​u​s​e​r​-​>​p​w​_​n​a​m​e​)​;​

 ​ ​ ​ ​u​s​e​r​ ​=​ ​g​e​t​p​w​u​i​d​ ​(​e​f​f​e​c​t​i​v​e​_​u​s​e​r​_​i​d​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​e​f​f​e​c​t​i​v​e​ ​u​s​e​r​ ​I​D​ ​i​s​ ​'​%​s​'​\​n​"​,​ ​u​s​e​r​-​>​p​w​_​n​a​m​e​)​;​

 ​ ​ ​ ​s​t​r​u​c​t​ ​g​r​o​u​p​ ​*​g​r​o​u​p​ ​=​ ​g​e​t​g​r​g​i​d​ ​(​g​r​o​u​p​_​i​d​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​r​e​a​l​ ​g​r​o​u​p​ ​i​s​ ​'​%​s​'​\​n​"​,​ ​g​r​o​u​p​-​>​g​r​_​n​a​m​e​)​;​

 ​ ​ ​ ​g​r​o​u​p​ ​=​ ​g​e​t​g​r​g​i​d​ ​(​e​f​f​e​c​t​i​v​e​_​g​r​o​u​p​_​i​d​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​e​f​f​e​c​t​i​v​e​ ​g​r​o​u​p​ ​i​s​ ​'​%​s​'​\​n​"​,​ ​g​r​o​u​p​-​>​g​r​_​n​a​m​e​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

Here is a run just as markd:

$​ ​l​s​ ​-​l​ ​u​i​d​
-​r​w​x​r​-​x​r​-​x​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​9​2​1​6​ ​M​a​y​ ​2​6​ ​1​5​:​5​4​ ​u​i​d​*​
$​ ​.​/​u​i​d​
r​e​a​l​ ​u​s​e​r​ ​I​D​ ​i​s​ ​'​m​a​r​k​d​'​
e​f​f​e​c​t​i​v​e​ ​u​s​e​r​ ​I​D​ ​i​s​ ​'​m​a​r​k​d​'​
r​e​a​l​ ​g​r​o​u​p​ ​i​s​ ​'​s​t​a​f​f​'​
e​f​f​e​c​t​i​v​e​ ​g​r​o​u​p​ ​i​s​ ​'​s​t​a​f​f​'​

Now run with sudo, so that it is actually run as root:

$​ ​s​u​d​o​ ​.​/​u​i​d​
r​e​a​l​ ​u​s​e​r​ ​I​D​ ​i​s​ ​'​r​o​o​t​'​
e​f​f​e​c​t​i​v​e​ ​u​s​e​r​ ​I​D​ ​i​s​ ​'​r​o​o​t​'​
r​e​a​l​ ​g​r​o​u​p​ ​i​s​ ​'​w​h​e​e​l​'​
e​f​f​e​c​t​i​v​e​ ​g​r​o​u​p​ ​i​s​ ​'​w​h​e​e​l​'​

Now make it suid root:

$​ ​s​u​d​o​ ​c​h​o​w​n​ ​r​o​o​t​:​w​h​e​e​l​ ​u​i​d​
$​ ​s​u​d​o​ ​c​h​m​o​d​ ​u​g​+​s​ ​u​i​d​
$​ ​l​s​ ​-​l​ ​u​i​d​
-​r​w​s​r​-​s​r​-​x​ ​ ​1​ ​r​o​o​t​ ​ ​w​h​e​e​l​ ​ ​9​2​1​6​ ​M​a​y​ ​2​6​ ​1​5​:​5​4​ ​u​i​d​*​
(​n​o​t​e​ ​t​h​e​ ​s​ ​b​i​t​s​)​
$​ ​.​/​u​i​d​
r​e​a​l​ ​u​s​e​r​ ​I​D​ ​i​s​ ​'​m​a​r​k​d​'​
e​f​f​e​c​t​i​v​e​ ​u​s​e​r​ ​I​D​ ​i​s​ ​'​r​o​o​t​'​
r​e​a​l​ ​g​r​o​u​p​ ​i​s​ ​'​s​t​a​f​f​'​
e​f​f​e​c​t​i​v​e​ ​g​r​o​u​p​ ​i​s​ ​'​w​h​e​e​l​'​

So now this program is running with privileges of root when run by an ordinary user.

Finally, let's try it as another ordinary user:

$​ ​s​u​d​o​ ​c​h​o​w​n​ ​b​o​r​k​:​a​d​m​i​n​ ​u​i​d​
$​ ​l​s​ ​-​l​ ​u​i​d​
-​r​w​s​r​-​s​r​-​x​ ​ ​1​ ​b​o​r​k​ ​ ​a​d​m​i​n​ ​ ​9​2​1​6​ ​M​a​y​ ​2​6​ ​1​5​:​5​4​ ​u​i​d​*​

$​ ​.​/​u​i​d​
r​e​a​l​ ​u​s​e​r​ ​I​D​ ​i​s​ ​'​m​a​r​k​d​'​
e​f​f​e​c​t​i​v​e​ ​u​s​e​r​ ​I​D​ ​i​s​ ​'​b​o​r​k​'​
r​e​a​l​ ​g​r​o​u​p​ ​i​s​ ​'​s​t​a​f​f​'​
e​f​f​e​c​t​i​v​e​ ​g​r​o​u​p​ ​i​s​ ​'​a​d​m​i​n​'​

One thing to note is that the setuid bits get cleared if a non-privileged program writes to the setuid file.

$​ ​l​s​ ​-​l​ ​u​i​d​
-​r​w​s​r​-​s​r​-​x​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​9​2​1​6​ ​M​a​y​ ​2​6​ ​1​5​:​5​4​ ​u​i​d​*​
$​ ​c​a​t​ ​>​>​ ​u​i​d​
1​ ​[​c​o​n​t​r​o​l​-​D​ ​f​o​r​ ​e​n​d​ ​o​f​ ​f​i​l​e​]​
$​ ​l​s​ ​-​l​ ​u​i​d​
-​r​w​x​r​-​x​r​-​x​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​9​2​1​7​ ​M​a​y​ ​2​6​ ​1​5​:​5​8​ ​u​i​d​*​

That is a security measure to keep some malicious program from replacing a setuid program and running hostile code with root privileges. Also, programs on disk images, such as FileValue volumes, will not be allowed to become setuid-root.

setuid-root is generally bad. Apple’s sanctioned way of performing privileged operations is to have a launchd-controlled daemon that does the heavy lifting and use interprocess communication to tell the daemon to do the work.

Directory Permissions

Directories have the same permission bits as files, rwx for user, group, and other, but with slightly different interpretations. You cannot run directories as programs, so the execute bit takes on a different meaning: it acts like a search bit. When opening any type of file by path, the user must have execute permission on every directory mentioned in the path. The execute bit can match the user, group, or other. Read permission is different from the execute/search permission. Read permission lets you read the directory itself, like with the ls command, to obtain a list of all the file names in the directory. Execute just means that you can pass through, or execute programs in that directory. You do not need read permissions along the way.

As an example, you create a directory and copy a program into it

$​ ​m​k​d​i​r​ ​p​e​r​m​t​e​s​t​
$​ ​c​p​ ​/​b​i​n​/​h​o​s​t​n​a​m​e​ ​p​e​r​m​t​e​s​t​
$​ ​c​h​m​o​d​ ​-​r​w​ ​p​e​r​m​t​e​s​t​
$​ ​l​s​ ​-​l​d​ ​p​e​r​m​t​e​s​t​
d​-​-​x​-​-​x​-​-​x​ ​ ​3​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​5​8​ ​A​u​g​ ​1​1​ ​1​6​:​5​2​ ​p​e​r​m​t​e​s​t​/​

Now you have turned off everything but the search bits.

$​ ​l​s​ ​p​e​r​m​t​e​s​t​
l​s​:​ ​p​e​r​m​t​e​s​t​:​ ​P​e​r​m​i​s​s​i​o​n​ ​d​e​n​i​e​d​

No read permission, so you get an error.

$​ ​.​/​p​e​r​m​t​e​s​t​/​h​o​s​t​n​a​m​e​
l​o​c​a​l​h​o​s​t​

But you can still run programs in there.

Permission-Check Algorithms

Here is a quick overview of how permissions interact with the file operations discussed earlier:

You must have read permission to open a file for reading with O_RDONLY or O_RDWR.

You must have write permission to open a file for writing with O_WRONLY and O_RDWR.

You must have write permission to O_TRUNC a file.

You must have write and execute permission on a directory to create a new file. You need write permission so you can modify the directory entry, and you need execute permission so the directory can be searched.

You must have write and execute permission on a directory to delete a file. You do not actually need read/write permissions on the file itself because the operation just manipulates the directory’s data structures.

You must have the execute bit set to run a file program as a program.

For accessing a file:

If the effective user ID is zero, which is the superuser / root ID, access is allowed.

If the effective user ID is the same as the owner ID of the file, check the appropriate permission bit and allow access.

If the effective group ID or supplementary group ID is the same as the group ID of the file, check the appropriate permission bit and allow access.

If the appropriate access permission bit is set, allow access.

Otherwise, do not allow access.

For the More Curious: Memory-Mapped Files

Memory-mapped files are a blend of the virtual memory system and the file system, where a file’s contents are mapped onto a range of bytes in memory. When you get bytes from that range of memory, you are actually reading from the file. When you change bytes in that range of memory, you are writing to the file. In essence, you are doing I/O without using read() and write(). The virtual memory system gets to do the work of buffering, reading, and writing, only bringing in the blocks from disk that are actually used. To use memory-mapped files, you need to open the file, then call

caddr_t mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset);

The return value is the address where the mapped file starts. Otherwise, -1 is returned, and errno is set.

Here are the arguments:

addr | A non-zero value hints to the OS where to start mapping the file in memory. mmap() is free to use or ignore this value. Usually, you just pass in zero.
len | The number of bytes to map from the file. If you have a huge file, you can map just a portion of it to make things faster.

prot

Protection on the mapped memory. Pass a bitwise OR of one or more of these values:

PROT_EXEC

Code may be executed on the mapped pages. This is available only on Mac OS X because iOS does not allow arbitrary execution.

PROT_READ

You can read from the pages.

PROT_WRITE

You can write to the pages.

These flags need to match the open mode of the file. For instance, you cannot PROT_WRITE a read-only file.

flags

Specifies various options. Here are the commonly used flags:

MAP_FIXED

Return value must equal addr. If that is not possible, an error is returned.

MAP_SHARED

Storing bytes in memory will modify the mapped file. Be sure to set this if you expect your memory writes to be reflected back in the file.

MAP_PRIVATE

Storing bytes in memory causes a copy of the mapped pages to be made, and all subsequent references reference the copy.

MAP_FILE

Map from a regular file or a character special device. This is the default and does not need to be specified.

MAP_INHERIT

Permit mapped regions to be mapped across exec() system calls so you can pass the mapped regions onto child processes.

fd | The file descriptor for the open file.
offset | The number of bytes into the file to start mapping. Usually, you just pass in zero.

To unmap pages, use

int munmap (caddr_t addr, size_t len);

This returns zero on success, -1/setting errno on error. You will generate invalid memory references if you try to access this memory after the munmap(). close() does not unmap pages, but pages will get unmapped when your program exits.

addr and offset should be multiples of the system’s virtual memory page size. This should be 512 bytes, but use sysconf() to figure it out.

Use msync() to flush modified pages back to the file system:

int msync (void *addr, size_t len, int flags);

If len is zero, all modified pages will be flushed. Possible flag values are

MS_ASYNC | Return immediately and let the write happen at the kernel’s convenience.
MS_SYNC | Perform synchronous writes.
MS_INVALIDATE | Invalidate all cached data (presumably to force a re-read from the file).

Some rules to remember:

You can memory-map regular files but not networked file descriptors or device files.

You need to be careful if the size of the underlying file could change after it gets mapped; otherwise, memory access errors might be triggered.

You cannot use mmap() and memory writes to memory to extend files. You will need to seek and write to accomplish that.

Example 11.10 is a program that opens files, mmap()s them, and then walks the memory, performing the “rot-13” encryption on them. Rot-13 (short for “rotate 13”) is a simple letter substitution cypher. It replaces letters with those that are 13 positions ahead of it in the alphabet. rot-13 is reversible. If you rot-13 text, you can rot-13 it again to get the original text back.

Example 11.10. mmap-rot13.m

/​/​ ​m​m​a​p​-​r​o​t​1​3​.​m​ ​-​-​ ​u​s​e​ ​m​e​m​o​r​y​ ​m​a​p​p​e​d​ ​I​/​O​ ​t​o​ ​a​p​p​l​y​ ​t​h​e​ ​r​o​t​ ​1​3​ ​'​e​n​c​r​y​p​t​i​o​n​'​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​l​g​o​r​i​t​h​m​ ​t​o​ ​a​ ​f​i​l​e​.​

/​/​ ​g​c​c​ ​-​g​ ​-​s​t​d​=​c​9​9​ ​-​W​a​l​l​ ​-​o​ ​m​m​a​p​-​r​o​t​1​3​ ​m​m​a​p​-​r​o​t​1​3​.​m​

#​i​m​p​o​r​t​ ​<​c​t​y​p​e​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​i​s​a​l​p​h​a​(​)​,​ ​e​t​c​
#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​,​ ​e​t​c​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​,​ ​e​t​c​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​(​)​
#​i​m​p​o​r​t​ ​<​s​y​s​/​f​c​n​t​l​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​O​_​R​D​W​R​ ​a​n​d​ ​o​p​e​n​(​)​
#​i​m​p​o​r​t​ ​<​s​y​s​/​m​m​a​n​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​m​m​a​p​,​ ​e​t​c​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​t​a​t​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​f​s​t​a​t​(​)​ ​a​n​d​ ​s​t​r​u​c​t​ ​s​t​a​t​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​c​a​d​d​r​_​t​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​c​l​o​s​e​(​)​

/​/​ ​w​a​l​k​ ​t​h​e​ ​b​u​f​f​e​r​ ​s​h​i​f​t​i​n​g​ ​a​l​p​h​a​b​e​t​i​c​ ​c​h​a​r​a​c​t​e​r​s​ ​1​3​ ​p​l​a​c​e​s​
v​o​i​d​ ​r​o​t​1​3​ ​(​c​a​d​d​r​_​t​ ​b​a​s​e​,​ ​s​i​z​e​_​t​ ​l​e​n​g​t​h​)​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​*​s​c​a​n​ ​=​ ​b​a​s​e​;​
 ​ ​ ​ ​c​h​a​r​ ​*​s​t​o​p​ ​=​ ​s​c​a​n​ ​+​ ​l​e​n​g​t​h​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​s​c​a​n​ ​<​ ​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​t​h​e​r​e​ ​a​r​e​ ​t​o​n​s​ ​o​f​ ​i​m​p​l​e​m​e​n​t​a​t​i​o​n​s​ ​o​f​ ​r​o​t​1​3​ ​o​u​t​ ​o​n​ ​t​h​e​ ​n​e​t​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​m​u​c​h​ ​m​o​r​e​ ​c​o​m​p​a​c​t​ ​t​h​a​n​ ​t​h​i​s​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​i​s​a​l​p​h​a​(​*​s​c​a​n​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​(​*​s​c​a​n​ ​>​=​ ​'​A​'​ ​&​&​ ​*​s​c​a​n​ ​<​=​ ​'​M​'​)​ ​|​|​ ​(​*​s​c​a​n​ ​>​=​ ​'​a​'​ ​&​&​ ​*​s​c​a​n​ ​<​=​ ​'​m​'​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​*​s​c​a​n​ ​+​=​ ​1​3​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​(​*​s​c​a​n​ ​>​=​ ​'​N​'​ ​&​&​ ​*​s​c​a​n​ ​<​=​ ​'​Z​'​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​|​|​ ​(​*​s​c​a​n​ ​>​=​ ​'​n​'​ ​&​&​ ​*​s​c​a​n​ ​<​=​ ​'​z​'​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​*​s​c​a​n​ ​-​=​ ​1​3​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​+​+​;​
 ​ ​ ​ ​}​

}​ ​/​/​ ​r​o​t​1​3​

v​o​i​d​ ​p​r​o​c​e​s​s​F​i​l​e​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​f​i​l​e​n​a​m​e​)​ ​{​
 ​ ​ ​ ​/​/​ ​o​p​e​n​ ​t​h​e​ ​f​i​l​e​ ​f​i​r​s​t​
 ​ ​ ​ ​i​n​t​ ​f​d​ ​=​ ​o​p​e​n​ ​(​f​i​l​e​n​a​m​e​,​ ​O​_​R​D​W​R​)​;​
 ​ ​ ​ ​i​f​ ​(​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​o​p​e​n​ ​%​s​:​ ​e​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​i​l​e​n​a​m​e​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​f​i​g​u​r​e​ ​o​u​t​ ​h​o​w​ ​b​i​g​ ​i​t​ ​i​s​
 ​ ​ ​ ​s​t​r​u​c​t​ ​s​t​a​t​ ​s​t​a​t​b​u​f​;​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​f​s​t​a​t​ ​(​f​d​,​ ​&​s​t​a​t​b​u​f​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​f​s​t​a​t​ ​o​f​ ​%​s​ ​f​a​i​l​e​d​:​ ​e​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​i​l​e​n​a​m​e​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​s​i​z​e​_​t​ ​l​e​n​g​t​h​ ​=​ ​s​t​a​t​b​u​f​.​s​t​_​s​i​z​e​;​

 ​ ​ ​ ​/​/​ ​m​m​a​p​ ​i​t​
 ​ ​ ​ ​c​a​d​d​r​_​t​ ​b​a​s​e​ ​=​ ​m​m​a​p​ ​(​N​U​L​L​,​ ​l​e​n​g​t​h​,​ ​P​R​O​T​_​R​E​A​D​ ​|​ ​P​R​O​T​_​W​R​I​T​E​,​ ​M​A​P​_​S​H​A​R​E​D​,​ ​f​d​,​ ​0​)​;​
 ​ ​ ​ ​i​f​ ​(​b​a​s​e​ ​=​=​ ​(​c​a​d​d​r​_​t​)​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​m​m​a​p​ ​%​s​:​ ​e​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​i​l​e​n​a​m​e​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​b​i​t​r​o​t​ ​i​t​.​
 ​ ​ ​ ​r​o​t​1​3​ ​(​b​a​s​e​,​ ​l​e​n​g​t​h​)​;​

 ​ ​ ​ ​/​/​ ​f​l​u​s​h​ ​t​h​e​ ​r​e​s​u​l​t​s​
 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​m​s​y​n​c​ ​(​b​a​s​e​,​ ​l​e​n​g​t​h​,​ ​M​S​_​S​Y​N​C​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​m​s​y​n​c​ ​f​a​i​l​e​d​ ​f​o​r​ ​%​s​:​ ​e​r​r​o​r​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​i​l​e​n​a​m​e​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​/​/​ ​c​l​e​a​n​ ​u​p​ ​a​n​y​ ​m​e​s​s​e​s​ ​w​e​'​v​e​ ​m​a​d​e​
 ​ ​ ​ ​i​f​ ​(​b​a​s​e​ ​!​=​ ​(​c​a​d​d​r​_​t​)​ ​-​1​)​ ​m​u​n​m​a​p​ ​(​b​a​s​e​,​ ​l​e​n​g​t​h​)​;​
 ​ ​ ​ ​i​f​ ​(​f​d​ ​!​=​ ​-​1​)​ ​c​l​o​s​e​ ​(​f​d​)​;​

}​ ​/​/​ ​p​r​o​c​e​s​s​F​i​l​e​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​=​=​ ​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​s​a​g​e​:​ ​%​s​ ​/​p​a​t​h​/​t​o​/​f​i​l​e​ ​.​.​.​ ​\​n​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​r​o​t​-​1​3​s​ ​f​i​l​e​s​ ​i​n​-​p​l​a​c​e​ ​u​s​i​n​g​ ​m​e​m​o​r​y​ ​m​a​p​p​e​d​ ​i​/​o​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​e​x​i​t​ ​(​E​X​I​T​_​F​A​I​L​U​R​E​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​1​;​ ​i​ ​<​ ​a​r​g​c​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​o​c​e​s​s​F​i​l​e​ ​(​a​r​g​v​[​i​]​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​e​x​i​t​ ​(​E​X​I​T​_​S​U​C​C​E​S​S​)​;​

}​ ​/​/​ ​m​a​i​n​

And of course, a sample run or two:

$​ ​c​a​t​ ​>​ ​b​l​o​r​f​
B​l​o​r​f​ ​i​s​ ​t​h​e​ ​n​a​m​e​ ​o​f​ ​a​ ​b​u​n​n​y​ ​r​a​b​b​i​t​.​

$​ ​.​/​m​m​a​p​-​r​o​t​1​3​ ​b​l​o​r​f​
$​ ​c​a​t​ ​b​l​o​r​f​
O​y​b​e​s​ ​v​f​ ​g​u​r​ ​a​n​z​r​ ​b​s​ ​n​ ​o​h​a​a​l​ ​e​n​o​o​v​g​.​

$​ ​.​/​m​m​a​p​-​r​o​t​1​3​ ​b​l​o​r​f​
$​ ​c​a​t​ ​b​l​o​r​f​
B​l​o​r​f​ ​i​s​ ​t​h​e​ ​n​a​m​e​ ​o​f​ ​a​ ​b​u​n​n​y​ ​r​a​b​b​i​t​.​

12
Files, Part 2: Directories, File Systems, and Links

Directories are the counterparts to files. Files are containers of information, while directories are containers of files. Directories can be nested inside of other directories, giving the hierarchical structure to the file system. The file system itself is conceptually built upon data blocks which store information, and inodes that store metadata. Both files and directories make use of data blocks and inodes. Links (hard links and symbolic links) allow a file or directory to appear in more than one location.

Directories

Compared to files, there is very little that can be done to directories. You can create them, remove them, and iterate through their contents.

Creation and destruction

int mkdir (const char *path, mode_t mode);

mkdir() creates a new directory at the given path. The permissions on the directory are specified by mode using the S_I* constants discussed with chmod() in section File permissions. The umask is applied, clearing bits in the mode that are set in the umask, and do not forget to set the execution bits. mkdir() returns zero on success, -1 on error with errno set as appropriate. mkdir() does not create intervening directories if they do not exist, returning an error instead.

int rmdir (const char *path);

rmdir() removes the directory at path. The directory must be empty of any files or subdirectories; otherwise, an error will be returned. Like mkdir(), zero is returned on success and -1 will be returned on error with errno set.

Directory iteration

The opendir() Family

These are the functions you use to iterate through the contents of a directory:

DIR *opendir (const char *filename);

struct dirent *readdir(DIR *dirp);

long telldir (const DIR *dirp);

void seekdir (DIR *dirp, long loc);

void rewinddir (DIR *dirp);

int closedir (DIR *dirp);

The typical use of this API is to call opendir(), supplying the path you want a director listing for. opendir() returns an opaque DIR handle whose resources are released by closedir(). Call readdir() in a loop until it tells you it is done by returning NULL. telldir() lets you know where you are in a directory stream. seekdir() will move the position of the directory stream to the indicated location, and rewinddir() will reset the position to the beginning of the directory. The struct dirent returned from readdir() looks like:

s​t​r​u​c​t​ ​d​i​r​e​n​t​ ​{​
 ​ ​ ​ ​i​n​o​_​t​ ​ ​ ​ ​ ​ ​d​_​f​i​l​e​n​o​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​f​i​l​e​ ​n​u​m​b​e​r​ ​(​i​n​o​d​e​)​ ​o​f​ ​e​n​t​r​y​ ​*​/​
 ​ ​ ​ ​u​i​n​t​1​6​_​t​ ​ ​ ​d​_​r​e​c​l​e​n​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​l​e​n​g​t​h​ ​o​f​ ​t​h​i​s​ ​r​e​c​o​r​d​ ​*​/​
 ​ ​ ​ ​u​i​n​t​8​_​t​ ​ ​ ​ ​d​_​t​y​p​e​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​t​h​e​ ​t​y​p​e​ ​o​f​ ​t​h​e​ ​f​i​l​e​ ​ ​*​/​
 ​ ​ ​ ​u​i​n​t​8​_​t​ ​ ​ ​ ​d​_​n​a​m​l​e​n​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​l​e​n​g​t​h​ ​o​f​ ​s​t​r​i​n​g​ ​i​n​ ​d​_​n​a​m​e​ ​*​/​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​ ​ ​ ​d​_​n​a​m​e​[​M​A​X​N​A​M​L​E​N​ ​+​ ​1​]​;​ ​ ​/​*​ ​n​a​m​e​ ​o​f​ ​t​h​e​ ​f​i​l​e​ ​*​/​
}​;​

The size of the integer fields is larger when building in 64-bit mode.

d_name, the name of the file, is the piece of information that is most interesting. On HFS+ file systems, file names are returned in alphabetical order, but this isn’t a guarantee provided by the API.

Example 12.1 is a cheap version of ls, without any features:

Example 12.1. cheapls.m

/​/​ ​c​h​e​a​p​l​s​.​m​ ​-​-​ ​a​ ​f​e​a​t​u​r​e​l​e​s​s​ ​l​s​ ​p​r​o​g​r​a​m​ ​u​s​i​n​g​ ​t​h​e​ ​d​i​r​e​c​t​o​r​y​ ​i​t​e​r​a​t​i​o​n​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​u​n​c​t​i​o​n​s​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​W​e​x​t​r​a​ ​-​o​ ​c​h​e​a​p​l​s​ ​c​h​e​a​p​l​s​.​m​

#​i​m​p​o​r​t​ ​<​d​i​r​e​n​t​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​o​p​e​n​d​i​r​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​
#​i​m​p​o​r​t​ ​<​s​y​s​/​d​i​r​e​n​t​.​h​>​ ​ ​/​/​ ​f​o​r​ ​s​t​r​u​c​t​ ​d​i​r​e​n​t​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​r​a​n​d​o​m​ ​t​y​p​e​ ​d​e​f​i​n​i​t​i​o​n​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​!​=​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​s​a​g​e​:​ ​ ​%​s​ ​/​p​a​t​h​/​t​o​/​d​i​r​e​c​t​o​r​y​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​D​I​R​ ​*​d​i​r​e​c​t​o​r​y​ ​=​ ​o​p​e​n​d​i​r​ ​(​a​r​g​v​[​1​]​)​;​
 ​ ​ ​ ​i​f​ ​(​d​i​r​e​c​t​o​r​y​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​o​p​e​n​ ​d​i​r​e​c​t​o​r​y​ ​'​%​s​'​\​n​"​,​ ​a​r​g​v​[​1​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​ ​i​s​ ​i​s​ ​u​s​e​f​u​l​:​ ​%​d​ ​(​%​s​)​\​n​"​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​s​t​r​u​c​t​ ​d​i​r​e​n​t​ ​*​e​n​t​r​y​;​
 ​ ​ ​ ​w​h​i​l​e​ ​(​(​e​n​t​r​y​ ​=​ ​r​e​a​d​d​i​r​(​d​i​r​e​c​t​o​r​y​)​)​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​l​o​n​g​ ​p​o​s​i​t​i​o​n​ ​=​ ​t​e​l​l​d​i​r​ ​(​d​i​r​e​c​t​o​r​y​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​3​l​d​:​ ​%​s​\​n​"​,​ ​p​o​s​i​t​i​o​n​,​ ​e​n​t​r​y​-​>​d​_​n​a​m​e​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​c​l​o​s​e​d​i​r​ ​(​d​i​r​e​c​t​o​r​y​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​ ​c​l​o​s​i​n​g​ ​d​i​r​e​c​t​o​r​y​:​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​/​/​ ​m​a​i​n​

And some sample runs (edited):

$​ ​.​/​c​h​e​a​p​l​s​ ​.​
 ​ ​1​:​ ​.​
 ​ ​2​:​ ​.​.​
 ​ ​3​:​ ​#​u​i​d​.​m​#​
 ​ ​4​:​ ​.​#​c​h​a​p​t​e​r​.​t​x​t​
 ​ ​5​:​ ​.​#​u​i​d​.​m​
 ​ ​6​:​ ​.​D​S​_​S​t​o​r​e​
 ​ ​7​:​ ​.​g​d​b​_​h​i​s​t​o​r​y​
 ​ ​8​:​ ​a​c​c​e​s​s​
 ​ ​9​:​ ​a​c​c​e​s​s​.​m​
 ​1​0​:​ ​a​c​c​e​s​s​.​m​~​
 ​1​1​:​ ​a​p​p​e​n​d​
 ​1​2​:​ ​a​p​p​e​n​d​.​m​
 ​ ​ ​ ​ ​.​.​.​
 ​4​9​:​ ​w​r​i​t​e​s​t​r​i​n​g​.​m​
 ​5​0​:​ ​w​r​i​t​e​v​e​c​s​t​r​i​n​g​
 ​5​1​:​ ​w​r​i​t​e​v​e​c​s​t​r​i​n​g​.​m​

$​ ​.​/​c​h​e​a​p​l​s​ ​/​D​e​v​e​l​o​p​e​r​/​A​p​p​l​i​c​a​t​i​o​n​s​
 ​ ​1​:​ ​.​
 ​ ​2​:​ ​.​.​
 ​ ​3​:​ ​.​D​S​_​S​t​o​r​e​
 ​ ​4​:​ ​A​u​d​i​o​
 ​ ​5​:​ ​D​a​s​h​c​o​d​e​.​a​p​p​
 ​ ​6​:​ ​G​r​a​p​h​i​c​s​ ​T​o​o​l​s​
 ​ ​7​:​ ​I​n​s​t​r​u​m​e​n​t​s​.​a​p​p​
 ​ ​8​:​ ​P​e​r​f​o​r​m​a​n​c​e​ ​T​o​o​l​s​
 ​ ​9​:​ ​Q​u​a​r​t​z​ ​C​o​m​p​o​s​e​r​.​a​p​p​
 ​1​0​:​ ​U​t​i​l​i​t​i​e​s​
 ​1​1​:​ ​X​c​o​d​e​.​a​p​p​

Current working directory

The current working directory is the default location used for file operations that don’t specify a full path. If a partial path is supplied to a function, such as open(), the full path is constructed by appending the path to the current working directory. You use this concept all the time in the shell when you cd to a directory and perform operations without specifying a full path, that is, a path beginning with a slash character.

int chdir (const char *path);

int fchdir (int fd);

chdir() sets your current working directory to path, while fchdir() uses a file descriptor of an open directory. On success, it returns zero, and on error, it returns -1 with errno set appropriately.

getcwd() tells you what the current directory is:

char *getcwd (char *buf, size_t size);

Pass it a buffer and the buffer size. You should make sure that buf is MAXPATHLEN bytes or larger. You can also pass NULL and have the function allocate memory on your behalf. Make sure you free() it.

buf is returned after a successful call, with NULL returned in case of error and errno set. The getcwd() manpage warns not to use getcwd() to save a directory for the purpose of returning to it, like when using the pushd command in a shell. Instead, you should open the current directory (“.”), stash away the file descriptor, and then use fchdir() to return to it.

The current working directory is a global value, so it is not a thread-safe concept. One thread could change the working directory and then get pre-empted by another thread, which then changes the working directory. When the first thread gets control, the current working directory will be wrong. If you are going to be dealing with multiple directories in a threaded app, use full path names.

Inside The File System

Figure 12.1 shows that the physical disk is divided up into partitions:

Figure 12.1  Partitions

Figure 12.2 shows an idealized Unix file system on a particular partition. HFS+ is actually implemented differently, but this mental model will help you understand the upcoming system calls.

Figure 12.2  Filesystem

The superblock is a block in a known location that has pointers to the rest of the on-disk data structures. Frequently there are redundant copies of the superblock in case the primary superblock gets destroyed.

An inode, short for “indirect block,” is the handle used to reference an individual file, as shown in Figure 12.3. Each file has an inode that contains the metadata for the file like the size, modification dates, etc., as well as a list of datablocks that compose the file. The inode has a link count, which is a reference count of the number of directory entries that point to the inode. The file is actually deleted, and its data blocks recycled, when this count goes to zero.

Figure 12.3  inodes

A data block can contain either file data or directory data, as shown in Figure 12.4. Directory blocks have a flag saying that this block represents a directory. A directory is just a list of file names and inode addresses. The directory does not actually store a copy of the inode itself. When you iterate through a directory, you are picking up the filename from the directory and then you can find the actual file by using the inode.

Figure 12.4  Directory structure

The special files in directories are just inode references. For instance, “.” (the current directory) is the inode that references the directory, and “..” (the parent directory) references the enclosing directory. Since the inode points within a particular file system, you cannot have a directory with files that cross file system boundaries. This means that moving a file to a new place in the current filesystem happens because of a couple of directory block manipulations. No data is actually copied. If you move a file to a different filesystem, the data needs to be copied.

There are some points to be taken from this. Since files are referenced by their inode number, which you can see when you do an ls -i, a file can physically appear in more than one place. These are hard links, which are covered below. It also means that there is a finite pool of inodes available. You can use df -i to see the number of inodes in use and free. If you run out of inodes, you will not be able to create any more files on that file system even if there is plenty of space. When you create new file systems from scratch, you need to decide on the inode density. For a file system that will primarily be storing digital video, you do not need a whole lot of inodes since you will have relatively few individual files. If you are creating a file system to store a usenet newsfeed, you will want to have a much higher density of inodes, since newsfeeds tend to have a huge number of tiny files. For typical consumer use, the defaults strike a good balance between inode density and available disk space.

There are some hard-coded limits regarding the filesystem, particularly things like file names and path names. Path names are limited to the constant PATH_MAX, which is defined as 1024 bytes. This is an unfortunate choice, since languages like Korean have 3-byte characters in UTF-8. Likewise, the maximum size for a file name is 1024 bytes.

Standard Unix file systems are not journaled. Journaled filesystems always keep the metadata on the disk in a consistent state. Mac HFS+ has had journaling support since OS X 10.3.

Links

A “link” is a way reference a file from more than one place. You may have software that makes assumptions about where a file lives in the filesystem, but it might be more convenient for that file to actually live elsewhere, perhaps on another file system. You may also have a large file, say a video tutorial, appear in each user’s home directory on a multi-user machine, but you do not want the disk space consumed for multiple copies.

Unix has two kinds of links: hard links and symbolic links. OS X also brings aliases to the table.

Hard links

As mentioned earlier in the brief overview of the file system, hard links are entries in multiple directories that all refer to the same inode. The file appears in multiple places at once. Use the ln command to create a hard link:

$​ ​m​k​d​i​r​ ​l​i​n​k​t​e​s​t​
$​ ​c​d​ ​l​i​n​k​t​e​s​t​
$​ ​t​o​u​c​h​ ​s​p​o​o​n​
(​c​r​e​a​t​e​s​ ​a​ ​n​e​w​ ​f​i​l​e​)​

$​ ​l​n​ ​s​p​o​o​n​ ​s​p​o​o​n​2​
(​m​a​k​e​s​ ​a​ ​h​a​r​d​ ​l​i​n​k​)​

$​ ​l​s​ ​-​l​i​
t​o​t​a​l​ ​0​
5​7​1​6​6​8​ ​-​r​w​-​r​-​-​r​-​-​ ​ ​2​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​2​ ​1​1​:​5​2​ ​s​p​o​o​n​
5​7​1​6​6​8​ ​-​r​w​-​r​-​-​r​-​-​ ​ ​2​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​2​ ​1​1​:​5​2​ ​s​p​o​o​n​2​

The left-hand column shows the inode of the file. As expected, the inodes are the same. Also notice the third column. It says that there are two hard links to this file.

$​ ​l​n​ ​s​p​o​o​n​ ​s​p​o​o​n​3​
$​ ​l​s​ ​-​l​i​
t​o​t​a​l​ ​0​
5​7​1​6​6​8​ ​-​r​w​-​r​-​-​r​-​-​ ​ ​3​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​2​ ​1​1​:​5​2​ ​s​p​o​o​n​
5​7​1​6​6​8​ ​-​r​w​-​r​-​-​r​-​-​ ​ ​3​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​2​ ​1​1​:​5​2​ ​s​p​o​o​n​2​
5​7​1​6​6​8​ ​-​r​w​-​r​-​-​r​-​-​ ​ ​3​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​2​ ​1​1​:​5​2​ ​s​p​o​o​n​3​

Now there are three hard links to the same file.

Hard links cannot cross file system boundaries because they refer to inodes. If you mv a hard linked file to another file system, the data will get copied, the original removed, and the link count will go down by one.

Users cannot create hard links to directories. On some file systems, the superuser can make hard links to directories, but in general that is not advisable because hard links can form cycles in the directories, which most software is not designed to handle. Apple’s Time Machine does use hard links on its backup volumes, including hard links to directories. An unmodified file that exists in multiple incremental backups will actually exit on disk just once, but will be referred to in multiple places via hard links.

Symbolic links

A symbolic link is a file that contains a path to another file. This path can be relative or absolute. Symbolic links are not included in the reference count of a file and, in fact, do not have to actually point to a real file at all, in which case they become dangling links. Use ln -s to create a symbolic link:

$​ ​m​k​d​i​r​ ​s​y​m​l​i​n​k​t​e​s​t​
$​ ​c​d​ ​s​y​m​l​i​n​k​t​e​s​t​
$​ ​t​o​u​c​h​ ​s​p​o​o​n​
(​m​a​k​e​s​ ​a​n​ ​e​m​p​t​y​ ​f​i​l​e​)​

$​ ​l​n​ ​-​s​ ​s​p​o​o​n​ ​s​p​o​o​n​2​
(​m​a​k​e​s​ ​a​ ​r​e​l​a​t​i​v​e​ ​s​y​m​b​o​l​i​c​ ​l​i​n​k​)​

$​ ​l​s​ ​-​l​
t​o​t​a​l​ ​8​
-​r​w​-​r​-​-​r​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​0​ ​A​u​g​ ​1​2​ ​1​2​:​0​8​ ​s​p​o​o​n​
l​r​w​x​r​-​x​r​-​x​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​5​ ​A​u​g​ ​1​2​ ​1​2​:​0​8​ ​s​p​o​o​n​2​@​ ​-​>​ ​s​p​o​o​n​

$​ ​l​n​ ​-​s​ ​`​p​w​d​`​/​s​p​o​o​n​ ​s​p​o​o​n​3​
(​u​s​e​ ​t​h​e​ ​b​a​c​k​t​i​c​k​ ​s​h​e​l​l​ ​o​p​e​r​a​t​o​r​ ​t​o​ ​r​u​n​ ​p​w​d​,​ ​w​h​i​c​h​ ​t​h​e​n​ ​i​s​ ​p​a​s​t​e​d​ ​i​n​t​o​ ​t​h​e​ ​c​o​m​m​a​n​d​
c​o​m​m​a​n​d​,​ ​w​h​i​c​h​ ​w​i​l​l​ ​g​i​v​e​ ​y​o​u​ ​a​ ​f​u​l​l​ ​p​a​t​h​)​

$​ ​l​s​ ​-​l​
t​o​t​a​l​ ​1​6​
-​r​w​-​r​w​-​r​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​ ​0​ ​A​u​g​ ​1​2​ ​1​2​:​0​8​ ​s​p​o​o​n​
l​r​w​x​r​w​x​r​-​x​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​ ​5​ ​A​u​g​ ​1​2​ ​1​2​:​0​8​ ​s​p​o​o​n​2​ ​-​>​ ​s​p​o​o​n​
l​r​w​x​r​w​x​r​-​x​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​3​5​ ​A​u​g​ ​1​2​ ​1​2​:​0​8​ ​s​p​o​o​n​3​ ​-​>​ ​/​U​s​e​r​s​/​m​a​r​k​d​/​b​a​d​g​e​r​s​/​\​
s​y​m​l​i​n​k​t​e​s​t​/​s​p​o​o​n​

Take a look at the size column. It is the number of characters in the symbolic link. The leading character over in the permissions is a lower case L, signifying that this is a link.

ls -F shows the extra symbols attached to file names:

$​ ​l​s​ ​-​F​
s​p​o​o​n​ ​ ​ ​s​p​o​o​n​2​@​ ​s​p​o​o​n​3​@​

The at-sign says it is a symbolic link. Additional character suffixes that -F uses are “/” for directories and “*” for executable files. You can alias ls to be ls -F so that you can see these clues in your directory listings.

Symbolic links are like pointers to files, as shown in Figure 12.5. They were created to work around some of the problems with hard links, such as crossing file systems and making links to directories. You can make a symbolic link to a directory and not cause problems. You can generate loops with symbolic links, but, luckily, function calls that try to resolve them will generate an error.

Figure 12.5  Hard and symbolic links

The real power of symbolic links comes when you move things around in the filesystem. Say your webserver log files in /home/nsadmin/logs/borkware are filling up your drive. You can copy all the log files to another place, like /Volumes/BigDisk/logs/borkware, and then point the first path to the second. No need to change any configuration files, and existing paths to the logs still work. Granted, things can get out of hand and can become unmaintainable if you have too many layers of symbolic link indirection.

Most of the file API you have seen so far will follow symbolic links automatically, like chmod(), chown(), open(), and also stat(), which is discussed a bit later.

There are some function calls that let you manipulate the link directly.

int lchown (const char *path, uid_t owner, gid_t group);

This changes the owner.

int readlink (const char *path, char *buf, int bufferLength);

This places the contents of the symbolic link path into the buffer. readlink() does not append the terminating zero byte. Instead, it returns the number of characters it wrote into the buffer or -1 on error with errno set appropriately.

lstat(), described later, will give you metadata for the symbolic link itself rather than the file it points to. rename() and unlink() also deal with the link directly.

The transparent resolution of links can lead to confusion when dangling links are involved:

$​ ​l​n​ ​-​s​ ​/​n​o​/​s​u​c​h​/​f​i​l​e​ ​o​o​p​a​c​k​
$​ ​l​s​ ​o​o​p​a​c​k​
o​o​p​a​c​k​
(​I​t​ ​i​s​ ​t​h​e​r​e​)​
$​ ​c​a​t​ ​o​o​p​a​c​k​
c​a​t​:​ ​o​o​p​a​c​k​:​ ​N​o​ ​s​u​c​h​ ​f​i​l​e​ ​o​r​ ​d​i​r​e​c​t​o​r​y​
(​A​c​t​u​a​l​l​y​,​ ​i​t​ ​i​s​ ​n​o​t​ ​t​h​e​r​e​.​)​

$​ ​l​s​ ​-​l​ ​o​o​p​a​c​k​
l​r​w​x​r​-​x​r​-​x​ ​ ​1​ ​m​a​r​k​d​ ​ ​s​t​a​f​f​ ​ ​1​3​ ​A​u​g​ ​1​2​ ​1​2​:​3​0​ ​o​o​p​a​c​k​@​ ​-​>​ ​/​n​o​/​s​u​c​h​/​f​i​l​e​
(​o​h​,​ ​i​t​ ​i​s​ ​a​ ​s​y​m​b​o​l​i​c​ ​l​i​n​k​ ​t​h​a​t​ ​p​o​i​n​t​s​ ​t​o​ ​n​o​w​h​e​r​e​)​

ls -F tells you what’s happening:

$​ ​l​s​ ​-​F​ ​o​o​p​a​c​k​
o​o​p​a​c​k​@​

oopack is now obviously a symbolic link.

Differences between the kinds of links:

Hard links always point to valid files.

Symbolic links do not track file moves, but aliases do.

Symbolic links survive file deletions, aliases do not always.

Symbolic links can make loops.

Mac OS aliases

Users can create aliases in Finder by explicitly making an alias or performing certain mouse dragging operations. Like symbolic links, aliases are not included in a file’s reference count, and they can dangle, pointing nowhere. On HFS+, an alias contains the file’s file number (similar to an inode number), so the alias can find the file anywhere on the disk. The path to the file is also stored in case the file is deleted and replaced with a new one.

API for links

int link (const char *name1, const char *name2);

int symlink (const char *name1, const char *name2);

link() creates a hard link, and symlink() creates a symbolic link with name2 referring to the same file as name1. Zero is returned on success, and -1 is returned on error with errno set appropriately. Use readlink() to get the file name stored in the link.

You will need to use the Carbon API if you want to deal with aliases.

File Metadata

File metadata is data about the file, but it is not the file’s data itself. This includes information like the file’s size, type, the last access times, and so on.

stat()

Three functions can be used to access metadata that lives in the inode:

int stat (const char *path, struct stat *sb);

int lstat (const char *path, struct stat *sb);

int fstat (int fd, struct stat *sb);

stat() gets the status, the information about the file at a path, following any intervening symbolic links. lstat() gets information about the symbolic link itself. fstat() gives you metadata about an already open file. As with most Unix file functions, a return value of zero indicates success, a value of -1 indicates an error, and errno is set appropriately.

You do not need read, write, or execute permissions on a file to stat() it, but all directories listed in the path name need to be searchable.

struct stat is the focal point for all of the data returned by these calls. You allocate a struct stat either on the stack or dynamically and pass that to the function.

s​t​r​u​c​t​ ​s​t​a​t​ ​{​
 ​ ​ ​ ​d​e​v​_​t​ ​ ​ ​ ​s​t​_​d​e​v​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​d​e​v​i​c​e​ ​i​n​o​d​e​ ​r​e​s​i​d​e​s​ ​o​n​ ​*​/​
 ​ ​ ​ ​i​n​o​_​t​ ​ ​ ​ ​s​t​_​i​n​o​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​i​n​o​d​e​'​s​ ​n​u​m​b​e​r​ ​*​/​
 ​ ​ ​ ​m​o​d​e​_​t​ ​ ​ ​s​t​_​m​o​d​e​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​i​n​o​d​e​ ​p​r​o​t​e​c​t​i​o​n​ ​m​o​d​e​ ​*​/​
 ​ ​ ​ ​n​l​i​n​k​_​t​ ​ ​s​t​_​n​l​i​n​k​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​n​u​m​b​e​r​ ​o​r​ ​h​a​r​d​ ​l​i​n​k​s​ ​t​o​ ​t​h​e​ ​f​i​l​e​ ​*​/​
 ​ ​ ​ ​u​i​d​_​t​ ​ ​ ​ ​s​t​_​u​i​d​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​u​s​e​r​-​i​d​ ​o​f​ ​o​w​n​e​r​ ​*​/​
 ​ ​ ​ ​g​i​d​_​t​ ​ ​ ​ ​s​t​_​g​i​d​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​g​r​o​u​p​-​i​d​ ​o​f​ ​o​w​n​e​r​ ​*​/​
 ​ ​ ​ ​d​e​v​_​t​ ​ ​ ​ ​s​t​_​r​d​e​v​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​d​e​v​i​c​e​ ​t​y​p​e​,​ ​f​o​r​ ​s​p​e​c​i​a​l​ ​f​i​l​e​ ​i​n​o​d​e​ ​*​/​
 ​ ​ ​ ​s​t​r​u​c​t​ ​t​i​m​e​s​p​e​c​ ​s​t​_​a​t​i​m​e​s​p​e​c​;​ ​ ​/​*​ ​t​i​m​e​ ​o​f​ ​l​a​s​t​ ​a​c​c​e​s​s​ ​*​/​
 ​ ​ ​ ​s​t​r​u​c​t​ ​t​i​m​e​s​p​e​c​ ​s​t​_​m​t​i​m​e​s​p​e​c​;​ ​ ​/​*​ ​t​i​m​e​ ​o​f​ ​l​a​s​t​ ​d​a​t​a​ ​m​o​d​i​f​i​c​a​t​i​o​n​ ​*​/​
 ​ ​ ​ ​s​t​r​u​c​t​ ​t​i​m​e​s​p​e​c​ ​s​t​_​c​t​i​m​e​s​p​e​c​;​ ​ ​/​*​ ​t​i​m​e​ ​o​f​ ​l​a​s​t​ ​f​i​l​e​ ​s​t​a​t​u​s​ ​c​h​a​n​g​e​ ​*​/​
 ​ ​ ​ ​o​f​f​_​t​ ​ ​ ​ ​s​t​_​s​i​z​e​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​f​i​l​e​ ​s​i​z​e​,​ ​i​n​ ​b​y​t​e​s​ ​*​/​
 ​ ​ ​ ​q​u​a​d​_​t​ ​ ​ ​s​t​_​b​l​o​c​k​s​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​b​l​o​c​k​s​ ​a​l​l​o​c​a​t​e​d​ ​f​o​r​ ​f​i​l​e​ ​*​/​
 ​ ​ ​ ​u​_​l​o​n​g​ ​ ​ ​s​t​_​b​l​k​s​i​z​e​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​o​p​t​i​m​a​l​ ​f​i​l​e​ ​s​y​s​ ​I​/​O​ ​o​p​s​ ​b​l​o​c​k​s​i​z​e​ ​*​/​
 ​ ​ ​ ​u​_​l​o​n​g​ ​ ​ ​s​t​_​f​l​a​g​s​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​u​s​e​r​ ​d​e​f​i​n​e​d​ ​f​l​a​g​s​ ​f​o​r​ ​f​i​l​e​ ​*​/​
 ​ ​ ​ ​u​_​l​o​n​g​ ​ ​ ​s​t​_​g​e​n​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​f​i​l​e​ ​g​e​n​e​r​a​t​i​o​n​ ​n​u​m​b​e​r​ ​*​/​
}​;​

Here is a breakdown of the more common fields:

st_mode contains the file type and permissions. The lower nine bits have the user, group, and other permissions. You can focus in on those by using the bit mask ACCESSPERMS (0777). You can also bitwise-AND using the macros listed with chmod(), such as S_IRWXU and S_IRUSR.

There are also bits used to describe the type of file being examined, as shown in Table 12.1.

Table 12.1. File Types

Mask | Convenience Macro | What it is
S_IFIFO | S_ISFIFO() | FIFO, a named pipe, an IPC mechanism
S_IFCHR | S_ISCHR() | character special device like terminals and serial lines
S_IFDIR | S_ISDIR() | directory
S_IFBLK | S_ISBLK() | block special device like disks and tapes
S_IFREG | S_ISREG() | plain old regular file
S_IFLNK | S_ISLNK() | symbolic link
S_IFSOCK | S_ISSOCK() | socket, network communication

The st_uid and st_gid fields tell you the user id of the user that owns the file, as well as the group associated with the file.

st_size has the file size in bytes.

There are three file times associated with each file, access, modification, and metadata change. These file times are of type struct timespec:

s​t​r​u​c​t​ ​t​i​m​e​s​p​e​c​ ​{​
 ​ ​ ​ ​t​i​m​e​_​t​ ​ ​t​v​_​s​e​c​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​s​e​c​o​n​d​s​
 ​ ​ ​ ​l​o​n​g​ ​ ​ ​ ​t​v​_​n​s​e​c​;​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​n​a​n​o​s​e​c​o​n​d​s​
}​;​

st_atimespec | The last access time. This is the last time the file was opened for reading. You can see this with ls -lu. You can also access just the tv_sec field by using st_atime.
st_mtimespec | The last modification time. This is the date displayed by ls -l. You can also access just the tv_sec field by using st_mtime.
st_ctimespec | Last change in inode status, like by using chmod or chown. ls -lc shows this time. You can also access just the tv_sec field by using st_ctime.

The shorter-named versions (st_mtime vs st_mtimespec) are of type time_t, which is the number of seconds since the start of the Unix epoch: midnight, Jan 1, 1970. They are conveniences for accessing the tv_sec field of the timespecs. Depending on various circumstances, these could be members of struct stat or #defines that dig into the timespec fields.

The utimes() system call can be used to change access and modification times of a file. This is handy if you are writing something like an archive utility and need to preserve access times when you expand an archive.

Example 12.2 is a little program that will print out a whole bunch of stuff from struct stat:

Example 12.2. permtype.m

/​/​ ​p​e​r​m​t​y​p​e​.​m​ ​-​-​ ​U​s​e​ ​s​t​a​t​ ​t​o​ ​d​i​s​c​o​v​e​r​ ​t​h​e​ ​t​y​p​e​ ​a​n​d​ ​p​e​r​m​i​s​s​i​o​n​s​ ​f​o​r​ ​a​ ​f​i​l​e​.​

/​/​ ​g​c​c​ ​-​g​ ​-​s​t​d​=​c​9​9​ ​-​W​a​l​l​ ​-​W​e​x​t​r​a​ ​-​o​ ​p​e​r​m​t​y​p​e​ ​p​e​r​m​t​y​p​e​.​m​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​g​r​p​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​g​r​o​u​p​ ​f​i​l​e​ ​a​c​c​e​s​s​ ​r​o​u​t​i​n​e​s​
#​i​m​p​o​r​t​ ​<​p​w​d​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​a​s​s​w​d​ ​f​i​l​e​ ​a​c​c​e​s​s​ ​r​o​u​t​i​n​e​s​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​t​a​t​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​a​t​(​)​ ​a​n​d​ ​s​t​r​u​c​t​ ​s​t​a​t​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​i​m​e​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​u​c​t​ ​t​m​,​ ​l​o​c​a​l​t​i​m​e​,​ ​e​t​c​

/​/​ ​L​o​o​k​u​p​ ​t​a​b​l​e​ ​f​o​r​ ​m​a​p​p​i​n​g​ ​p​e​r​m​i​s​s​i​o​n​ ​v​a​l​u​e​s​ ​t​o​ ​t​h​e​ ​f​a​m​i​l​i​a​r​
/​/​ ​c​h​a​r​a​c​t​e​r​ ​s​t​r​i​n​g​s​.​
s​t​a​t​i​c​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​g​_​p​e​r​m​s​[​]​ ​ ​=​ ​{​
 ​ ​ ​ ​"​-​-​-​"​,​ ​"​-​-​x​"​,​ ​"​-​w​-​"​,​ ​"​-​w​x​"​,​ ​"​r​-​-​"​,​ ​"​r​-​x​"​,​ ​"​r​w​-​"​,​ ​"​r​w​x​"​
}​;​

/​/​ ​M​a​p​p​i​n​g​ ​f​r​o​m​ ​t​h​e​ ​f​i​l​e​ ​t​y​p​e​ ​b​i​t​ ​m​a​s​k​ ​t​o​ ​a​ ​h​u​m​a​n​-​r​e​a​d​a​b​l​e​ ​s​t​r​i​n​g​.​
t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​F​i​l​e​T​y​p​e​ ​{​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​ ​ ​m​a​s​k​;​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​ ​ ​ ​ ​*​t​y​p​e​;​
}​ ​F​i​l​e​T​y​p​e​;​

s​t​a​t​i​c​ ​F​i​l​e​T​y​p​e​ ​g​_​f​i​l​e​t​y​p​e​s​[​]​ ​=​ ​{​
 ​ ​ ​ ​{​ ​S​_​I​F​R​E​G​,​ ​"​R​e​g​u​l​a​r​ ​F​i​l​e​"​ ​}​,​
 ​ ​ ​ ​{​ ​S​_​I​F​D​I​R​,​ ​"​D​i​r​e​c​t​o​r​y​"​ ​}​,​
 ​ ​ ​ ​{​ ​S​_​I​F​L​N​K​,​ ​"​S​y​m​b​o​l​i​c​ ​L​i​n​k​"​ ​}​,​
 ​ ​ ​ ​{​ ​S​_​I​F​C​H​R​,​ ​"​C​h​a​r​a​c​t​e​r​ ​S​p​e​c​i​a​l​ ​D​e​v​i​c​e​"​ ​}​,​
 ​ ​ ​ ​{​ ​S​_​I​F​B​L​K​,​ ​"​B​l​o​c​k​ ​S​p​e​c​i​a​l​ ​D​e​v​i​c​e​"​ ​}​,​
 ​ ​ ​ ​{​ ​S​_​I​F​I​F​O​,​ ​"​F​I​F​O​"​ ​}​,​
 ​ ​ ​ ​{​ ​S​_​I​F​S​O​C​K​,​ ​"​S​o​c​k​e​t​"​ ​}​,​
}​;​

v​o​i​d​ ​d​i​s​p​l​a​y​I​n​f​o​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​f​i​l​e​n​a​m​e​)​ ​{​
 ​ ​ ​ ​s​t​r​u​c​t​ ​s​t​a​t​ ​s​t​a​t​b​u​f​;​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​l​s​t​a​t​ ​(​f​i​l​e​n​a​m​e​,​ ​&​s​t​a​t​b​u​f​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​ ​w​i​t​h​ ​s​t​a​t​(​%​s​)​ ​:​ ​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​i​l​e​n​a​m​e​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​s​:​\​n​"​,​ ​f​i​l​e​n​a​m​e​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​p​e​r​m​i​s​s​i​o​n​s​:​ ​%​s​%​s​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​_​p​e​r​m​s​[​(​s​t​a​t​b​u​f​.​s​t​_​m​o​d​e​ ​&​ ​S​_​I​R​W​X​U​)​ ​>​>​ ​6​]​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​_​p​e​r​m​s​[​(​s​t​a​t​b​u​f​.​s​t​_​m​o​d​e​ ​&​ ​S​_​I​R​W​X​G​)​ ​>​>​ ​3​]​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​_​p​e​r​m​s​[​(​s​t​a​t​b​u​f​.​s​t​_​m​o​d​e​ ​&​ ​S​_​I​R​W​X​O​)​]​)​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​r​e​a​d​a​b​l​e​ ​s​t​r​i​n​g​ ​f​o​r​ ​t​h​e​ ​t​y​p​e​.​
 ​ ​ ​ ​F​i​l​e​T​y​p​e​ ​*​s​c​a​n​ ​=​ ​g​_​f​i​l​e​t​y​p​e​s​;​
 ​ ​ ​ ​F​i​l​e​T​y​p​e​ ​*​s​t​o​p​ ​=​ ​s​c​a​n​ ​+​ ​(​s​i​z​e​o​f​(​g​_​f​i​l​e​t​y​p​e​s​)​ ​/​ ​s​i​z​e​o​f​(​*​g​_​f​i​l​e​t​y​p​e​s​)​)​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​s​c​a​n​ ​<​ ​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​(​s​t​a​t​b​u​f​.​s​t​_​m​o​d​e​ ​&​ ​S​_​I​F​M​T​)​ ​=​=​ ​s​c​a​n​-​>​m​a​s​k​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​t​y​p​e​:​ ​%​s​\​n​"​,​ ​s​c​a​n​-​>​t​y​p​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​+​+​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​A​n​y​ ​s​p​e​c​i​a​l​ ​b​i​t​s​ ​s​e​t​s​?​
 ​ ​ ​ ​i​f​ ​(​(​s​t​a​t​b​u​f​.​s​t​_​m​o​d​e​ ​&​ ​S​_​I​S​U​I​D​)​ ​=​=​ ​S​_​I​S​U​I​D​)​ ​p​r​i​n​t​f​ ​(​"​ ​ ​s​e​t​-​u​i​d​!​\​n​"​)​;​
 ​ ​ ​ ​i​f​ ​(​(​s​t​a​t​b​u​f​.​s​t​_​m​o​d​e​ ​&​ ​S​_​I​S​G​I​D​)​ ​=​=​ ​S​_​I​S​G​I​D​)​ ​p​r​i​n​t​f​ ​(​"​ ​ ​s​e​t​-​g​r​o​u​p​-​i​d​!​\​n​"​)​;​

 ​ ​ ​ ​/​/​ ​F​i​l​e​ ​s​i​z​e​.​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​f​i​l​e​ ​i​s​ ​%​l​d​ ​b​y​t​e​s​ ​(​%​.​2​f​ ​K​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​l​o​n​g​)​s​t​a​t​b​u​f​.​s​t​_​s​i​z​e​,​ ​(​f​l​o​a​t​)​ ​(​s​t​a​t​b​u​f​.​s​t​_​s​i​z​e​ ​/​ ​1​0​2​4​.​0​)​)​;​

 ​ ​ ​ ​/​/​ ​O​w​n​i​n​g​ ​u​s​e​r​ ​/​ ​g​r​o​u​p​
 ​ ​ ​ ​s​t​r​u​c​t​ ​p​a​s​s​w​d​ ​*​p​a​s​s​w​d​ ​=​ ​g​e​t​p​w​u​i​d​ ​(​s​t​a​t​b​u​f​.​s​t​_​u​i​d​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​u​s​e​r​:​ ​%​s​ ​(​%​d​)​\​n​"​,​ ​p​a​s​s​w​d​-​>​p​w​_​n​a​m​e​,​ ​s​t​a​t​b​u​f​.​s​t​_​u​i​d​)​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​g​r​o​u​p​ ​*​g​r​o​u​p​ ​=​ ​g​e​t​g​r​g​i​d​ ​(​s​t​a​t​b​u​f​.​s​t​_​g​i​d​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​g​r​o​u​p​:​ ​%​s​ ​(​%​d​)​\​n​"​,​ ​g​r​o​u​p​-​>​g​r​_​n​a​m​e​,​ ​s​t​a​t​b​u​f​.​s​t​_​g​i​d​)​;​

 ​ ​ ​ ​/​/​ ​N​o​w​ ​t​h​e​ ​d​a​t​e​s​
 ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​1​0​2​4​]​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​t​m​ ​*​t​m​;​

 ​ ​ ​ ​t​m​ ​=​ ​l​o​c​a​l​t​i​m​e​ ​(​&​s​t​a​t​b​u​f​.​s​t​_​a​t​i​m​e​)​;​
 ​ ​ ​ ​s​t​r​f​t​i​m​e​ ​(​b​u​f​f​e​r​,​ ​1​0​2​4​,​ ​"​%​c​"​,​ ​t​m​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​l​a​s​t​ ​a​c​c​e​s​s​:​ ​%​s​\​n​"​,​ ​b​u​f​f​e​r​)​;​

 ​ ​ ​ ​t​m​ ​=​ ​l​o​c​a​l​t​i​m​e​ ​(​&​s​t​a​t​b​u​f​.​s​t​_​m​t​i​m​e​)​;​
 ​ ​ ​ ​s​t​r​f​t​i​m​e​ ​(​b​u​f​f​e​r​,​ ​1​0​2​4​,​ ​"​%​c​"​,​ ​t​m​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​l​a​s​t​ ​m​o​d​i​f​i​c​a​t​i​o​n​:​ ​%​s​\​n​"​,​ ​b​u​f​f​e​r​)​;​

 ​ ​ ​ ​t​m​ ​=​ ​l​o​c​a​l​t​i​m​e​ ​(​&​s​t​a​t​b​u​f​.​s​t​_​c​t​i​m​e​)​;​
 ​ ​ ​ ​s​t​r​f​t​i​m​e​ ​(​b​u​f​f​e​r​,​ ​1​0​2​4​,​ ​"​%​c​"​,​ ​t​m​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​l​a​s​t​ ​i​n​o​d​e​ ​c​h​a​n​g​e​:​ ​%​s​\​n​"​,​ ​b​u​f​f​e​r​)​;​

 ​ ​ ​ ​/​/​ ​d​o​u​b​l​e​-​s​p​a​c​e​ ​o​u​t​p​u​t​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​n​"​)​;​

}​ ​/​/​ ​d​i​s​p​l​a​y​I​n​f​o​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​=​=​ ​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​s​a​g​e​:​ ​ ​%​s​ ​/​p​a​t​h​/​t​o​/​f​i​l​e​ ​.​.​.​ ​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​1​;​ ​i​ ​<​ ​a​r​g​c​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​d​i​s​p​l​a​y​I​n​f​o​ ​(​a​r​g​v​[​i​]​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

A sample run

$​ ​.​/​p​e​r​m​t​y​p​e​ ​.​/​p​e​r​m​t​y​p​e​ ​o​o​p​a​c​k​ ​/​ ​/​d​e​v​/​k​l​o​g​ ​/​u​s​r​/​b​i​n​/​p​a​s​s​w​d​ ​n​o​b​o​d​y​-​h​o​m​e​
(​w​h​e​r​e​ ​o​o​p​a​c​k​ ​i​s​ ​a​ ​s​y​m​b​o​l​i​c​ ​l​i​n​k​)​

!​.​
.​/​p​e​r​m​t​y​p​e​ ​.​/​p​e​r​m​t​y​p​e​ ​/​ ​/​d​e​v​/​k​l​o​g​ ​/​u​s​r​/​b​i​n​/​p​a​s​s​w​d​ ​n​o​b​o​d​y​-​h​o​m​e​
.​/​p​e​r​m​t​y​p​e​:​
 ​ ​p​e​r​m​i​s​s​i​o​n​s​:​ ​r​w​x​r​-​x​r​-​x​
 ​ ​t​y​p​e​:​ ​R​e​g​u​l​a​r​ ​F​i​l​e​
 ​ ​f​i​l​e​ ​i​s​ ​9​8​5​6​ ​b​y​t​e​s​ ​(​9​.​6​2​ ​K​)​
 ​ ​u​s​e​r​:​ ​m​a​r​k​d​ ​(​5​0​1​)​
 ​ ​g​r​o​u​p​:​ ​s​t​a​f​f​ ​(​2​0​)​
 ​ ​l​a​s​t​ ​a​c​c​e​s​s​:​ ​T​h​u​ ​M​a​y​ ​2​6​ ​1​9​:​1​4​:​2​3​ ​2​0​1​1​
 ​ ​l​a​s​t​ ​m​o​d​i​f​i​c​a​t​i​o​n​:​ ​T​h​u​ ​M​a​y​ ​2​6​ ​1​9​:​1​4​:​1​8​ ​2​0​1​1​
 ​ ​l​a​s​t​ ​i​n​o​d​e​ ​c​h​a​n​g​e​:​ ​T​h​u​ ​M​a​y​ ​2​6​ ​1​9​:​1​4​:​1​8​ ​2​0​1​1​

/​:​
 ​ ​p​e​r​m​i​s​s​i​o​n​s​:​ ​r​w​x​r​-​x​r​-​x​
 ​ ​t​y​p​e​:​ ​D​i​r​e​c​t​o​r​y​
 ​ ​f​i​l​e​ ​i​s​ ​1​2​9​2​ ​b​y​t​e​s​ ​(​1​.​2​6​ ​K​)​
 ​ ​u​s​e​r​:​ ​r​o​o​t​ ​(​0​)​
 ​ ​g​r​o​u​p​:​ ​a​d​m​i​n​ ​(​8​0​)​
 ​ ​l​a​s​t​ ​a​c​c​e​s​s​:​ ​T​h​u​ ​M​a​y​ ​2​6​ ​1​9​:​1​4​:​1​8​ ​2​0​1​1​
 ​ ​l​a​s​t​ ​m​o​d​i​f​i​c​a​t​i​o​n​:​ ​W​e​d​ ​M​a​y​ ​2​5​ ​1​7​:​0​3​:​2​1​ ​2​0​1​1​
 ​ ​l​a​s​t​ ​i​n​o​d​e​ ​c​h​a​n​g​e​:​ ​W​e​d​ ​M​a​y​ ​2​5​ ​1​7​:​1​0​:​5​0​ ​2​0​1​1​

/​d​e​v​/​k​l​o​g​:​
 ​ ​p​e​r​m​i​s​s​i​o​n​s​:​ ​r​w​-​-​-​-​-​-​-​
 ​ ​t​y​p​e​:​ ​C​h​a​r​a​c​t​e​r​ ​S​p​e​c​i​a​l​ ​D​e​v​i​c​e​
 ​ ​f​i​l​e​ ​i​s​ ​0​ ​b​y​t​e​s​ ​(​0​.​0​0​ ​K​)​
 ​ ​u​s​e​r​:​ ​r​o​o​t​ ​(​0​)​
 ​ ​g​r​o​u​p​:​ ​w​h​e​e​l​ ​(​0​)​
 ​ ​l​a​s​t​ ​a​c​c​e​s​s​:​ ​T​h​u​ ​M​a​y​ ​2​6​ ​1​9​:​1​2​:​0​2​ ​2​0​1​1​
 ​ ​l​a​s​t​ ​m​o​d​i​f​i​c​a​t​i​o​n​:​ ​S​u​n​ ​M​a​y​ ​ ​1​ ​2​1​:​4​8​:​1​4​ ​2​0​1​1​
 ​ ​l​a​s​t​ ​i​n​o​d​e​ ​c​h​a​n​g​e​:​ ​S​u​n​ ​M​a​y​ ​ ​1​ ​2​1​:​4​8​:​1​4​ ​2​0​1​1​

/​u​s​r​/​b​i​n​/​p​a​s​s​w​d​:​
 ​ ​p​e​r​m​i​s​s​i​o​n​s​:​ ​r​-​x​r​-​x​r​-​x​
 ​ ​t​y​p​e​:​ ​R​e​g​u​l​a​r​ ​F​i​l​e​
 ​ ​s​e​t​-​u​i​d​!​
 ​ ​f​i​l​e​ ​i​s​ ​1​1​1​9​6​8​ ​b​y​t​e​s​ ​(​1​0​9​.​3​4​ ​K​)​
 ​ ​u​s​e​r​:​ ​r​o​o​t​ ​(​0​)​
 ​ ​g​r​o​u​p​:​ ​w​h​e​e​l​ ​(​0​)​
 ​ ​l​a​s​t​ ​a​c​c​e​s​s​:​ ​S​u​n​ ​M​a​y​ ​ ​1​ ​2​1​:​3​5​:​3​6​ ​2​0​1​1​
 ​ ​l​a​s​t​ ​m​o​d​i​f​i​c​a​t​i​o​n​:​ ​W​e​d​ ​M​a​r​ ​ ​9​ ​0​0​:​4​9​:​2​7​ ​2​0​1​1​
 ​ ​l​a​s​t​ ​i​n​o​d​e​ ​c​h​a​n​g​e​:​ ​S​u​n​ ​M​a​y​ ​ ​1​ ​2​1​:​3​5​:​3​6​ ​2​0​1​1​

e​r​r​o​r​ ​w​i​t​h​ ​s​t​a​t​(​n​o​b​o​d​y​-​h​o​m​e​)​ ​:​ ​ ​2​ ​(​N​o​ ​s​u​c​h​ ​f​i​l​e​ ​o​r​ ​d​i​r​e​c​t​o​r​y​)​

Now, just to make your life a little more complicated, both Cocoa and Carbon have different kinds of metadata they bring to the table, including things like the HFS+ Creator and Type code for files, and whether the file extension should be hidden.

NSFileManager has a number of constants for use in the attribute dictionary returned by -[NSFileManager fileAttributesAtPath: traverseLink:]. There is some overlap with what you get from struct stat:

NSFileSize

NSFileModificationDate

NSFileOwnerAccountName

NSFileGroupOwnerAccountName

NSFileReferenceCount (# of hard links)

NSFileIdentifier

NSFilePosixPermissions (the rwx permissions)

NSFileExtensionHidden

NSFileHFSCreatorCode

HSFileHFSTypeCode

NSFileType (This is a string that says whether it is of type Directory, Regular, Symbolic Link, etc.)

In Carbon, you can use calls like FSGetCatalogInfo() to get an FSCatalogInfo structure that has information like AppleShare sharing flags, the creation date, the backup date, Finder information, the logical and physical size of the data and resource fork, as well as a text encoding hint.

getattrlist()

Darwin introduces function, getattrlist(), that returns file metadata. In addition to the metadata that stat() gives you, getattrlist() can give you a whole lot more. You can pick and choose the individual pieces of metadata that you want to look at. If you are only interested in seeing a file’s size and last access time, getattrlist() can be told to just return those two pieces of metadata and not worry about anything else.

getattrlist() has existed since Mac OS X 10.0, but it was first documented in Mac OS X 10.4. The manpage is full of caveats and a bit of humor, such as “For reasons that are not at all obvious” and “Generally not a useful value.” getattrlist() is not defined to work for all file system types. You will see the highlights here, and you can find the full description in the getattrlist() manpage.

int getattrlist (const char *path, struct attrlist *attrList,
                 void *attrBuf, size_t attrBufSize,
                 unsigned long options)

path is the file you want the metadata for, and the function will store it into the attrBuf of attrBufSize. The return value from getattrlist() is zero on success, -1 on error, with the actual error placed in errno.

One word of warning: if the attribute buffer is too small, getattrlist() will truncate the data it places in the buffer. There are some variable-length structures (which you’ll see later) that reference data in other places in the buffer, which could end up referencing memory off the end. You need to pre-flight these variable-length structures to make sure they don’t reference data that’s been cut.

options can be zero or FSOPT_NOFOLLOW to prevent symbolic link resolution if it occurs as the last component of path.

Describing the attributes

A struct attrlist is used to tell getattrlist() what metadata you are interested in:

s​t​r​u​c​t​ ​a​t​t​r​l​i​s​t​ ​{​
 ​ ​ ​ ​u​_​s​h​o​r​t​ ​ ​ ​ ​ ​ ​ ​b​i​t​m​a​p​c​o​u​n​t​;​ ​/​*​ ​n​u​m​b​e​r​ ​o​f​ ​b​i​t​ ​s​e​t​s​ ​h​e​r​e​ ​*​/​
 ​ ​ ​ ​u​_​i​n​t​1​6​_​t​ ​ ​ ​ ​ ​p​a​d​d​i​n​g​;​ ​ ​ ​ ​ ​/​*​ ​f​o​r​ ​4​-​b​y​t​e​ ​a​l​i​g​n​m​e​n​t​ ​*​/​
 ​ ​ ​ ​a​t​t​r​g​r​o​u​p​_​t​ ​ ​ ​c​o​m​m​o​n​a​t​t​r​;​ ​ ​/​*​ ​c​o​m​m​o​n​ ​a​t​t​r​i​b​u​t​e​s​ ​*​/​
 ​ ​ ​ ​a​t​t​r​g​r​o​u​p​_​t​ ​ ​ ​v​o​l​a​t​t​r​;​ ​ ​ ​ ​ ​/​*​ ​v​o​l​u​m​e​ ​a​t​t​r​i​b​u​t​e​s​ ​*​/​
 ​ ​ ​ ​a​t​t​r​g​r​o​u​p​_​t​ ​ ​ ​d​i​r​a​t​t​r​;​ ​ ​ ​ ​ ​/​*​ ​d​i​r​e​c​t​o​r​y​ ​a​t​t​r​i​b​u​t​e​s​ ​*​/​
 ​ ​ ​ ​a​t​t​r​g​r​o​u​p​_​t​ ​ ​ ​f​i​l​e​a​t​t​r​;​ ​ ​ ​ ​/​*​ ​f​i​l​e​ ​a​t​t​r​i​b​u​t​e​s​ ​*​/​
 ​ ​ ​ ​a​t​t​r​g​r​o​u​p​_​t​ ​ ​ ​f​o​r​k​a​t​t​r​;​ ​ ​ ​ ​/​*​ ​f​o​r​k​ ​a​t​t​r​i​b​u​t​e​s​ ​*​/​
}​;​

The structure starts out with bitmapcount, which is the number of attrgroup_ts that the structure contains. An attrgroup_t is just an unsigned 32-bit integer. The current version of struct attrlist has five entries. Use the constant ATTR_BIT_MAP_COUNT to set that value. After some padding to keep everything on 4-byte alignment, the five bitmaps follow. Set bits in these bitmaps using the bitwise-OR operator to indicate the attributes you are interested in. Be sure to zero out the entire structure before setting any specific attribute flags.

commonattr contains bitmap flags for attributes that relate to all types of file system objects. Here are some common attributes you may be interested in. The typenames mentioned in each attribute are what get placed into your attrBuf:

ATTR_CMN_NAME

The name of the file, represented by an attrreference_t structure. It is a zero-terminated UTF-8 C string. The attrreference_t structure will be described in the section below that talks about parsing the returned attributes.

ATTR_CMN_OBJTYPE

The type of the file. The values are taken from enum vtype that is defined in <sys/vnode.h>. Common values include VREG (regular file), VDIR (directory), and VLNK (symbolic link)

ATTR_CMN_MODTIME

The modification time of the file, represented as a struct timespec. This is equivalent to st_mtimespec in struct stat. You can access other file times by using ATTR_CMN_CRTIME (creation time), ATTR_CMN_CHGTIME (last attribute modification time), and ATTR_CMN_ACCTIME (time of last access).

ATTR_CMN_OWNERID

The owner of the file, represented as a uid_t. You can get the group name with ATTR_CMD_GRPID, which places a gid_t into the attributes buffer.

ATTR_ACCESSMASK

The access permissions of the file represented as a mode_t. This is equivalent to st_mode in struct stat

volattr contains bitmap flags for attributes that relate to mounted volumes. The path should refer to the volume’s mountpoint.

ATTR_VOL_INFO

If you want any volume attributes to be returned, you must include this value in the bitmask. This does not result in any attribute data actually being returned.

ATTR_VOL_SIZE

The total size of the volume, in bytes, represented by an off_t.

ATTR_VOL_SPACEFREE

The free space of the volume, in bytes, represented by an off_t.

ATTR_VOL_SPACEAVAIL

The space, in bytes, on the volume that is available to non-privileged users. Volumes reserve an amount of free space to prevent disk exhaustion errors. You should report this value, and not ATTR_VOL_SPACEFREE, to users.

ATTR_VOL_IOBLOCKSIZE

The optimal block size to use when reading or writing data. This is an unsigned long.

ATTR_VOL_FILECOUNT

The number of files currently on the volume. Also an unsigned long.

ATTR_VOL_DIRCOUNT

The number of directories currently on the volume. An unsigned long

ATTR_VOL_NAME

An attrreference_t structure containing the name of the volume. This is a UTF-8 zero-terminated C string. The data length will not be greater than NAME_MAX + 1.

ATTR_VOL_CAPABILITIES

A vol_capabilities_attr_t structure that describes the optional features supported by the volume. Consult the getattrlist() manpage for the vol_capabilities_attr_t and the different volume capabilities supported.

ATTR_VOL_ATTRIBUTES

A vol_attributes_attr_t structure that describes the attributes supported by the volume. Likewise, consult the manpage for details on using this.

dirattr contains bitmap flags for attributes that relate to directories. There are two other flags, but they’re documented as either “you should not always rely on this value being accurate” or “Due to a bug, this flag is never set on current systems.” So this is the only directory attribute you should use:

ATTR_DIR_ENTRYCOUNT | An unsigned long with the number of file system objects in the directory, not including things like . and .., termed “synthetic entries.”

fileattr contains bitmap flags for attributes that relate to regular files. These values are undefined if you get the metadata for something other than a regular file, such as a directory.

ATTR_FILE_TOTALSIZE

The total number of bytes of all forks of a file, represented by an off_t. This is the logical size of a file. If a text file had 40 characters in it, the logical size would be 40.

ATTR_FILE_ALLOCSIZE

The total number of bytes on disk used by all of the file’s forks. This is the physical size of a file. If the 40-character text file was living on a volume with a 4K block size, the physical size of the file would be 4K.

ATTR_FILE_IOBLOCKSIZE

The optimal block size to use when reading or writing this file’s data. This is an unsigned long

ATTR_FILE_DATALENGTH

The logical size of the data fork for the file. This is an off_t. You can use ATTR_FILE_DATAALLOCSIZE to get the physical size of the data fork.

ATTR_FILE_RSRCLENGTH

The logical size of the resource fork for the file. This is an off_t. You can use ATTR_FILE_RSRCALLOCSIZE to get the physical size of the resource fork.

The forkattr bitmap should be left empty. According to the manpage, “Fork attributes are not properly implemented by any current Mac OS X volume format implementation. We strongly recommend that client programs do not request fork attributes.”

Parsing the buffer

The data that is placed into your buffer by getattrlist() is placed in the order that the attribute bitmap flags are listed in the manpage (and the order they are listed here). The scalar types, such as mode_t, off_t, unsigned long, are stacked end-to-end, sometimes with padding to align values to four-byte boundaries. Typically, you will define a struct that lays out the data types you’re expecting back and then pass the address to one of these structs for getattrlist() to fill in.

The variable-length attributes, particularly names, are accessed indirectly using an attrreference_t. If you are using the struct-overlay idiom, you would have an attrreference_t in your struct. An attrreference_t looks like this:

t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​a​t​t​r​r​e​f​e​r​e​n​c​e​ ​{​
 ​ ​ ​ ​l​o​n​g​ ​ ​ ​ ​ ​ ​ ​ ​a​t​t​r​_​d​a​t​a​o​f​f​s​e​t​;​
 ​ ​ ​ ​s​i​z​e​_​t​ ​ ​ ​ ​ ​ ​a​t​t​r​_​l​e​n​g​t​h​;​
}​ ​a​t​t​r​r​e​f​e​r​e​n​c​e​_​t​;​

To get at the actual data, you would add attr_dataoffset to the address of the attrreference_t in the struct. Say that you are interested in the file name and the modification time. Your overlay struct might look like this:

t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​P​e​r​m​T​y​p​e​A​t​t​r​i​b​u​t​e​s​ ​{​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​ ​ ​ ​ ​ ​ ​l​e​n​g​t​h​;​
 ​ ​ ​ ​a​t​t​r​r​e​f​e​r​e​n​c​e​_​t​ ​ ​ ​ ​ ​n​a​m​e​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​t​i​m​e​s​p​e​c​ ​ ​ ​ ​ ​m​o​d​T​i​m​e​;​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​i​l​e​N​a​m​e​S​p​a​c​e​[​M​A​X​P​A​T​H​L​E​N​]​;​
}​ ​P​e​r​m​T​y​p​e​A​t​t​r​i​b​u​t​e​s​;​

And then call the function:

P​e​r​m​T​y​p​e​A​t​t​r​i​b​u​t​e​s​ ​p​e​r​m​A​t​t​r​i​b​u​t​e​s​;​
i​n​t​ ​r​e​s​u​l​t​ ​=​ ​g​e​t​a​t​t​r​l​i​s​t​ ​(​f​i​l​e​n​a​m​e​,​ ​&​a​t​t​r​L​i​s​t​,​ ​&​p​e​r​m​A​t​t​r​i​b​u​t​e​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​o​f​(​p​e​r​m​A​t​t​r​i​b​u​t​e​s​)​,​ ​0​)​;​

Then access the name like this:

c​h​a​r​ ​*​f​i​l​e​n​a​m​e​F​r​o​m​A​t​t​r​s​ ​=​
 ​ ​ ​ ​(​(​c​h​a​r​ ​*​)​&​p​e​r​m​A​t​t​r​i​b​u​t​e​s​-​>​n​a​m​e​)​ ​+​ ​p​e​r​m​A​t​t​r​i​b​u​t​e​s​-​>​n​a​m​e​.​a​t​t​r​_​d​a​t​a​o​f​f​s​e​t​;​

Example 12.3 is the permtype program, but it is implemented using getattrlist() rather than stat().

Example 12.3. permtype-getattrlist.m

/​/​ ​p​e​r​m​t​y​p​e​-​g​e​t​a​t​t​r​l​i​s​t​.​m​ ​-​-​ ​u​s​e​ ​g​e​t​a​t​t​r​l​i​s​t​(​)​ ​t​o​ ​d​i​s​c​o​v​e​r​ ​t​y​p​e​ ​a​n​d​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​m​i​s​i​s​o​n​s​ ​f​o​r​ ​a​ ​f​i​l​e​.​

/​/​ ​g​c​c​ ​-​g​ ​-​s​t​d​=​c​9​9​ ​-​o​ ​p​e​r​m​t​y​p​e​-​g​e​t​a​t​t​r​l​i​s​t​ ​p​e​r​m​t​y​p​e​-​g​e​t​a​t​t​r​l​i​s​t​.​m​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​g​r​p​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​g​r​o​u​p​ ​f​i​l​e​ ​a​c​c​e​s​s​ ​r​o​u​t​i​n​e​s​
#​i​m​p​o​r​t​ ​<​p​w​d​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​a​s​s​w​d​ ​f​i​l​e​ ​a​c​c​e​s​s​ ​r​o​u​t​i​n​e​s​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​m​e​m​s​e​t​(​)​
#​i​m​p​o​r​t​ ​<​s​y​s​/​a​t​t​r​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​a​t​t​r​i​b​u​t​e​ ​s​t​r​u​c​t​u​r​e​s​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​t​a​t​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​a​t​(​)​ ​a​n​d​ ​s​t​r​u​c​t​ ​s​t​a​t​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​g​e​t​a​t​t​r​l​i​s​t​(​)​

/​/​ ​L​o​o​k​u​p​ ​t​a​b​l​e​ ​f​o​r​ ​m​a​p​p​i​n​g​ ​p​e​r​m​i​s​s​i​o​n​ ​v​a​l​u​e​s​ ​t​o​ ​t​h​e​ ​f​a​m​i​l​i​a​r​
/​/​ ​c​h​a​r​a​c​t​e​r​ ​s​t​r​i​n​g​s​.​
s​t​a​t​i​c​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​g​_​p​e​r​m​s​[​]​ ​ ​=​ ​{​
 ​ ​ ​ ​"​-​-​-​"​,​ ​"​-​-​x​"​,​ ​"​-​w​-​"​,​ ​"​-​w​x​"​,​ ​"​r​-​-​"​,​ ​"​r​-​x​"​,​ ​"​r​w​-​"​,​ ​"​r​w​x​"​
}​;​

/​/​ ​M​a​p​p​i​n​g​ ​f​r​o​m​ ​t​h​e​ ​f​i​l​e​ ​t​y​p​e​ ​b​i​t​ ​m​a​s​k​ ​t​o​ ​a​ ​h​u​m​a​n​-​r​e​a​d​a​b​l​e​ ​s​t​r​i​n​g​.​
t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​F​i​l​e​T​y​p​e​ ​{​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​ ​ ​ ​ ​ ​ ​m​a​s​k​;​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​ ​ ​ ​ ​ ​ ​ ​ ​*​t​y​p​e​;​
}​ ​F​i​l​e​T​y​p​e​;​

s​t​a​t​i​c​ ​F​i​l​e​T​y​p​e​ ​g​_​t​y​p​e​s​[​]​ ​=​ ​{​
 ​ ​ ​ ​{​ ​S​_​I​F​R​E​G​,​ ​"​R​e​g​u​l​a​r​ ​F​i​l​e​"​ ​}​,​
 ​ ​ ​ ​{​ ​S​_​I​F​D​I​R​,​ ​"​D​i​r​e​c​t​o​r​y​"​ ​}​,​
 ​ ​ ​ ​{​ ​S​_​I​F​L​N​K​,​ ​"​S​y​m​b​o​l​i​c​ ​L​i​n​k​"​ ​}​,​
 ​ ​ ​ ​{​ ​S​_​I​F​C​H​R​,​ ​"​C​h​a​r​a​c​t​e​r​ ​S​p​e​c​i​a​l​ ​D​e​v​i​c​e​"​ ​}​,​
 ​ ​ ​ ​{​ ​S​_​I​F​B​L​K​,​ ​"​B​l​o​c​k​ ​S​p​e​c​i​a​l​ ​D​e​v​i​c​e​"​ ​}​,​
 ​ ​ ​ ​{​ ​S​_​I​F​I​F​O​,​ ​"​F​I​F​O​"​ ​}​,​
 ​ ​ ​ ​{​ ​S​_​I​F​S​O​C​K​,​ ​"​S​o​c​k​e​t​"​ ​}​,​
}​;​

/​/​ ​s​t​r​u​c​t​u​r​e​ ​o​f​ ​t​h​e​ ​d​a​t​a​ ​b​e​i​n​g​ ​r​e​t​u​r​n​e​d​ ​b​y​ ​g​e​t​a​t​t​r​l​i​s​t​(​)​

t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​P​e​r​m​T​y​p​e​A​t​t​r​i​b​u​t​e​s​ ​{​
 ​ ​ ​ ​u​_​i​n​t​3​2​_​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​l​e​n​g​t​h​;​
 ​ ​ ​ ​a​t​t​r​r​e​f​e​r​e​n​c​e​_​t​ ​ ​ ​ ​ ​n​a​m​e​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​t​i​m​e​s​p​e​c​ ​ ​ ​ ​ ​m​o​d​T​i​m​e​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​t​i​m​e​s​p​e​c​ ​ ​ ​ ​ ​c​h​a​n​g​e​T​i​m​e​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​t​i​m​e​s​p​e​c​ ​ ​ ​ ​ ​a​c​c​e​s​s​T​i​m​e​;​
 ​ ​ ​ ​u​i​d​_​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​w​n​e​r​I​d​;​
 ​ ​ ​ ​g​i​d​_​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​r​o​u​p​I​d​;​
#​i​f​ ​_​_​L​I​T​T​L​E​_​E​N​D​I​A​N​_​_​
 ​ ​ ​ ​m​o​d​e​_​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​c​c​e​s​s​M​a​s​k​;​
 ​ ​ ​ ​s​h​o​r​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​a​d​d​i​n​g​;​
#​e​l​s​e​
 ​ ​ ​ ​s​h​o​r​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​a​d​d​i​n​g​;​
 ​ ​ ​ ​m​o​d​e​_​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​c​c​e​s​s​M​a​s​k​;​
#​e​n​d​i​f​
 ​ ​ ​ ​o​f​f​_​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​i​l​e​L​o​g​i​c​a​l​S​i​z​e​;​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​i​l​e​N​a​m​e​S​p​a​c​e​[​M​A​X​P​A​T​H​L​E​N​]​;​
}​ ​_​_​a​t​t​r​i​b​u​t​e​_​_​ ​(​(​p​a​c​k​e​d​)​)​ ​P​e​r​m​T​y​p​e​A​t​t​r​i​b​u​t​e​s​;​

v​o​i​d​ ​d​i​s​p​l​a​y​I​n​f​o​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​f​i​l​e​n​a​m​e​)​ ​{​
 ​ ​ ​ ​/​/​ ​C​l​e​a​r​ ​o​u​t​ ​t​h​e​ ​a​t​t​r​i​b​u​t​e​ ​r​e​q​u​e​s​t​ ​s​t​r​u​c​t​u​r​e​ ​f​i​r​s​t​,​ ​o​t​h​e​r​w​i​s​e​ ​y​o​u​'​l​l​
 ​ ​ ​ ​/​/​ ​g​e​t​ ​u​n​d​e​f​i​n​e​d​ ​r​e​s​u​l​t​s​.​
 ​ ​ ​ ​s​t​r​u​c​t​ ​a​t​t​r​l​i​s​t​ ​a​t​t​r​L​i​s​t​ ​=​ ​{​ ​0​ ​}​;​
 ​ ​ ​ ​a​t​t​r​L​i​s​t​.​b​i​t​m​a​p​c​o​u​n​t​ ​=​ ​A​T​T​R​_​B​I​T​_​M​A​P​_​C​O​U​N​T​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​n​a​m​e​,​ ​t​h​e​ ​p​e​r​m​i​s​s​i​o​n​s​,​ ​a​n​d​ ​t​h​e​ ​s​t​a​t​-​s​t​y​l​e​ ​t​i​m​e​s​.​
 ​ ​ ​ ​a​t​t​r​L​i​s​t​.​c​o​m​m​o​n​a​t​t​r​ ​=​ ​A​T​T​R​_​C​M​N​_​N​A​M​E​ ​|​ ​A​T​T​R​_​C​M​N​_​M​O​D​T​I​M​E​ ​|​ ​A​T​T​R​_​C​M​N​_​C​H​G​T​I​M​E​
 ​ ​ ​ ​ ​ ​ ​ ​|​ ​A​T​T​R​_​C​M​N​_​A​C​C​T​I​M​E​ ​|​ ​A​T​T​R​_​C​M​N​_​O​W​N​E​R​I​D​ ​|​ ​A​T​T​R​_​C​M​N​_​G​R​P​I​D​ ​|​ ​A​T​T​R​_​C​M​N​_​A​C​C​E​S​S​M​A​S​K​;​

 ​ ​ ​ ​/​/​ ​A​l​s​o​ ​g​e​t​ ​t​h​e​ ​f​i​l​e​ ​s​i​z​e​.​
 ​ ​ ​ ​a​t​t​r​L​i​s​t​.​f​i​l​e​a​t​t​r​ ​=​ ​A​T​T​R​_​F​I​L​E​_​T​O​T​A​L​S​I​Z​E​;​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​r​e​t​u​r​n​e​d​ ​d​a​t​a​ ​c​a​n​n​o​t​ ​b​e​ ​l​a​r​g​e​r​ ​t​h​a​n​ ​t​h​e​ ​s​i​z​e​ ​o​f​ ​P​e​r​m​T​y​p​e​A​t​t​r​i​b​u​t​e​s​.​
 ​ ​ ​ ​P​e​r​m​T​y​p​e​A​t​t​r​i​b​u​t​e​s​ ​p​e​r​m​A​t​t​r​i​b​u​t​e​s​;​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​g​e​t​a​t​t​r​l​i​s​t​ ​(​f​i​l​e​n​a​m​e​,​ ​&​a​t​t​r​L​i​s​t​,​ ​&​p​e​r​m​A​t​t​r​i​b​u​t​e​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​o​f​(​p​e​r​m​A​t​t​r​i​b​u​t​e​s​)​,​ ​0​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​ ​w​i​t​h​ ​g​e​t​a​t​t​r​l​i​s​t​(​%​s​)​ ​:​ ​%​d​ ​/​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​i​l​e​n​a​m​e​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​B​e​ ​a​ ​l​i​t​t​l​e​ ​p​a​r​a​n​o​i​d​ ​a​b​o​u​t​ ​t​h​e​ ​v​a​r​i​a​b​l​e​-​s​i​z​e​d​ ​r​e​t​u​r​n​e​d​ ​d​a​t​a​.​
 ​ ​ ​ ​c​h​a​r​ ​*​n​a​m​e​S​t​a​r​t​ ​=​ ​(​(​c​h​a​r​ ​*​)​ ​&​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​n​a​m​e​)​
 ​ ​ ​ ​ ​ ​ ​ ​+​ ​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​n​a​m​e​.​a​t​t​r​_​d​a​t​a​o​f​f​s​e​t​;​
 ​ ​ ​ ​c​h​a​r​ ​*​n​a​m​e​E​n​d​ ​=​ ​n​a​m​e​S​t​a​r​t​ ​+​ ​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​n​a​m​e​.​a​t​t​r​_​l​e​n​g​t​h​;​
 ​ ​ ​ ​c​h​a​r​ ​*​b​u​f​f​e​r​E​n​d​ ​=​ ​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​f​i​l​e​N​a​m​e​S​p​a​c​e​
 ​ ​ ​ ​ ​ ​ ​ ​+​ ​s​i​z​e​o​f​(​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​f​i​l​e​N​a​m​e​S​p​a​c​e​)​;​

 ​ ​ ​ ​/​/​ ​g​e​t​a​t​t​r​l​i​s​t​(​)​ ​w​o​n​'​t​ ​a​c​t​u​a​l​l​y​ ​c​l​o​b​b​e​r​ ​p​a​s​t​ ​t​h​e​ ​e​n​d​ ​o​f​ ​o​u​r​ ​s​t​r​u​c​t​u​r​e​,​
 ​ ​ ​ ​/​/​ ​b​u​t​ ​b​l​i​n​d​l​y​ ​f​o​l​l​o​w​i​n​g​ ​p​o​i​n​t​e​r​s​ ​c​a​n​ ​b​e​ ​p​a​i​n​f​u​l​.​
 ​ ​ ​ ​i​f​ ​(​n​a​m​e​E​n​d​ ​>​ ​b​u​f​f​e​r​E​n​d​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​R​e​t​u​r​n​e​d​ ​f​i​l​e​n​a​m​e​ ​w​a​s​ ​t​r​u​n​c​a​t​e​d​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​P​r​i​n​t​ ​t​h​e​ ​N​a​m​e​,​ ​t​h​e​n​ ​p​e​r​m​i​s​s​i​o​n​s​.​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​s​:​\​n​"​,​ ​n​a​m​e​S​t​a​r​t​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​p​e​r​m​i​s​s​i​o​n​s​:​ ​%​s​%​s​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​_​p​e​r​m​s​[​(​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​a​c​c​e​s​s​M​a​s​k​ ​&​ ​S​_​I​R​W​X​U​)​ ​>​>​ ​6​]​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​_​p​e​r​m​s​[​(​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​a​c​c​e​s​s​M​a​s​k​ ​&​ ​S​_​I​R​W​X​G​)​ ​>​>​ ​3​]​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​_​p​e​r​m​s​[​(​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​a​c​c​e​s​s​M​a​s​k​ ​&​ ​S​_​I​R​W​X​O​)​]​)​;​

 ​ ​ ​ ​/​/​ ​F​i​g​u​r​e​ ​o​u​t​ ​t​h​e​ ​t​y​p​e​.​
 ​ ​ ​ ​F​i​l​e​T​y​p​e​ ​*​s​c​a​n​ ​=​ ​g​_​t​y​p​e​s​;​
 ​ ​ ​ ​F​i​l​e​T​y​p​e​ ​*​s​t​o​p​ ​=​ ​s​c​a​n​ ​+​ ​(​s​i​z​e​o​f​(​g​_​t​y​p​e​s​)​ ​/​ ​s​i​z​e​o​f​(​*​g​_​t​y​p​e​s​)​)​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​s​c​a​n​ ​<​ ​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​(​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​a​c​c​e​s​s​M​a​s​k​ ​&​ ​S​_​I​F​M​T​)​ ​=​=​ ​s​c​a​n​-​>​m​a​s​k​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​t​y​p​e​:​ ​%​s​\​n​"​,​ ​s​c​a​n​-​>​t​y​p​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​+​+​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​A​n​y​ ​s​p​e​c​i​a​l​ ​b​i​t​s​ ​s​e​t​s​?​
 ​ ​ ​ ​i​f​ ​(​(​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​a​c​c​e​s​s​M​a​s​k​ ​&​ ​S​_​I​S​U​I​D​)​ ​=​=​ ​S​_​I​S​U​I​D​)​ ​p​r​i​n​t​f​ ​(​"​ ​ ​s​e​t​-​u​i​d​!​\​n​"​)​;​
 ​ ​ ​ ​i​f​ ​(​(​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​a​c​c​e​s​s​M​a​s​k​ ​&​ ​S​_​I​S​G​I​D​)​ ​=​=​ ​S​_​I​S​U​I​D​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​s​e​t​-​g​r​o​u​p​-​i​d​!​\​n​"​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​f​i​l​e​ ​s​i​z​e​ ​i​s​n​'​t​ ​a​p​p​l​i​c​a​b​l​e​ ​t​o​ ​d​i​r​e​c​t​o​r​i​e​s​.​
 ​ ​ ​ ​i​f​ ​(​(​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​a​c​c​e​s​s​M​a​s​k​ ​&​ ​S​_​I​F​M​T​)​ ​=​=​ ​S​_​I​F​R​E​G​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​f​i​l​e​ ​i​s​ ​%​l​l​d​ ​b​y​t​e​s​ ​(​%​l​l​d​ ​K​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​f​i​l​e​L​o​g​i​c​a​l​S​i​z​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​f​i​l​e​L​o​g​i​c​a​l​S​i​z​e​ ​/​ ​1​0​2​4​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​O​w​n​i​n​g​ ​u​s​e​r​ ​/​ ​g​r​o​u​p​.​
 ​ ​ ​ ​s​t​r​u​c​t​ ​p​a​s​s​w​d​ ​*​p​a​s​s​w​d​ ​=​ ​g​e​t​p​w​u​i​d​ ​(​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​o​w​n​e​r​I​d​)​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​g​r​o​u​p​ ​*​g​r​o​u​p​ ​=​ ​g​e​t​g​r​g​i​d​ ​(​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​g​r​o​u​p​I​d​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​u​s​e​r​:​ ​%​s​ ​(​%​d​)​\​n​"​,​ ​p​a​s​s​w​d​-​>​p​w​_​n​a​m​e​,​ ​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​o​w​n​e​r​I​d​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​g​r​o​u​p​:​ ​%​s​ ​(​%​d​)​\​n​"​,​ ​g​r​o​u​p​-​>​g​r​_​n​a​m​e​,​ ​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​g​r​o​u​p​I​d​)​;​

 ​ ​ ​ ​/​/​ ​N​o​w​ ​t​h​e​ ​d​a​t​e​s​.​
 ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​1​0​2​4​]​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​t​m​ ​*​t​m​;​

 ​ ​ ​ ​t​m​ ​=​ ​l​o​c​a​l​t​i​m​e​ ​(​&​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​a​c​c​e​s​s​T​i​m​e​.​t​v​_​s​e​c​)​;​
 ​ ​ ​ ​s​t​r​f​t​i​m​e​ ​(​b​u​f​f​e​r​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​,​ ​"​%​c​"​,​ ​t​m​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​l​a​s​t​ ​a​c​c​e​s​s​:​ ​%​s​\​n​"​,​ ​b​u​f​f​e​r​)​;​

 ​ ​ ​ ​t​m​ ​=​ ​l​o​c​a​l​t​i​m​e​ ​(​&​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​m​o​d​T​i​m​e​.​t​v​_​s​e​c​)​;​
 ​ ​ ​ ​s​t​r​f​t​i​m​e​ ​(​b​u​f​f​e​r​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​,​ ​"​%​c​"​,​ ​t​m​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​l​a​s​t​ ​m​o​d​i​f​i​c​a​t​i​o​n​:​ ​%​s​\​n​"​,​ ​b​u​f​f​e​r​)​;​

 ​ ​ ​ ​t​m​ ​=​ ​l​o​c​a​l​t​i​m​e​ ​(​&​p​e​r​m​A​t​t​r​i​b​u​t​e​s​.​c​h​a​n​g​e​T​i​m​e​.​t​v​_​s​e​c​)​;​
 ​ ​ ​ ​s​t​r​f​t​i​m​e​ ​(​b​u​f​f​e​r​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​,​ ​"​%​c​"​,​ ​t​m​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​l​a​s​t​ ​i​n​o​d​e​ ​c​h​a​n​g​e​:​ ​%​s​\​n​"​,​ ​b​u​f​f​e​r​)​;​

 ​ ​ ​ ​/​/​ ​d​o​u​b​l​e​-​s​p​a​c​e​ ​o​u​t​p​u​t​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​n​"​)​;​

}​ ​/​/​ ​d​i​s​p​l​a​y​I​n​f​o​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​=​=​ ​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​s​a​g​e​:​ ​ ​%​s​ ​/​p​a​t​h​/​t​o​/​f​i​l​e​ ​.​.​.​ ​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​1​;​ ​i​ ​<​ ​a​r​g​c​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​d​i​s​p​l​a​y​I​n​f​o​ ​(​a​r​g​v​[​i​]​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

The output from this program is identical to the output from permtype.

Metadata in batches

So why talk about getattrlist() when there are perfectly functional POSIX calls to do the same work? This function leads up to the somewhat unpronounceable getdirentriesattr() function that lets you get file attributes in bulk. You can get metadata for many files with a single call to getdirentriesattr(), which in some cases can make your program faster.

int getdirentriesattr (int fd, struct attrlist *attrList,
                       void *attrBuf, size_t attrBufSize,
                       uint32_t *count,
                       uint32_t *basePointer,
                       uint32_t *newState,
                       uint32_t options);

getdirentriesattr() reads directory entries, like with readdir() and getdirentries(), and returns their attributes, like with stat() and getattrlist(). fd is an open file descriptor for the directory you want to iterate through. Just open the directory using open(). attrList is the set of attributes you want for each file. This is constructed just like the attribute list for getattrlist. attrBuf is the chunk of memory where the attributes should be stored. Pass the length of this buffer in attrBufSize.

count is an in/out parameter. You pass in the number of directory entries you are interested in seeing, and the function will pass back through that parameter the number of entries it actually placed into the buffer. Unlike getattrlist(), this function will not silently truncate your data. The basePointer parameter is an out parameter, giving you the offset into the directory, in case you need to lseek() at a later time. Usually the value can be ignored, but you still have to pass a valid address here. newState is an out parameter too, which changes value if the directory has been modified since you started iterating through it. Finally, options should have zero passed. There is one option described in the manpage, with the tantalizingly vague statement “This option allowed for specific performance optimizations for specific clients on older systems.”

On a successful completion the function returns 0 and returns 1 if the call completed and it has returned the last entry. -1 is returned on errors, so check errno.

The data is arranged in the buffer like it is with getattrlist(). Each file’s run of attributes starts out with a 4-byte length value. This is the number of bytes of attributes for that file. Add this length to the attribute’s starting address to find the next batch. Variable length attributes are stored like getattrlist() as well, by using the attr_dataoffset element to find the location in the buffer of the filename data.

Example 12.4 calculates the amount of disk space consumed by a folder. It uses both stat() and getdirentriesattr().

Example 12.4. foldersize.m

/​/​ ​f​o​l​d​e​r​s​i​z​e​.​m​ ​-​-​ ​c​a​l​c​u​l​a​t​e​ ​t​h​e​ ​s​i​z​e​ ​o​f​ ​a​ ​f​o​l​d​e​r​ ​w​i​t​h​ ​s​t​a​t​ ​a​n​d​ ​g​e​t​d​i​r​e​n​t​r​i​e​s​a​t​t​r​

#​i​m​p​o​r​t​ ​<​d​i​r​e​n​t​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​g​e​t​d​i​r​e​n​t​r​i​e​s​(​)​
#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​f​c​n​t​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​O​_​R​D​O​N​L​Y​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​
#​i​m​p​o​r​t​ ​<​s​y​s​/​a​t​t​r​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​a​t​t​r​r​e​f​e​r​e​n​c​e​_​t​
#​i​m​p​o​r​t​ ​<​s​y​s​/​d​i​r​e​n​t​.​h​>​ ​ ​/​/​ ​f​o​r​ ​s​t​r​u​c​t​ ​d​i​r​e​n​t​
#​i​m​p​o​r​t​ ​<​s​y​s​/​p​a​r​a​m​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​M​A​X​P​A​T​H​L​E​N​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​t​a​t​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​u​c​t​ ​s​t​a​t​b​u​f​ ​a​n​d​ ​s​t​a​t​(​)​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​r​a​n​d​o​m​ ​t​y​p​e​ ​d​e​f​i​n​i​t​i​o​n​
#​i​m​p​o​r​t​ ​<​s​y​s​/​v​n​o​d​e​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​V​D​I​R​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​g​e​t​d​i​r​e​n​t​r​i​e​s​a​t​t​r​(​)​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​f​o​l​d​e​r​s​i​z​e​ ​f​o​l​d​e​r​s​i​z​e​.​m​

/​/​ ​s​h​o​w​ ​t​h​e​ ​f​i​l​e​s​ ​a​n​d​ ​s​i​z​e​s​ ​o​f​ ​t​h​e​ ​f​i​l​e​s​ ​a​s​ ​t​h​e​y​ ​a​r​e​ ​p​r​o​c​e​s​s​e​d​
s​t​a​t​i​c​ ​i​n​t​ ​g​_​v​e​r​b​o​s​e​ ​=​ ​0​;​

/​/​ ​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
/​/​ ​s​t​a​t​ ​c​o​d​e​

/​/​ ​C​a​l​c​u​l​a​t​e​ ​t​h​e​ ​d​i​r​e​c​t​o​r​y​ ​s​i​z​e​ ​v​i​a​ ​s​t​a​t​(​)​.​
o​f​f​_​t​ ​s​i​z​e​F​o​r​F​o​l​d​e​r​S​t​a​t​ ​(​c​h​a​r​ ​*​p​a​t​h​)​ ​{​
 ​ ​ ​ ​D​I​R​ ​*​d​i​r​e​c​t​o​r​y​ ​=​ ​o​p​e​n​d​i​r​ ​(​p​a​t​h​)​;​

 ​ ​ ​ ​i​f​ ​(​d​i​r​e​c​t​o​r​y​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​o​p​e​n​ ​d​i​r​e​c​t​o​r​y​ ​'​%​s​'​\​n​"​,​ ​p​a​t​h​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​e​x​i​t​ ​(​E​X​I​T​_​F​A​I​L​U​R​E​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​o​f​f​_​t​ ​s​i​z​e​ ​=​ ​0​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​d​i​r​e​n​t​ ​*​e​n​t​r​y​;​
 ​ ​ ​ ​w​h​i​l​e​ ​(​(​e​n​t​r​y​ ​=​ ​r​e​a​d​d​i​r​(​d​i​r​e​c​t​o​r​y​)​)​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​f​i​l​e​n​a​m​e​[​M​A​X​P​A​T​H​L​E​N​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​d​o​n​'​t​ ​m​e​s​s​ ​w​i​t​h​ ​t​h​e​ ​m​e​t​a​d​i​r​e​c​t​o​r​i​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​s​t​r​c​m​p​(​e​n​t​r​y​-​>​d​_​n​a​m​e​,​ ​"​.​"​)​ ​=​=​ ​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​|​|​ ​s​t​r​c​m​p​(​e​n​t​r​y​-​>​d​_​n​a​m​e​,​ ​"​.​.​"​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​i​n​u​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​R​a​t​h​e​r​ ​t​h​a​n​ ​c​h​a​n​g​i​n​g​ ​t​h​e​ ​c​u​r​r​e​n​t​ ​w​o​r​k​i​n​g​ ​d​i​r​e​c​t​o​r​y​ ​e​a​c​h​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​t​i​m​e​ ​t​h​r​o​u​g​h​ ​t​h​e​ ​l​o​o​p​,​ ​c​o​n​s​t​r​u​c​t​ ​t​h​e​ ​f​u​l​l​ ​p​a​t​h​ ​r​e​l​a​t​i​v​e​ ​t​h​e​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​g​i​v​e​n​ ​p​a​t​h​.​ ​ ​B​e​c​a​u​s​e​ ​t​h​e​ ​o​r​i​g​i​n​a​l​ ​p​a​t​h​ ​i​s​ ​e​i​t​h​e​r​ ​a​b​s​o​l​u​t​e​,​ ​o​r​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​r​e​l​a​t​i​v​e​ ​t​o​ ​t​h​e​ ​c​u​r​r​e​n​t​ ​w​o​r​k​i​n​g​ ​d​i​r​e​c​t​o​r​y​,​ ​t​h​i​s​ ​s​h​o​u​l​d​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​a​l​w​a​y​s​ ​g​i​v​e​ ​u​s​ ​a​ ​s​t​a​t​-​a​b​l​e​ ​p​a​t​h​
 ​ ​ ​ ​ ​ ​ ​ ​s​n​p​r​i​n​t​f​ ​(​f​i​l​e​n​a​m​e​,​ ​M​A​X​P​A​T​H​L​E​N​,​ ​"​%​s​/​%​s​"​,​ ​p​a​t​h​,​ ​e​n​t​r​y​-​>​d​_​n​a​m​e​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​U​s​e​ ​l​s​t​a​t​ ​s​o​ ​w​e​ ​d​o​n​'​t​ ​m​u​l​t​i​p​l​y​-​c​o​u​n​t​ ​t​h​e​ ​s​i​z​e​s​ ​o​f​ ​f​i​l​e​s​ ​t​h​a​t​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​a​r​e​ ​p​o​i​n​t​e​d​ ​t​o​ ​b​y​ ​s​y​m​l​i​n​k​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​t​a​t​ ​s​t​a​t​b​u​f​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​l​s​t​a​t​ ​(​f​i​l​e​n​a​m​e​,​ ​&​s​t​a​t​b​u​f​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​!​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​s​t​a​t​ ​'​%​s​'​:​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​t​r​y​-​>​d​_​n​a​m​e​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​i​n​u​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​R​e​c​u​r​s​e​ ​i​n​t​o​ ​s​u​b​f​o​l​d​e​r​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​S​_​I​S​D​I​R​(​s​t​a​t​b​u​f​.​s​t​_​m​o​d​e​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​ ​+​=​ ​s​i​z​e​F​o​r​F​o​l​d​e​r​S​t​a​t​ ​(​f​i​l​e​n​a​m​e​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​g​_​v​e​r​b​o​s​e​)​ ​p​r​i​n​t​f​ ​(​"​%​l​l​d​ ​%​s​\​n​"​,​ ​s​t​a​t​b​u​f​.​s​t​_​s​i​z​e​,​ ​e​n​t​r​y​-​>​d​_​n​a​m​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​ ​+​=​ ​s​t​a​t​b​u​f​.​s​t​_​s​i​z​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​c​l​o​s​e​d​i​r​ ​(​d​i​r​e​c​t​o​r​y​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​i​z​e​;​

}​ ​/​/​ ​s​i​z​e​F​o​r​F​o​l​d​e​r​S​t​a​t​

/​/​ ​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
/​/​ ​g​e​t​d​i​r​e​n​t​r​i​e​s​a​t​t​r​ ​c​o​d​e​

/​/​ ​T​h​e​ ​a​t​t​r​i​b​u​t​e​s​ ​w​e​ ​w​a​n​t​ ​t​o​ ​g​e​t​ ​w​i​t​h​ ​e​a​c​h​ ​c​a​l​l​ ​t​o​ ​g​e​t​d​i​r​e​n​t​r​i​e​s​a​t​t​r​.​
s​t​a​t​i​c​ ​s​t​r​u​c​t​ ​a​t​t​r​l​i​s​t​ ​g​_​a​t​t​r​l​i​s​t​;​ ​/​/​ ​g​e​t​s​ ​z​e​r​o​e​d​ ​a​u​t​o​m​a​t​i​c​a​l​l​y​

/​/​ ​T​h​e​ ​d​a​t​a​ ​b​e​i​n​g​ ​r​e​t​u​r​n​e​d​ ​b​y​ ​e​a​c​h​ ​c​a​l​l​
t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​f​i​l​e​i​n​f​o​ ​{​
 ​ ​ ​ ​u​_​i​n​t​3​2​_​t​ ​ ​ ​ ​ ​ ​ ​ ​l​e​n​g​t​h​;​
 ​ ​ ​ ​a​t​t​r​r​e​f​e​r​e​n​c​e​_​t​ ​ ​n​a​m​e​;​
 ​ ​ ​ ​f​s​o​b​j​_​t​y​p​e​_​t​ ​ ​ ​ ​ ​o​b​j​T​y​p​e​;​
 ​ ​ ​ ​o​f​f​_​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​l​o​g​i​c​a​l​S​i​z​e​;​
}​ ​f​i​l​e​i​n​f​o​;​

/​/​ ​t​r​y​ ​t​o​ ​p​i​c​k​ ​u​p​ ​t​h​i​s​ ​m​a​n​y​ ​e​n​t​r​i​e​s​ ​e​a​c​h​ ​t​i​m​e​ ​t​h​r​o​u​g​h​
#​d​e​f​i​n​e​ ​E​N​T​R​I​E​S​_​C​O​U​N​T​ ​3​0​

/​/​ ​D​o​n​'​t​ ​k​n​o​w​ ​h​o​w​ ​l​o​n​g​ ​e​a​c​h​ ​f​i​l​e​ ​n​a​m​e​ ​i​s​,​ ​s​o​ ​m​a​k​e​ ​a​ ​g​u​e​s​s​ ​s​o​ ​w​e​ ​c​a​n​
/​/​ ​s​i​z​e​ ​t​h​e​ ​r​e​s​u​l​t​s​ ​b​u​f​f​e​r​
#​d​e​f​i​n​e​ ​A​V​G​_​N​A​M​E​_​G​U​E​S​S​T​I​M​A​T​E​ ​6​4​

o​f​f​_​t​ ​s​i​z​e​F​o​r​F​o​l​d​e​r​A​t​t​r​ ​(​c​h​a​r​ ​*​p​a​t​h​)​ ​{​
 ​ ​ ​ ​o​f​f​_​t​ ​s​i​z​e​ ​=​ ​0​;​
 ​ ​ ​ ​i​n​t​ ​f​d​ ​=​ ​o​p​e​n​ ​(​p​a​t​h​,​ ​O​_​R​D​O​N​L​Y​)​;​

 ​ ​ ​ ​i​f​ ​(​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​o​p​e​n​ ​d​i​r​e​c​t​o​r​y​ ​'​%​s​'​\​n​"​,​ ​p​a​t​h​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​e​x​i​t​ ​(​E​X​I​T​_​F​A​I​L​U​R​E​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​A​ ​r​o​u​g​h​ ​g​u​e​s​s​ ​o​n​ ​t​h​e​ ​a​p​p​r​o​p​r​i​a​t​e​ ​b​u​f​f​e​r​ ​s​i​z​e​
 ​ ​ ​ ​c​h​a​r​ ​a​t​t​r​b​u​f​[​E​N​T​R​I​E​S​_​C​O​U​N​T​ ​*​ ​(​s​i​z​e​o​f​(​f​i​l​e​i​n​f​o​)​ ​+​ ​A​V​G​_​N​A​M​E​_​G​U​E​S​S​T​I​M​A​T​E​)​]​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​u​_​i​n​t​3​2​_​t​ ​c​o​u​n​t​ ​=​ ​E​N​T​R​I​E​S​_​C​O​U​N​T​;​
 ​ ​ ​ ​ ​ ​ ​ ​u​_​i​n​t​3​2​_​t​ ​n​e​w​S​t​a​t​e​ ​=​ ​0​;​
 ​ ​ ​ ​ ​ ​ ​ ​u​_​i​n​t​3​2​_​t​ ​b​a​s​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​g​e​t​d​i​r​e​n​t​r​i​e​s​a​t​t​r​ ​(​f​d​,​ ​&​g​_​a​t​t​r​l​i​s​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​t​t​r​b​u​f​,​ ​s​i​z​e​o​f​(​a​t​t​r​b​u​f​)​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​&​c​o​u​n​t​,​ ​&​b​a​s​e​,​ ​&​n​e​w​S​t​a​t​e​,​ ​0​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​<​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​ ​w​i​t​h​ ​g​e​t​d​i​r​e​n​t​r​i​e​s​a​t​t​r​ ​f​o​r​ ​'​%​s​'​.​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​a​t​h​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​w​a​l​k​ ​t​h​e​ ​r​e​t​u​r​n​e​d​ ​b​u​f​f​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​f​i​l​e​i​n​f​o​ ​*​s​c​a​n​ ​=​ ​(​f​i​l​e​i​n​f​o​ ​*​)​ ​a​t​t​r​b​u​f​;​

 ​ ​ ​ ​ ​ ​ ​ ​f​o​r​ ​(​;​ ​c​o​u​n​t​ ​>​ ​0​;​ ​c​o​u​n​t​-​-​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​s​c​a​n​-​>​o​b​j​T​y​p​e​ ​=​=​ ​V​D​I​R​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​f​i​l​e​n​a​m​e​[​M​A​X​P​A​T​H​L​E​N​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​n​p​r​i​n​t​f​ ​(​f​i​l​e​n​a​m​e​,​ ​M​A​X​P​A​T​H​L​E​N​,​ ​"​%​s​/​%​s​"​,​ ​p​a​t​h​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​(​c​h​a​r​ ​*​)​ ​&​s​c​a​n​-​>​n​a​m​e​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​+​ ​s​c​a​n​-​>​n​a​m​e​.​a​t​t​r​_​d​a​t​a​o​f​f​s​e​t​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​ ​+​=​ ​s​i​z​e​F​o​r​F​o​l​d​e​r​A​t​t​r​ ​(​f​i​l​e​n​a​m​e​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​g​_​v​e​r​b​o​s​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​l​l​d​ ​%​s​\​n​"​,​ ​s​c​a​n​-​>​l​o​g​i​c​a​l​S​i​z​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​(​c​h​a​r​ ​*​)​ ​&​s​c​a​n​-​>​n​a​m​e​)​ ​+​ ​s​c​a​n​-​>​n​a​m​e​.​a​t​t​r​_​d​a​t​a​o​f​f​s​e​t​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​ ​+​=​ ​s​c​a​n​-​>​l​o​g​i​c​a​l​S​i​z​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​M​o​v​e​ ​t​o​ ​t​h​e​ ​n​e​x​t​ ​a​t​t​r​i​b​u​t​e​ ​i​n​ ​t​h​e​ ​r​e​t​u​r​n​e​d​ ​s​e​t​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​ ​=​ ​(​f​i​l​e​i​n​f​o​*​)​ ​(​(​(​c​h​a​r​ ​*​)​ ​s​c​a​n​)​ ​+​ ​s​c​a​n​-​>​l​e​n​g​t​h​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​e​'​r​e​ ​d​o​n​e​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​c​l​o​s​e​ ​(​f​d​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​i​z​e​;​

}​ ​/​/​ ​s​i​z​e​F​o​r​F​o​l​d​e​r​A​t​t​r​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​/​/​ ​s​a​n​i​t​y​ ​c​h​e​c​k​ ​t​h​e​ ​p​r​o​g​r​a​m​ ​a​r​g​u​m​e​n​t​s​ ​f​i​r​s​t​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​!​=​ ​3​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​s​a​g​e​:​ ​ ​%​s​ ​{​s​t​a​t​|​a​t​t​r​}​ ​/​p​a​t​h​/​t​o​/​d​i​r​e​c​t​o​r​y​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​o​f​f​_​t​ ​s​i​z​e​ ​=​ ​0​;​

 ​ ​ ​ ​i​f​ ​(​s​t​r​c​m​p​(​a​r​g​v​[​1​]​,​ ​"​s​t​a​t​"​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​ ​=​ ​s​i​z​e​F​o​r​F​o​l​d​e​r​S​t​a​t​ ​(​a​r​g​v​[​2​]​)​;​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​s​t​r​c​m​p​(​a​r​g​v​[​1​]​,​ ​"​a​t​t​r​"​)​ ​=​=​ ​0​)​ ​{​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​t​h​e​s​e​ ​a​r​e​ ​t​h​e​ ​a​t​t​r​i​b​u​t​e​s​ ​w​e​'​r​e​ ​w​a​n​t​i​n​g​.​ ​ ​S​e​t​ ​t​h​e​m​ ​u​p​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​g​l​o​b​a​l​l​y​ ​s​o​ ​w​e​ ​d​o​n​'​t​ ​h​a​v​e​ ​t​o​ ​d​o​ ​a​ ​m​e​m​s​e​t​ ​+​ ​t​h​i​s​ ​j​a​z​z​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​o​n​ ​e​v​e​r​y​ ​r​e​c​u​r​s​i​o​n​

 ​ ​ ​ ​ ​ ​ ​ ​g​_​a​t​t​r​l​i​s​t​.​b​i​t​m​a​p​c​o​u​n​t​ ​=​ ​A​T​T​R​_​B​I​T​_​M​A​P​_​C​O​U​N​T​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​_​a​t​t​r​l​i​s​t​.​c​o​m​m​o​n​a​t​t​r​ ​=​ ​A​T​T​R​_​C​M​N​_​N​A​M​E​ ​|​ ​A​T​T​R​_​C​M​N​_​O​B​J​T​Y​P​E​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​_​a​t​t​r​l​i​s​t​.​f​i​l​e​a​t​t​r​ ​=​ ​A​T​T​R​_​F​I​L​E​_​D​A​T​A​L​E​N​G​T​H​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​U​s​i​n​g​ ​A​T​T​R​_​F​I​L​E​_​T​O​T​A​L​S​I​Z​E​ ​w​o​u​l​d​ ​b​e​ ​b​e​t​t​e​r​ ​s​o​ ​t​h​a​t​ ​w​e​ ​g​e​t​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​t​h​e​ ​s​p​a​c​e​ ​c​o​n​s​u​m​e​d​ ​b​y​ ​r​e​s​o​u​r​c​e​ ​f​o​r​k​s​,​ ​b​u​t​ ​w​e​'​r​e​ ​u​s​i​n​g​ ​t​h​i​s​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​c​o​d​e​ ​t​o​ ​p​a​r​a​l​l​e​l​ ​w​h​a​t​ ​s​t​a​t​ ​g​i​v​e​s​ ​u​s​,​ ​w​h​i​c​h​ ​d​o​e​s​n​'​t​ ​i​n​c​l​u​d​e​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​r​e​s​o​u​r​c​e​ ​f​o​r​k​ ​s​i​z​e​.​

 ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​ ​=​ ​s​i​z​e​F​o​r​F​o​l​d​e​r​A​t​t​r​ ​(​a​r​g​v​[​2​]​)​;​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​s​a​g​e​:​ ​ ​%​s​ ​{​s​t​a​t​|​a​t​t​r​}​ ​/​p​a​t​h​/​t​o​/​d​i​r​e​c​t​o​r​y​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​s​i​z​e​ ​i​s​ ​%​l​l​d​ ​b​y​t​e​s​ ​(​%​l​l​d​ ​K​)​.​\​n​"​,​ ​s​i​z​e​,​ ​s​i​z​e​ ​/​ ​1​0​2​4​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

Run the program by telling it whether you want to run the “stat” or “attr” version of the code, while also giving it a starting directory:

%​ ​.​/​f​o​l​d​e​r​s​i​z​e​ ​s​t​a​t​ ​/​D​e​v​e​l​o​p​e​r​
s​i​z​e​ ​i​s​ ​1​0​6​5​9​4​1​1​0​1​8​ ​b​y​t​e​s​ ​(​1​0​4​0​9​5​8​1​ ​K​)​.​

%​ ​.​/​f​o​l​d​e​r​s​i​z​e​ ​a​t​t​r​ ​/​D​e​v​e​l​o​p​e​r​/​
s​i​z​e​ ​i​s​ ​1​0​6​5​9​4​1​1​0​1​8​ ​b​y​t​e​s​ ​(​1​0​4​0​9​5​8​1​ ​K​)​.​

Things get interesting when you start timing the program. This timing is done in the C shell because it has a better time.

%​ ​t​i​m​e​ ​.​/​f​o​l​d​e​r​s​i​z​e​ ​s​t​a​t​ ​/​D​e​v​e​l​o​p​e​r​
s​i​z​e​ ​i​s​ ​1​0​6​5​9​4​1​1​0​1​8​ ​b​y​t​e​s​ ​(​1​0​4​0​9​5​8​1​ ​K​)​.​
0​.​2​4​9​u​ ​3​.​1​2​0​s​ ​0​:​0​3​.​3​7​ ​9​9​.​7​%​ ​ ​ ​ ​ ​ ​0​+​0​k​ ​0​+​0​i​o​ ​0​p​f​+​0​w​

%​ ​t​i​m​e​ ​.​/​f​o​l​d​e​r​s​i​z​e​ ​a​t​t​r​ ​/​D​e​v​e​l​o​p​e​r​
s​i​z​e​ ​i​s​ ​1​0​6​5​9​4​1​1​0​1​8​ ​b​y​t​e​s​ ​(​1​0​4​0​9​5​8​1​ ​K​)​.​
0​.​0​3​8​u​ ​0​.​7​0​9​s​ ​0​:​0​0​.​7​5​ ​9​7​.​3​%​ ​ ​ ​ ​ ​ ​0​+​0​k​ ​0​+​0​i​o​ ​0​p​f​+​0​w​

The time command is discussed in Chapter 10: Performance Tuning, but the thing to look for here is the time that is shown in bold. It took three seconds to crawl through the Xcode directory with the stat() version but about a two-thirds of a second with the getdirentriesattr() function. These tests were run multiple times so that the file system metadata cache would be warmed up.

Mac OS X Specific Weirdness

OS X is a hybrid between classic Mac OS and Unix, so there are some peculiarities unique to OS X.

Resource forks

The first weirdness is resource forks, the additional stream that files have which store structured data. At the file system level, forks are really just two files that happen to share a name. Until Mac OS X 10.4, the Unix command-line utilities did not know resource forks were there, so it was possible for the resource fork to get lost or corrupted. cp and tar, for instance, did not preserve the fork.

In the command-line environment, you cannot really see the resource fork unless you append /..namedfork/rsrc to the name of the file. For example, the outline processor that we used to outline the first edition of this book was the 1990’s version of More running in the Classic environment. Here is a look at More in the terminal:

$​ ​c​d​ ​/​A​p​p​l​i​c​a​t​i​o​n​s​
$​ ​l​s​ ​-​l​ ​M​O​R​E​
-​r​w​-​r​-​-​r​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​u​n​k​n​o​w​n​ ​ ​0​ ​J​u​n​ ​2​3​ ​1​2​:​1​8​ ​M​O​R​E​

Hmmm, nothing there. But back in the Old Days, almost nothing was put into an application’s data fork. Everything went into the resource fork:

$​ ​l​s​ ​-​l​ ​M​O​R​E​/​.​.​n​a​m​e​d​f​o​r​k​/​r​s​r​c​
-​r​w​-​r​-​-​r​-​-​ ​ ​1​ ​m​a​r​k​d​ ​ ​u​n​k​n​o​w​n​ ​ ​6​0​7​2​5​1​ ​J​u​n​ ​2​3​ ​1​2​:​1​8​ ​M​O​R​E​/​.​.​n​a​m​e​d​f​o​r​k​/​r​s​r​c​

Most of the calls you have seen in this chapter do not pay any attention to the resource fork; they just look at the data fork. If you need to support resource fork reading and writing, you will need to use the Carbon libraries. getattrlist() and getdirentriesattr() will give you metadata about the resource fork.

.DS_Store

If you ls -a in directories, you will probably find a file named .DS_Store. This is a cache file the Finder writes that contains file names and icon placement. When mounting network volumes, the Finder will scribble the files into any directory the user visits, which can sometimes lead to friction between the Mac users and the people who run the file servers.

Disk I/O and sleep

A programmer on one of the mailing lists was describing a problem he was having with a program that works like tail -f, which monitors the log file and reads any new stuff that is appended. Much to his horror, machines that were running this program would never go to sleep. If a lot of I/O happens (say more than one physical disk I/O every couple of seconds), the OS considers the machine to be busy and will not go to sleep. In this particular case, he was seeking to the end and attempting to read. The suggestion was to use stat() instead to see if the file changed size. stat() does not cause physical I/O once the inode for a file is cached in memory.

For The More Curious

Differences between HFS+ and UFS

The Apple engineers did a masterful job of integrating HFS+, the Class Mac OS disk format, into the world of Unix. The above discussion about inodes is more accurate for UFS, the Unix file system, also known as the Berkeley Fast File System, but the general concepts apply to both worlds.

In HFS+, file name encoding is in unicode rather than ASCII. File names are also case preserving, but case insensitive. Traditional Unix file systems are case sensitive. The Apple teams originally thought this case preserving / case sensitive difference would cause lots of problems, but it turns out not to be so bad. There are not many examples in the real world where the case of letters in filenames is used to discriminate. Unfortunately, with the make command, makefile and Makefile are different beasts. There are some open source projects that have distinct files that differ only in the case of their names. A way around this limitation is to create a UFS partition for these malcontents, or you can use Disk Utility to create a UFS disk image.

The path separator for UFS is /, but in HFS+, it is :. This is addressed by the HFS+ file system implementation converting colons to slashes and vice versa. The file system sees colons, but everything above that is slashes.

Until OS X 10.5, HFS+ lacked support for hard links. Hardlinks are actually implemented by a kernel-level symbolic link visible only to the HFS+ file system. The behavior is very similar to hard links when viewed from above the kernel, but they are relatively inefficient in comparison.

UFS does not support file IDs. File IDs are persistent handles to files like inodes, and they can be used similar to path names in Unix. The nice thing about file IDs is that once the ID is obtained, the file can be renamed or moved anywhere on disk and still be found and opened. Also, in HFS+, access by ID is faster than by path (since it avoids path parsing and traversal). These file IDs are part of how aliases do their thing.

HFS+ also allows for arbitrarily named file attributes. Mac OS X lets you get and set these attributes.

HFS+ filenames are at most 255 characters long, or 512 bytes. Files have a maximum size of 2⁶³ bytes.

From running experiments, it does not look like HFS+ supports holes in files. In traditional Unix file systems, if you write a byte, seek 100 megs, then write another byte, your file will only occupy two disk blocks. The intervening zero-filled blocks will not actually be consuming space in the file system. This is a disk space optimization for files that could have large ranges of zeros in them, like core files.

There are some cultural differences regarding file names. Mac OS users use spaces and special characters in their file names, while Unix folks do not. This can lead to really bad situations like an old iTunes installer wiping out entire disks because a script did not consider that a volume name might have a space in it. So if you are doing any file name manipulations, especially in shell scripts, be sure to keep this in mind.

System directories that Unix folks are familiar with (/etc, /usr, /tmp, /var) are hidden at the application level, and some are hidden behind symbolic links so that casual Mac users will not need to worry about them.

The usual Unix software install paradigm (putting library files into */lib, program files into */bin, and documentation into */share) does not work that well with individually administered systems like personal computers. One program can end up scattering junk everywhere, making uninstallation nearly impossible. NeXT bundles, the directories that behave like files, addressed a lot of problems here. The OS X folks liked bundles so much, they followed the same scheme for system-wide libraries. Rather than put stuff into /usr/include and /usr/lib, it goes into framework bundles.

Other random calls

Here are some system calls that did not fit in well anyplace else.

int rename (const char *from, const char *to);

This causes from to be renamed to to. This is just inode/directory manipulation, so both from and to must live on the same file system. This returns zero on success and -1 / errno on error.

int dup (int fd);

int dup2 (int fd, int newfd);

These functions duplicate an existing file descriptor as if you opened the file again. The return value of dup() is a file descriptor that is guaranteed to be the lowest numbered available (or -1 on error). The original file is not closed. A use for dup() is reassigning one of the standard streams. You can close file descriptor zero (standard in), open another file (say a log file), and then dup() that descriptor so that you get fd zero.

dup2() has slightly different semantics. It will create a duplicate file descriptor at a particular file descriptor value. If there is already a file open using that descriptor, it is closed first. This is another way of replacing the standard streams. Since it does a close() and dup() in one operation, it is atomic.

Note that duplicated file descriptors share the same reference in the per-process file table. So a write to one fd will move the current location of the second fd. If you want a true independent reference, reopen the file.

The fcntl() function is the kitchen-sink function for setting various attributes on file descriptors and for performing some operations as well:

int fcntl (int fd, int cmd, int arg);

fd is the file descriptor to manipulate. This can be an opened file, a socket connection, or any other entity that is referred to by a file descriptor. cmd is the command to apply to the file descriptor, and arg is an argument to the command.

Here are some fcntl() commands:

F_DUPFD | Duplicate a file descriptor. The new fd is the lowest numbered descriptor that is not open that is greater than or equal to the third argument (as an integer).
F_GETFD / F_SETFD | Get/set per-processor file descriptor flags, such as FD_CLOEXEC, to close the file automatically in child processes. This will prevent your file descriptors from leaking into a subprocess.
F_GETFL / F_SETFL | Get/set kernel status flags: Such as O_RDONLY / O_WRONLY / O_APPEND, etc.
F_GETOWN / F_SETOWN | Get/set async i/o ownership.

Duplicating descriptors low with dup() and dup2() makes sense when redirecting streams. Why the F_DUPD behavior, then, of duplicating things higher? This actually came in really handy with the AOLserver webserver on older versions of IRIX, the Unix-like system that ran on Silicon Graphics workstations in the 1990s. AOLserver had an embedded Tcl interpreter that used buffered I/O FILEs for certain commands. Unfortunately, the IRIX C library only used a 8-bit value to store the file descriptor in FILEs, so you could have at most 253 of these FILEs. Whenever a non-FILE fd was created, like from a new connection from across the network or opening a file to be returned to the web browser, the fd was duplicated to a value greater than 256 using fcntl(F_DUPFD), leaving the lower values free for the Tcl interpreter.

int truncate (const char *path, off_t length);

int ftruncate (int fd, off_t length);

These truncate a file, whether by path or an open file descriptor, to be at most the given length. Truncating a file smaller may lose some data. Truncating a file to be larger is not-portable, but is supported on Mac OS X.

long pathconf (const char *path, int name);

long fpathconf (int fd, int name);

These provide applications ways to determine the current value of some system limits or options. Here are some possible names:

_PC_LINK_MAX | maximum file link count
_PC_NAME_MAX | maximum number of bytes in a file name
_PC_PATH_MAX | maximum number of bytes in a pathname
_PC_PIPE_BUF | maximum number of bytes that will be written atomically to a pipe

-1 is returned and errno is set if the call fails. If the given variable does not have a limit, -1 is returned and errno is not modified. Otherwise, the current value is returned.

int sysctl (int *name, u_int namelen, void *oldp, size_t *oldlenp,
            void *newp, size_t newlen);

This retrieves system information and lets processes with appropriate privileges set system information. There are all sorts of information you can get from this, like kernel debug values, machine model, cpu count, native byte order, amount of physical memory, the kernel page size, etc. Check out the manpage for more details.

Other random programs

The lsof command shows open files system wide.

chflags program lets you set some attributes on the file. Of particular interest are the [no]schg and [no]uchg flags, which set the system and user immutable flags. When these are set, the files cannot be modified even if their file permissions would allow it. This is handy for hardening a file system against attackers. Many “script-kiddie” system crackers are not prepared for immutable files. This is also the flag that the Finder sets when you select the Locked attribute of a file.

/Developer/Tools/SetFile sets attributes of HFS+ files, which include whether a file is an alias, is a bundle, has a custom icon, is on the desktop, is locked, or is invisible. You can also use it to set the file type and creator.

Access control lists

Mac OS X 10.4 introduced access control lists, commonly abbreviated ACLs. An access control list is a set of permissions in addition to, and sometimes superseding, traditional Unix permissions. This allows you to specify that a particular user has access or is denied access to a particular file with a particular kind of action. Mac OS X’s ACLs are based on the Windows NTFS ACLs.

You can use the chmod command to grant permissions:

$​ ​c​h​m​o​d​ ​+​a​ ​"​m​a​r​k​d​ ​a​l​l​o​w​ ​r​e​a​d​"​ ​s​p​o​o​n​.​t​x​t​

Use the -le flag to show the extra permissions:

$​ ​l​s​ ​-​l​e​ ​s​p​o​o​n​.​t​x​t​
l​s​ ​-​l​e​ ​s​p​o​o​n​
-​r​-​-​-​-​-​-​-​-​ ​+​ ​1​ ​o​o​p​a​c​k​ ​ ​w​h​e​e​l​ ​ ​1​2​ ​J​u​n​ ​ ​6​ ​1​6​:​5​8​ ​s​p​o​o​n​.​t​x​t​
 ​0​:​ ​u​s​e​r​:​m​a​r​k​d​ ​a​l​l​o​w​ ​r​e​a​d​

You can specify permissions for all file system objects about who can delete the object, who can read or write attributes or extended attributes, who can read or write security information, and who can chown the file.

For files, you can put permissions on who can read, write, append, or execute a file. For directories, you have control over who can list, search, add files to, add subdirectories to, or delete children. The ACL API has over 30 function calls in it, so we will not be discussing it here because the kernel will automatically apply ACLs when you try to open files or perform other file manipulations. Check the acl manpage for details.

Extended attributes

Extended attributes, introduced in Mac OS X 10.4, are name/data pairs associated with file system objects. The name is a zero-terminated UTF-8 string, with the value being any kind of data. You can set an extended attribute with setxattr():

int setxattr (const char *path, const char *name, void *value,
              size_t size, uint32_t position, int options);

path is the path to the file to set the attribute for. name is the extended attribute to set. The value of the attribute is specified by value and its size. The position is used as an offset into the attribute so you can change parts of an extended attribute in-place. options controls symbolic link following behavior (XATTR_NOFOLLOW) and flags that control creation and replacing behavior: XATTR_CREATE will fail if the named attribute already exists, and XATTR_REPLACE will fail if the named attribute does not exist.

If you don’t specify either one of these flags, setxattr() will allow both creation and replacement. If you already have a file open, you can use fsetxattr(), which takes a file descriptor rather than a path name. This function returns zero on success and -1 on failure with errno set appropriately.

You can fetch extended attributes using getxattr():

ssize_t getxattr (const char *path, const char *name, void *value,
                  size_t size, uint32_t position, int options);

This function retrieves up to size bytes from the attribute named name for the file specified by path. The position is the position within the attribute to start reading from, and size specifies how many bytes to read from the attribute. The only option available is XATTR_NOFOLLOW, which does not follow symbolic links. The function returns the amount of data actually read from the attribute or -1 on error. In the error case, errno is set accordingly. If you already have an open file, you can use fgetxattr() to get attributes via an existing file descriptor.

When you are done with an extended attribute, you can remove it with removexattr():

int removexattr (const char *path, const char *name, int options);

path is the path to the file, name is the name of the attribute to remove, and option can be zero, or XATTR_NOFOLLOW to not follow symbolic links. This function returns zero on success and -1 on failure, with errno set. If you already have an open file, you can use fremovexattr().

To iterate through existing attributes, use listxattr():

ssize_t listxattr (const char *path, char *namebuf, size_t size,
                   int options);

listxattr() returns a list of names of extended attributes for the file referenced by path and deposits them into the buffer namebuf. If namebuf is NULL, the function returns the size of the list of extended attribute names so you can allocate enough memory. Of course, this is subject to race conditions: somebody else may change the attributes of the file between calling listxattr() with a NULL namebuf and the call with the actual buffer. options should either be zero, or XATTR_NOFOLLOW to not follow symbolic links. The size of the name list is returned. If there are no attributes, zero is returned. Otherwise, -1 is returned, and errno is set. As you might have guessed, there is a flistxattr() function that takes a file descriptor rather than a path.

13
NSFileManager - Cocoa and the File System

Cocoa provides a number of classes and categories that make dealing with the file system easier than using the lower-level Unix interfaces. NSFileManager encapsulates many file system operations, such as moving and renaming files and creating and resolving symbolic links. NSString has categories that make manipulating file paths easier, and starting with Mac OS X 10.6, NSURL has become the preferred way of identifying objects in the file system of interest.

Making and Manipulating Paths

Unix identifies objects in the file system with paths. A path is a listing of directories and optionally a filename, collectively known as path components, separated by a forward slash. Classic Mac OS, as well as parts of the Carbon layer, use a colon for a path separator. The file extension, such as .txt or .pages, is the path extension. The different pieces are shown in Figure 13.1. The path components are Users, markd, Documents, and Badgers.acorn. The path extension is .acorn.

Figure 13.1  Path components

The Unix file system is represented as a single tree of directories that contain files and other objects. Additional file systems are “mounted” in this tree, extending the single file system with the contents of the new file systems. An absolute path starts with a forward slash, signifying that the path originates with the root of the file system. Relative paths do not have a leading path separator and should be interpreted relative to some other directory.

NSString path utilities

The NSStringPathExtensions category adds a number of methods to NSString for constructing and breaking apart paths. You should choose these methods over hard-coding paths or using other string mechanisms for constructing paths like -stringByAppendingString:. Typically you start with a base path, such as the user’s home directory or a temporary directory. Then you append path components and path extensions with these methods:

- (NSString *) stringByAppendingPathComponent: (NSString *) str;

- (NSString *) stringByAppendingPathExtension: (NSString *) str;

To build the path seen in Figure 13.1, you would do something like this:

 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​ ​=​ ​N​S​H​o​m​e​D​i​r​e​c​t​o​r​y​ ​(​)​;​
 ​ ​ ​ ​p​a​t​h​ ​=​ ​[​p​a​t​h​ ​s​t​r​i​n​g​B​y​A​p​p​e​n​d​i​n​g​P​a​t​h​C​o​m​p​o​n​e​n​t​:​ ​@​"​D​o​c​u​m​e​n​t​s​"​]​;​
 ​ ​ ​ ​p​a​t​h​ ​=​ ​[​p​a​t​h​ ​s​t​r​i​n​g​B​y​A​p​p​e​n​d​i​n​g​P​a​t​h​C​o​m​p​o​n​e​n​t​:​ ​@​"​B​a​d​g​e​r​s​"​]​;​
 ​ ​ ​ ​p​a​t​h​ ​=​ ​[​p​a​t​h​ ​s​t​r​i​n​g​B​y​A​p​p​e​n​d​i​n​g​P​a​t​h​E​x​t​e​n​s​i​o​n​:​ ​@​"​a​c​o​r​n​"​]​;​

If you already have an array of path components, you can join them together with this:

+ (NSString *) pathWithComponents: (NSArray *) components;

If all you are interested in is the last component, which is usually the filename+extension, you can query the string:

- (NSString *) lastPathComponent;

- (NSString *) pathExtension;

You can ask a path to break itself into components for further processing:

- (NSArray *) pathComponents;

You can also modify the path by taking off components. This does not destructively modify the string. Instead, it makes a new one a bit shorter than the original and returns it.

- (NSString *) stringByDeletingLastPathComponent;

- (NSString *) stringByDeletingPathExtension;

It is common to represent the user’s home directory with a tilde (~) allowing a path to be specified relative to the user’s $HOME. You can collapse a string by replacing the home directory with ~, as well as expanding it:

- (NSString *) stringByAbbreviatingWithTildeInPath;

- (NSString *) stringByExpandingTildeInPath;

You can ask a path if it is an absolute path:

- (BOOL) isAbsolutePath;

When you need to pass a path to a Unix system call or library function, you should encode the string in a way so that the kernel can handle it. This is known as the “file system representation,” which is UTF-8, and is obtainable by this method:

- (__strong const char *) fileSystemRepresentation;

The returned memory will automatically be cleaned up.

NSURL path utilities

Starting in OS X 10.6, Apple has added a number of path and file features to NSURL, and Apple recommends using NSURLs over NSString-based paths. NSURL performs caching of metadata and other information under the hood to improve overall performance. For instance, there are some operations in Cocoa that happen on paths that end up involving multiple conversions between string paths and Carbon FSRefs, some of which require a disk I/O. The URL-based file system locators remove these inefficiencies.

You can make a file URL with a path or with an array of path components:

+ (id) fileURLWithPath: (NSString *) path  isDirectory: (BOOL) isDir;

+ (id) fileURLWithPath: (NSString *) path;

+ (NSURL *) fileURLWithPathComponents: (NSArray *) components;

The first form is more efficient if you already know that a path is a directory because it can avoid a disk I/O to make that determination.

There are a number of utility methods that should look familiar:

- (NSArray *) pathComponents;

- (NSString *) lastPathComponent;

- (NSString *) pathExtension;

- (NSURL *) URLByAppendingPathComponent: (NSString *) pathComponent;

- (NSURL *) URLByDeletingLastPathComponent;

- (NSURL *) URLByAppendingPathExtension: (NSString *) pathExtension;

- (NSURL *) URLByDeletingPathExtension;

These work in an analogous manner to the similarly-named NSString methods.

Finding Standard Directories

Frequently, you will construct a path or URL by appending your application’s specific path components to another well-known location, such as the user’s home directory, or the Application Support directory.

The functions NSHomeDirectory() and NSTemporaryDirectory() return paths to the home directory and temporary directory, respectively. There are also lookup functions and methods that give you other directories of interest. These share two configuration parameters.

The first is the search domain. There are a number of different locations where directories could be. For example, there are a number of “Library” directories: in the user’s home, in /Library, in /Network/Library, and /System/Library. When searching for directories, you can bitwise-OR these domain masks:

NSUserDomainMask

The user’s home directory. This is the only directory on iOS that you can write to.

NSLocalDomainMask

Local to the current machine. You would put things here that are available to all users on a machine. This would be in /Library.

NSNetworkDomainMask

Publicly available location in the local area network. Usually this is not available, but you may have a system administrator that makes company-wide information and configurations available here. Directories found in this domain would be in /Network.

NSSystemDomainMask

Apple’s territory, so hands-off. Apple does not guarantee to preserve any directories in /System/Library during OS update time, even for point-releases, so do not install your own files here.

NSAllDomainsMask

Bitwise-OR of all the above masks.

The second configuration parameter is the search path directories. These are not bit flags but enumerations, so you can only search for one particular kind of directory at a time. But you can look for this directory in multiple domains if you wish. This is a sampling of some directories. See the NSPathUtilities.h header file or the documentation for the complete list:

NSApplicationDirectory

Where applications live

NSLibraryDirectory

The library directory, which is the catch-all for random directories of information that is not really user-accessible.

NSUserDirectory

Users’ home directories

NSDocumentDirectory

The directory to store documents in

NSAutosavedInformationDirectory

A directory for auto-save documents

NSItemReplacementDirectory

A directory you can write an interim version of a file to. Usually you let NSDocument do atomic saves for you, but if you wish to do atomic saves, you would first write the new file to this directory and then replace the original document with this one.

NSApplicationSupportDirectory

A directory in which applications can create subdirectories to store information that may be expensive computationally or impossible to recreate on demand. For instance, a painting program might copy a default set of paint brushes from the potentially read-only application bundle to Application Support so that the user could edit them.

NSCachesDirectory

A place to put discardable cache files

NSDownloadsDirectory

The directory in which Safari (and other applications) places downloaded files from the Internet.

NSMusicDirectory

The user’s music directory

NSPicturesDirectory

The user’s pictures directory

Path utilities

You can get an array of paths for a particular directory in any number of domains from this function:

NSArray *NSSearchPathForDirectoriesInDomains (NSSearchPathDirectory directory,
                                              NSSearchPathDomainMask domainMask,
                                              BOOL expandTilde);

You specify a directory you are interested in, such as NSDocumentDirectory, along with a bitwise-OR of the domains you are interested in. Individual returned directories might include tildes. If you pass YES to expandTilde, the tilde will be automatically replaced with the current user’s home directory.

The directory returned might not actually exist, so you may need to create it before placing any new files or directories there. Example 13.1 finds all of the application support directories available.

Example 13.1. searchpath.m

/​/​ ​s​e​a​r​c​h​p​a​t​h​.​m​ ​-​-​ ​S​h​o​w​ ​N​S​S​e​a​r​c​h​P​a​t​h​F​o​r​D​i​r​e​c​t​o​r​i​e​s​I​n​D​o​m​a​i​n​s​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​o​ ​s​e​a​r​c​h​p​a​t​h​ ​s​e​a​r​c​h​p​a​t​h​.​m​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​p​a​t​h​s​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​S​e​a​r​c​h​P​a​t​h​F​o​r​D​i​r​e​c​t​o​r​i​e​s​I​n​D​o​m​a​i​n​s​ ​(​N​S​A​p​p​l​i​c​a​t​i​o​n​S​u​p​p​o​r​t​D​i​r​e​c​t​o​r​y​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​A​l​l​D​o​m​a​i​n​s​M​a​s​k​,​ ​N​O​)​;​

 ​ ​ ​ ​f​o​r​ ​(​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​ ​i​n​ ​p​a​t​h​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​a​ ​p​a​t​h​!​ ​ ​%​@​"​,​ ​p​a​t​h​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​

}​ ​/​/​ ​m​a​i​n​

And you can see that it found three of them, the user path with a tilde:

$​ ​.​/​s​e​a​r​c​h​p​a​t​h​
a​ ​p​a​t​h​!​ ​ ​~​/​L​i​b​r​a​r​y​/​A​p​p​l​i​c​a​t​i​o​n​ ​S​u​p​p​o​r​t​
a​ ​p​a​t​h​!​ ​ ​/​L​i​b​r​a​r​y​/​A​p​p​l​i​c​a​t​i​o​n​ ​S​u​p​p​o​r​t​
a​ ​p​a​t​h​!​ ​ ​/​N​e​t​w​o​r​k​/​L​i​b​r​a​r​y​/​A​p​p​l​i​c​a​t​i​o​n​ ​S​u​p​p​o​r​t​

URL utilities

You can get an array of URLs in a manner similar to getting an array of paths using this NSFileManager method:

- (NSArray *) URLsForDirectory: (NSSearchPathDirectory) directory
                     inDomains: (NSSearchPathDomainMask) domainMask;

You pass it a directory and a domain mask. There is no tilde equivalent for URLs, so there is no tilde-expansion argument.

One problem with this method (and NSSearchPathForDirectories) is that it returns an array of objects. Even if you are only interested in one path or URL, you still need to dig into the array for the path, also making sure the array is not empty lest you generate a run-time exception. Plus, there is no guarantee the directory exists, so you need to create it yourself. The following method addresses these two problems:

- (NSURL *) URLForDirectory: (NSSearchPathDirectory) directory
                   inDomain: (NSSearchPathDomainMask) domain
          appropriateForURL: (NSURL *) appropriateURL
                     create: (BOOL) shouldCreate
                      error: (NSError **) error;

You give it a single directory and a single domain. You cannot pass NSAllDomainsMask. Pass YES for shouldCreate if the directory should be created with permissions allowing, of course. It returns the URL for the directory, or nil if it was not possible, and returning the proper error in error.

If the directory is NSItemReplacementDirectory, and appropriateURL is non-nil, then the returned URL will vary based on the passed-in URL. In particular, if the URL points to a different machine or file system, the returned URL will be a location on that machine or file system. If the URL is local, the returned URL will be local. Example 13.2 shows -URLForDirectory: in action.

Example 13.2. urldirectory.m

/​/​ ​u​r​l​d​i​r​e​c​t​o​r​y​.​m​ ​-​-​ ​U​s​e​ ​U​R​L​F​o​r​D​i​r​e​c​t​o​r​y​:​i​n​D​o​m​a​i​n​:​e​t​c​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​o​ ​u​r​l​d​i​r​e​c​t​o​r​y​ ​u​r​l​d​i​r​e​c​t​o​r​y​.​m​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​N​S​U​R​L​ ​*​a​p​p​r​o​p​r​i​a​t​e​F​o​r​ ​=​ ​n​i​l​;​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​=​=​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​ ​=​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​U​T​F​8​S​t​r​i​n​g​:​ ​a​r​g​v​[​1​]​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​p​p​r​o​p​r​i​a​t​e​F​o​r​ ​=​ ​[​N​S​U​R​L​ ​f​i​l​e​U​R​L​W​i​t​h​P​a​t​h​:​ ​p​a​t​h​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​N​S​F​i​l​e​M​a​n​a​g​e​r​ ​*​f​m​ ​=​ ​[​[​[​N​S​F​i​l​e​M​a​n​a​g​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​;​
 ​ ​ ​ ​N​S​U​R​L​ ​*​d​i​r​e​c​t​o​r​y​U​R​L​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​f​m​ ​U​R​L​F​o​r​D​i​r​e​c​t​o​r​y​:​ ​N​S​I​t​e​m​R​e​p​l​a​c​e​m​e​n​t​D​i​r​e​c​t​o​r​y​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​D​o​m​a​i​n​:​ ​N​S​U​s​e​r​D​o​m​a​i​n​M​a​s​k​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​p​p​r​o​p​r​i​a​t​e​F​o​r​U​R​L​:​ ​a​p​p​r​o​p​r​i​a​t​e​F​o​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​r​e​a​t​e​:​ ​Y​E​S​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​:​ ​&​e​r​r​o​r​]​;​

 ​ ​ ​ ​i​f​ ​(​d​i​r​e​c​t​o​r​y​U​R​L​ ​=​=​ ​n​i​l​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​C​o​u​l​d​ ​n​o​t​ ​g​e​t​ ​d​i​r​e​c​t​o​r​y​ ​U​R​L​.​ ​E​r​r​o​r​ ​%​@​"​,​ ​e​r​r​o​r​)​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​d​i​r​e​c​t​o​r​y​U​R​L​ ​i​s​ ​%​@​"​,​ ​d​i​r​e​c​t​o​r​y​U​R​L​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

You can supply an argument on the command line to specify the location to pass in the appropriateForURL argument. Here are two sample runs on a machine with two file systems. The first on the main file system.

$​ ​.​/​u​r​l​d​i​r​e​c​t​o​r​y​ ​.​e​m​a​c​s​
d​i​r​e​c​t​o​r​y​U​R​L​ ​i​s​ ​f​i​l​e​:​/​/​l​o​c​a​l​h​o​s​t​/​p​r​i​v​a​t​e​/​v​a​r​/​f​o​l​d​e​r​s​/​h​E​/​h​E​C​L​n​E​M​T​G​R​i​w​a​R​u​P​x​R​j​w​2​E​+​+​+​T​I​/​
 ​ ​ ​ ​-​T​m​p​-​/​T​e​m​p​o​r​a​r​y​I​t​e​m​s​/​(​A​%​2​0​D​o​c​u​m​e​n​t​%​2​0​B​e​i​n​g​%​2​0​S​a​v​e​d​%​2​0​B​y​%​2​0​u​r​l​d​i​r​e​c​t​o​r​y​)​/​

And the second a drive mounted on /Volumes/Video:

$​ ​.​/​u​r​l​d​i​r​e​c​t​o​r​y​ ​/​V​o​l​u​m​e​s​/​V​i​d​e​o​/​A​p​p​l​e​T​V​V​i​d​e​o​s​/​S​c​i​-​F​i​/​Z​a​r​d​o​z​.​m​4​v​
d​i​r​e​c​t​o​r​y​U​R​L​ ​i​s​ ​f​i​l​e​:​/​/​l​o​c​a​l​h​o​s​t​/​V​o​l​u​m​e​s​/​V​i​d​e​o​/​.​T​e​m​p​o​r​a​r​y​I​t​e​m​s​/​f​o​l​d​e​r​s​.​5​0​1​/​
 ​ ​ ​ ​T​e​m​p​o​r​a​r​y​I​t​e​m​s​/​(​A​%​2​0​D​o​c​u​m​e​n​t​%​2​0​B​e​i​n​g​%​2​0​S​a​v​e​d​%​2​0​B​y​%​2​0​u​r​l​d​i​r​e​c​t​o​r​y​)​/​

Notice that the second URL references /Volumes/Video rather than /private/var/folders.

File Metadata

There are a number of functions, system calls, and toolkit APIs for accessing file metadata. NSFileManager provides a path-based way to acquire metadata, as well as URL-based equivalents. The URL-based API actually exposes much more file metadata than the path-based one. Unfortunately, even though the URL-based metadata API is available on iOS, as of iOS 4.3, the methods return nil. So for iOS, you will need to stay with the path-based metadata API.

Metadata through paths

NSFileManager’s -attributesOfItemAtPath: returns a dictionary of attributes.

- (NSDictionary *) attributesOfItemAtPath: (NSString *) path
                                    error: (NSError **) error;

You can access these attributes through an NSDictionary category, of which a subset is shown below. The constant comment following it is the key you would use for dictionary lookup.

@​i​n​t​e​r​f​a​c​e​ ​N​S​D​i​c​t​i​o​n​a​r​y​ ​(​N​S​F​i​l​e​A​t​t​r​i​b​u​t​e​s​)​
-​ ​(​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​l​o​n​g​)​ ​f​i​l​e​S​i​z​e​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​N​S​F​i​l​e​S​i​z​e​
-​ ​(​N​S​D​a​t​e​ ​*​)​ ​f​i​l​e​C​r​e​a​t​i​o​n​D​a​t​e​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​N​S​F​i​l​e​C​r​e​a​t​i​o​n​D​a​t​e​
-​ ​(​N​S​D​a​t​e​ ​*​)​ ​f​i​l​e​M​o​d​i​f​i​c​a​t​i​o​n​D​a​t​e​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​N​S​F​i​l​e​M​o​d​i​f​i​c​a​t​i​o​n​D​a​t​e​
-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​f​i​l​e​T​y​p​e​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​N​S​F​i​l​e​T​y​p​e​
-​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​f​i​l​e​P​o​s​i​x​P​e​r​m​i​s​s​i​o​n​s​;​ ​ ​ ​ ​ ​ ​ ​/​/​ ​N​S​F​i​l​e​P​o​s​i​x​P​e​r​m​i​s​s​i​o​n​s​
-​ ​(​N​S​N​u​m​b​e​r​ ​*​)​ ​f​i​l​e​O​w​n​e​r​A​c​c​o​u​n​t​I​D​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​N​S​F​i​l​e​O​w​n​e​r​A​c​c​o​u​n​t​I​D​
-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​f​i​l​e​O​w​n​e​r​A​c​c​o​u​n​t​N​a​m​e​;​ ​ ​ ​ ​ ​ ​ ​/​/​ ​N​S​F​i​l​e​O​w​n​e​r​A​c​c​o​u​n​t​N​a​m​e​
-​ ​(​N​S​N​u​m​b​e​r​ ​*​)​ ​f​i​l​e​G​r​o​u​p​O​w​n​e​r​A​c​c​o​u​n​t​I​D​;​ ​ ​ ​ ​/​/​ ​N​S​F​i​l​e​G​r​o​u​p​O​w​n​e​r​A​c​c​o​u​n​t​I​D​
-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​f​i​l​e​G​r​o​u​p​O​w​n​e​r​A​c​c​o​u​n​t​N​a​m​e​;​ ​ ​/​/​ ​N​S​F​i​l​e​G​r​o​u​p​O​w​n​e​r​A​c​c​o​u​n​t​N​a​m​e​
-​ ​(​B​O​O​L​)​ ​f​i​l​e​E​x​t​e​n​s​i​o​n​H​i​d​d​e​n​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​N​S​F​i​l​e​E​x​t​e​n​s​i​o​n​H​i​d​d​e​n​
-​ ​(​B​O​O​L​)​ ​f​i​l​e​I​s​I​m​m​u​t​a​b​l​e​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​N​S​F​i​l​e​I​m​m​u​t​a​b​l​e​
-​ ​(​B​O​O​L​)​ ​f​i​l​e​I​s​A​p​p​e​n​d​O​n​l​y​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​N​S​F​i​l​e​A​p​p​e​n​d​O​n​l​y​
@​e​n​d​

The returned dictionary is not guaranteed to have all of these values. The value of -fileType / NSFileType key is one of these string constants:

N​S​F​i​l​e​T​y​p​e​D​i​r​e​c​t​o​r​y​
N​S​F​i​l​e​T​y​p​e​R​e​g​u​l​a​r​
N​S​F​i​l​e​T​y​p​e​S​y​m​b​o​l​i​c​L​i​n​k​
N​S​F​i​l​e​T​y​p​e​S​o​c​k​e​t​
N​S​F​i​l​e​T​y​p​e​C​h​a​r​a​c​t​e​r​S​p​e​c​i​a​l​
N​S​F​i​l​e​T​y​p​e​B​l​o​c​k​S​p​e​c​i​a​l​
N​S​F​i​l​e​T​y​p​e​U​n​k​n​o​w​n​

You can change metadata values as well:

- (BOOL) setAttributes: (NSDictionary *) attributes
          ofItemAtPath: (NSString *) path
                 error: (NSError **) error;

Give it the path to change and give it a dictionary, populated with keys and values from the above list.

Metadata through URLs

NSFileManager’s -attributesOfItemAtPath: returns a subset of available metadata for files and file systems. Through the URL API, you can access many more kinds of metadata in a single call. You can also ask for metadata in batches, allowing for more efficient use of system resources. By asking only for what you need, the toolkit doesn’t need to fetch attribute values that you will never use. This makes it kind of odd that these URL-based metadata calls are no-ops in iOS, and so the optimizations are not available on that platform.

You can access resource values individually via this NSURL method:

- (BOOL) getResourceValue: (id *) value
                   forKey: (NSString *) key
                    error: (NSError **) error;

Pass a key for the resource value and a pointer to an object of the type you are expecting. Recall that id is a pointer type already, so id* is a pointer to a pointer. The call will fill in your pointer with an object and return YES or NO if the call had problems. On failure, you can check error to see what went wrong. It is possible for this method to return YES and also return nil in value. This can happen if there is no metadata available for it. Figure 13.2 shows all (at the time of writing) available resource keys.

Figure 13.2  NSURL resource keys

The types of objects returned are what you would expect from the call. A date resource returns an NSDate in the value pointer, a name returns an NSString, a color returns an NSColor, etc. If your program only uses the Foundation framework and you wish to access resources like colors and images, you will need to link with the AppKit or Cocoa frameworks.

You can get attribute values back in batch as well:

- (NSDictionary *) resourceValuesForKeys: (NSArray *) keys  error: (NSError **);

Give it an array of keys, and it will return a dictionary of the values. There will not be a corresponding value in the dictionary if there is no value for a key.

You can also change resource values, either individually or in bulk:

- (BOOL) setResourceValue: (id) value
                   forKey: (NSString *) key
                    error: (NSError **) error;

- (BOOL) setResourceValues: (NSDictionary *) keyedValues
                     error: (NSError **) error;

Using -setResourceValue:error: is straightforward. Try to set the value. It returns NO if the value could not be changed and returns YES otherwise. -setResourceValues:error: lets you supply a dictionary of metadata values to set at one time. It is possible to have multiple failures when setting multiple properties. The userInfo dictionary of the error has an array of keys that were not set.

Finally, you can use NSFileManager to retrieve an array of URLs for each of the mounted volumes currently on the system, as well as pre-fetch metadata about those volumes:

- (NSArray *) mounted​Volume​URLs​Including​Resource​Values​For​Keys: (NSArray *) propKeys
              options: (NSVolumeEnumerationOptions) options

There are currently two bitmasks for the volume enumeration options:

N​S​V​o​l​u​m​e​E​n​u​m​e​r​a​t​i​o​n​S​k​i​p​H​i​d​d​e​n​V​o​l​u​m​e​s​
N​S​V​o​l​u​m​e​E​n​u​m​e​r​a​t​i​o​n​P​r​o​d​u​c​e​F​i​l​e​R​e​f​e​r​e​n​c​e​U​R​L​s​

Pass zero for no special enumerating behavior. Skipping hidden volumes is just that – it will not return any volumes that would otherwise be hidden to the user. If the file reference URLs option is bitwise-OR’d in, the URLs returned are file reference URLs. File reference URLs are discussed later. Example 13.3 shows the accessing metadata for individual objects in the file system, as well as getting all of the mounted volumes.

Example 13.3. urlmeta.m

/​/​ ​u​r​l​m​e​t​a​.​m​ ​-​-​ ​G​e​t​ ​f​i​l​e​ ​m​e​t​a​d​a​t​a​ ​a​b​o​u​t​ ​a​ ​f​i​l​e​ ​v​i​a​ ​U​R​L​s​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​o​ ​u​r​l​m​e​t​a​ ​u​r​l​m​e​t​a​.​m​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​!​=​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​u​s​a​g​e​:​ ​%​s​ ​p​a​t​h​ ​-​-​ ​p​r​i​n​t​ ​m​e​t​a​d​a​t​a​ ​a​b​o​u​t​ ​f​i​l​e​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​ ​u​s​e​ ​/​ ​f​o​r​ ​v​o​l​u​m​e​ ​i​n​f​o​r​m​a​t​i​o​n​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​i​f​ ​(​s​t​r​l​e​n​(​a​r​g​v​[​1​]​)​ ​=​=​ ​1​ ​&​&​ ​a​r​g​v​[​1​]​[​0​]​ ​=​=​ ​'​/​'​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​F​i​l​e​M​a​n​a​g​e​r​ ​*​f​m​ ​=​ ​[​[​[​N​S​F​i​l​e​M​a​n​a​g​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​v​o​l​u​m​e​s​ ​=​ ​[​f​m​ ​m​o​u​n​t​e​d​V​o​l​u​m​e​U​R​L​s​I​n​c​l​u​d​i​n​g​R​e​s​o​u​r​c​e​V​a​l​u​e​s​F​o​r​K​e​y​s​:​ ​n​i​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​p​t​i​o​n​s​:​ ​0​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​v​o​l​u​m​e​s​ ​a​r​e​ ​%​@​"​,​ ​v​o​l​u​m​e​s​)​;​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​k​e​y​s​ ​=​ ​[​N​S​A​r​r​a​y​ ​a​r​r​a​y​W​i​t​h​O​b​j​e​c​t​s​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​N​a​m​e​K​e​y​,​ ​N​S​U​R​L​L​o​c​a​l​i​z​e​d​N​a​m​e​K​e​y​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​I​s​D​i​r​e​c​t​o​r​y​K​e​y​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​C​r​e​a​t​i​o​n​D​a​t​e​K​e​y​,​ ​n​i​l​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​ ​=​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​U​T​F​8​S​t​r​i​n​g​:​ ​a​r​g​v​[​1​]​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​ ​*​u​r​l​ ​=​ ​[​N​S​U​R​L​ ​f​i​l​e​U​R​L​W​i​t​h​P​a​t​h​:​ ​p​a​t​h​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​a​t​t​r​i​b​u​t​e​s​ ​=​ ​[​u​r​l​ ​r​e​s​o​u​r​c​e​V​a​l​u​e​s​F​o​r​K​e​y​s​:​ ​k​e​y​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​:​ ​&​e​r​r​o​r​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​A​t​t​r​i​b​u​t​e​s​ ​f​o​r​ ​%​s​"​,​ ​a​r​g​v​[​1​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​%​@​"​,​ ​a​t​t​r​i​b​u​t​e​s​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

Here are some sample runs:

$​ ​.​/​u​r​l​m​e​t​a​ ​.​/​u​r​l​m​e​t​a​
A​t​t​r​i​b​u​t​e​s​ ​f​o​r​ ​.​/​u​r​l​m​e​t​a​
 ​ ​ ​ ​N​S​U​R​L​C​r​e​a​t​i​o​n​D​a​t​e​K​e​y​ ​=​ ​"​2​0​1​1​-​0​5​-​2​9​ ​2​0​:​3​7​:​1​3​ ​-​0​4​0​0​"​;​
 ​ ​ ​ ​N​S​U​R​L​I​s​D​i​r​e​c​t​o​r​y​K​e​y​ ​=​ ​0​;​
 ​ ​ ​ ​N​S​U​R​L​L​o​c​a​l​i​z​e​d​N​a​m​e​K​e​y​ ​=​ ​u​r​l​m​e​t​a​;​
 ​ ​ ​ ​N​S​U​R​L​N​a​m​e​K​e​y​ ​=​ ​u​r​l​m​e​t​a​;​

$​ ​.​/​u​r​l​m​e​t​a​ ​~​/​L​i​b​r​a​r​y​
A​t​t​r​i​b​u​t​e​s​ ​f​o​r​ ​/​U​s​e​r​s​/​m​a​r​k​d​/​L​i​b​r​a​r​y​
 ​ ​ ​ ​N​S​U​R​L​C​r​e​a​t​i​o​n​D​a​t​e​K​e​y​ ​=​ ​"​2​0​0​9​-​0​6​-​1​8​ ​2​1​:​0​1​:​2​1​ ​-​0​4​0​0​"​;​
 ​ ​ ​ ​N​S​U​R​L​I​s​D​i​r​e​c​t​o​r​y​K​e​y​ ​=​ ​1​;​
 ​ ​ ​ ​N​S​U​R​L​L​o​c​a​l​i​z​e​d​N​a​m​e​K​e​y​ ​=​ ​L​i​b​r​a​r​y​;​
 ​ ​ ​ ​N​S​U​R​L​N​a​m​e​K​e​y​ ​=​ ​L​i​b​r​a​r​y​;​

Notice the localized name key. Some directories in the file system are displayed to users with a different name than what is actually in the file system. The Library directory is always called Library in the file system, even if it is presented with a different word in a non-English localization. Set the language of your machine to Italian and re-run the last example:

$​ ​.​/​u​r​l​m​e​t​a​ ​~​/​L​i​b​r​a​r​y​
A​t​t​r​i​b​u​t​e​s​ ​f​o​r​ ​/​U​s​e​r​s​/​m​a​r​k​d​/​L​i​b​r​a​r​y​
 ​ ​ ​ ​N​S​U​R​L​C​r​e​a​t​i​o​n​D​a​t​e​K​e​y​ ​=​ ​"​2​0​0​9​-​0​6​-​1​8​ ​2​1​:​0​1​:​2​1​ ​-​0​4​0​0​"​;​
 ​ ​ ​ ​N​S​U​R​L​I​s​D​i​r​e​c​t​o​r​y​K​e​y​ ​=​ ​1​;​
 ​ ​ ​ ​N​S​U​R​L​L​o​c​a​l​i​z​e​d​N​a​m​e​K​e​y​ ​=​ ​L​i​b​r​e​r​i​a​;​
 ​ ​ ​ ​N​S​U​R​L​N​a​m​e​K​e​y​ ​=​ ​L​i​b​r​a​r​y​;​

Now for the volumes. This is a run on a laptop without any extra disks attached:

$​ ​.​/​u​r​l​m​e​t​a​ ​/​
v​o​l​u​m​e​s​ ​a​r​e​
 ​ ​ ​ ​"​f​i​l​e​:​/​/​l​o​c​a​l​h​o​s​t​/​"​,​
 ​ ​ ​ ​"​f​i​l​e​:​/​/​l​o​c​a​l​h​o​s​t​/​h​o​m​e​/​"​,​
 ​ ​ ​ ​"​f​i​l​e​:​/​/​l​o​c​a​l​h​o​s​t​/​n​e​t​/​"​

And then run on a MacPro with four drives installed in the bays:

$​ ​.​/​u​r​l​m​e​t​a​ ​/​
v​o​l​u​m​e​s​ ​a​r​e​
 ​ ​ ​ ​"​f​i​l​e​:​/​/​l​o​c​a​l​h​o​s​t​/​"​,​
 ​ ​ ​ ​"​f​i​l​e​:​/​/​l​o​c​a​l​h​o​s​t​/​V​o​l​u​m​e​s​/​V​i​d​e​o​/​"​,​
 ​ ​ ​ ​"​f​i​l​e​:​/​/​l​o​c​a​l​h​o​s​t​/​V​o​l​u​m​e​s​/​M​e​d​i​a​Z​2​/​"​,​
 ​ ​ ​ ​"​f​i​l​e​:​/​/​l​o​c​a​l​h​o​s​t​/​V​o​l​u​m​e​s​/​D​V​D​I​m​a​g​e​s​/​"​,​
 ​ ​ ​ ​"​f​i​l​e​:​/​/​l​o​c​a​l​h​o​s​t​/​h​o​m​e​/​"​,​
 ​ ​ ​ ​"​f​i​l​e​:​/​/​l​o​c​a​l​h​o​s​t​/​n​e​t​/​"​

The hard drives are the first four, and the last two are for automounted filesystems.

File Operations

NSFileManager also abstracts out a number of common file operations, such as copying, moving, removing, and linking items. As expected, there are two API families: one using string paths and the other using URLs.

Path operations

You can use the following methods to copy, move, (hard) link, and remove files and directories:

- (BOOL) copyItemAtPath: (NSString *) srcPath  toPath: (NSString *) dstPath
                  error: (NSError **) error;

- (BOOL) moveItemAtPath: (NSString *) srcPath  toPath: (NSString *) dstPath
                  error: (NSError **) error;

- (BOOL) linkItemAtPath: (NSString *) srcPath  toPath: (NSString *) dstPath
                  error: (NSError **) error;

- (BOOL) removeItemAtPath: (NSString *) path
                    error: (NSError **) error;

These methods will potentially invoke delegate methods on your NSFileManager instance. If you choose to set a delegate, you should alloc and init your own rather than using the shared +defaultManager.

There are two classes of delegate methods. The first asks if a particular file should be copied, moved, linked, or removed:

- (BOOL) fileManager: (NSFileManager *) fileManager
shouldCopyItemAtPath: (NSString *) srcPath  toPath: (NSString *) dstPath;

- (BOOL) fileManager: (NSFileManager *) fileManager
shouldMoveItemAtPath: (NSString *) srcPath  toPath: (NSString *) dstPath;

- (BOOL) fileManager: (NSFileManager *) fileManager
shouldLinkItemAtPath: (NSString *) srcPath  toPath: (NSString *) dstPath;

- (BOOL) fileManager: (NSFileManager *) fileManager
   shouldRemoveItemAtPath: (NSString *) path;

If you do not implement any of these, it is assumed that they returned YES for every file. If you return NO for a directory, that directory and all of the directory’s children are skipped.

The other delegate methods relate to error handling. Return YES to proceed after an error. Returning NO will stop the operation and return from the original call. Not implementing a delegate method behaves as if it returned NO, that is, it will always give up after the first error.

- (BOOL) fileManager: (NSFileManager *) fileManager
    shouldProceedAfterError: (NSError *) error
   copyingItemAtPath: (NSString *) srcPath
              toPath: (NSString *) dstPath;

- (BOOL) fileManager: (NSFileManager *) fileManager
    shouldProceedAfterError: (NSError *) error
    movingItemAtPath: (NSString *) srcPath
              toPath: (NSString *) dstPath;

- (BOOL) fileManager: (NSFileManager *) fileManager
    shouldProceedAfterError: (NSError *) error
   linkingItemAtPath: (NSString *) srcPath
              toPath: (NSString *) dstPath;

- (BOOL) fileManager: (NSFileManager *) fileManager
    shouldProceedAfterError: (NSError *) error
  removingItemAtPath: (NSString *) path;

You can also create directories:

- (BOOL) createDirectoryAtPath: (NSString *) path
   withIntermediateDirectories: (BOOL) createIntermediates
                    attributes: (NSDictionary *) attributes
                         error: (NSError **) error;

This will create a directory at path. If there are any missing directories in the path, they will be created using the given attributes if createIntermediates is YES. The method returns YES or NO depending on its success, with error being filled in if an error happened. If no attributes are provided, directories are created with mode 777, modified by the process’ umask. If those permissions are too open for your tastes, then you can provide your own permissions in the attributes dictionary.

URL operations

Even though it may come as a complete surprise, NSFileManager can perform file operations using URLs. These are exactly analogous to the path-based operations and invoke the same delegate methods:

- (BOOL) copyItemAtURL: (NSURL *) srcURL  toURL: (NSURL *) dstURL
                 error: (NSError **) error;

- (BOOL) moveItemAtURL: (NSURL *) srcURL  toURL: (NSURL *) dstURL
                 error: (NSError **) error;

- (BOOL) linkItemAtURL: (NSURL *) srcURL  toURL: (NSURL *) dstURL
                 error: (NSError **) error;

- (BOOL) removeItemAtURL: (NSURL *) URL
                   error: (NSError **) error;

Symbolic links

NSFileManager has API for creating and resolving symbolic links. Ordinarily, symbolic links are transparent to you, but you may need to deal with them directly. This creates a new symbolic link:

- (BOOL) createSymbolicLinkAtPath: (NSString *) path
              withDestinationPath: (NSString *) destPath
                            error: (NSError **) error;

You can get what a symbolic link points to.

- (NSString *) destinationOfSymbolicLinkAtPath: (NSString *) path
                                         error: (NSError **) error;

As of Mac OS X 10.6, there are no URL-based calls to create symbolic links or get their values, but you can resolve links. Resolving a link replaces symbolic links in a path with their destinations. This is supported both by paths and URLs:

- (NSString *) stringByResolvingSymlinksInPath;

- (NSURL *) URLByResolvingSymlinksInPath;

Directory Enumeration

Processing the contents of a directory is a common operation. There are a number of NSFileManager calls, which can process a directory in different ways. Both the path-based and URL-based calls will get you the contents of a directory. The URL-based calls can also prefetch the metadata you want, speeding up the operation.

Enumeration with paths

You can get a flat listing of a directory in an array:

- (NSArray *) contentsOfDirectoryAtPath: (NSString *) path
                                  error: (NSError **) error;

This returns an array of file names of all the files in the directory. It is flat because it does not recurse into subdirectories. You can get a deep listing that recurses into subdirectories with this call:

- (NSArray *) subpathsOfDirectoryAtPath: (NSString *) path
                                  error: (NSError **) error;

You can also ask NSFileManager for an NSDictionaryEnumerator object, which when processed, will walk a directory hierarchy:

- (NSDirectoryEnumerator *) enumeratorAtPath: (NSString *) path;

Don’t forget you can feed this enumerator object to fast enumeration:

f​o​r​ ​(​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​ ​i​n​ ​[​f​i​l​e​M​a​n​a​g​e​r​ ​e​n​u​m​e​r​a​t​o​r​A​t​P​a​t​h​:​ ​p​a​t​h​]​)​ ​.​.​.​

NSDirectoryEnumerator provides a number of useful methods:

- (NSDictionary *) fileAttributes;

This returns an attribute dictionary for this file.

- (NSDictionary *) directoryAttributes;

This returns an attribute dictionary for the directory being iterated.

- (NSUInteger) level;

Available in 10.6. this tells you how deep the iteration has run. The directory that started the iteration is considered to be level zero.

- (void) skipDescendents;

This causes the enumerator to skip recursion into the most recently seen subdirectory.

- (void) skipDescendants;

This is available in 10.6 only. So says the header file: "This method is spelled correctly."

Enumeration with URLs

The URL-based directory enumeration methods are more complex than the path-based counterparts. For instance:

- (NSArray *) contentsOfDirectoryAtURL: (NSURL *) url
            includingPropertiesForKeys: (NSArray *) keys
                               options: (NSDirectoryEnumerationOptions) mask
                                 error: (NSError **) error;

This call collects the contents of the directory, prefetches the metadata for the given keys, and returns an array of URLs. Pass nil for keys to prefetch a default set of metadata. Pass an empty array for no prefetching.

The options mask can be composed of these flags:

N​S​D​i​r​e​c​t​o​r​y​E​n​u​m​e​r​a​t​i​o​n​S​k​i​p​s​S​u​b​d​i​r​e​c​t​o​r​y​D​e​s​c​e​n​d​a​n​t​s​
N​S​D​i​r​e​c​t​o​r​y​E​n​u​m​e​r​a​t​i​o​n​S​k​i​p​s​P​a​c​k​a​g​e​D​e​s​c​e​n​d​a​n​t​s​
N​S​D​i​r​e​c​t​o​r​y​E​n​u​m​e​r​a​t​i​o​n​S​k​i​p​s​H​i​d​d​e​n​F​i​l​e​s​

Even though this method takes an enumeration options mask, it does not actually recurse into subdirectories, so you should either pass zero or use the mask to skip hidden files.

To perform a deep iteration, you need to use -enumeratorAtURL:

- (NSDirectoryEnumerator *) enumeratorAtURL: (NSURL *)  url
                 includingPropertiesForKeys: (NSArray *) keys
                                    options: (NSDirectoryEnumerationOptions) mask
                 errorHandler: (BOOL (^) (NSURL *url, NSError *error) ) handler;

Like -contentsOfDirectoryAtURL, this will prefetch the metadata indicated by keys. Unlike -contentsOfDirectoryAtURL, it honors the options to skip subdirectories and/or package files. The error handler is a block that will get invoked if an error happens. Return YES from the block to continue iteration in the face of errors, and return NO to terminate. One thing that is slightly mind-bending about this method is that, even though iteration is happening in a loop after you acquire the NSDirectoryEnumerator, the block you provide could get invoked inside that loop. So code you defined earlier could be called in the iteration loop.

Example 13.4 shows this method being used to walk a directory hierarchy, print out an indented list of files, each file’s size, and accumulate a total sum of the bytes of the files.

Example 13.4. urlenumerator.m

/​/​ ​u​r​l​e​n​u​m​e​r​a​t​o​r​.​m​ ​-​-​ ​I​t​e​r​a​t​e​ ​w​i​t​h​ ​-​e​n​u​m​e​r​a​t​o​r​A​t​U​R​L​,​ ​s​h​o​w​ ​a​ ​t​r​e​e​ ​l​i​s​t​i​n​g​,​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​n​d​ ​c​a​l​c​u​l​a​t​e​ ​d​i​s​k​ ​s​p​a​c​e​ ​c​o​n​s​u​m​p​t​i​o​n​

/​/​ ​g​c​c​ ​-​g​ ​-​s​t​d​=​c​9​9​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​o​ ​u​r​l​e​n​u​m​e​r​a​t​o​r​ ​u​r​l​e​n​u​m​e​r​a​t​o​r​.​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​/​/​ ​S​a​n​i​t​y​-​c​h​e​c​k​,​ ​a​n​d​ ​m​a​k​e​ ​t​h​e​ ​t​a​r​g​e​t​ ​U​R​L​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​!​=​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​u​s​a​g​e​:​ ​%​s​ ​p​a​t​h​ ​-​-​ ​e​n​u​m​e​r​a​t​e​ ​s​t​a​r​t​i​n​g​ ​f​r​o​m​ ​t​h​e​ ​p​a​t​h​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​ ​=​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​U​T​F​8​S​t​r​i​n​g​:​ ​a​r​g​v​[​1​]​]​;​
 ​ ​ ​ ​N​S​U​R​L​ ​*​u​r​l​ ​=​ ​[​N​S​U​R​L​ ​f​i​l​e​U​R​L​W​i​t​h​P​a​t​h​:​ ​p​a​t​h​]​;​

 ​ ​ ​ ​/​/​ ​P​r​e​f​e​c​t​ ​t​h​e​s​e​ ​v​a​l​u​e​s​.​ ​ ​T​h​e​ ​n​a​m​e​ ​i​s​ ​a​l​w​a​y​s​ ​d​i​s​p​l​a​y​e​d​.​
 ​ ​ ​ ​/​/​ ​I​f​ ​t​h​e​ ​U​R​L​ ​i​s​ ​a​ ​d​i​r​e​c​t​o​r​y​,​ ​d​o​n​'​t​ ​a​c​c​u​m​u​l​a​t​e​ ​i​t​s​ ​s​i​z​e​.​
 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​k​e​y​s​ ​=​ ​[​N​S​A​r​r​a​y​ ​a​r​r​a​y​W​i​t​h​O​b​j​e​c​t​s​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​L​o​c​a​l​i​z​e​d​N​a​m​e​K​e​y​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​I​s​D​i​r​e​c​t​o​r​y​K​e​y​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​F​i​l​e​S​i​z​e​K​e​y​,​ ​n​i​l​]​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​e​n​u​m​e​r​a​t​o​r​.​
 ​ ​ ​ ​N​S​F​i​l​e​M​a​n​a​g​e​r​ ​*​f​m​ ​=​ ​[​[​[​N​S​F​i​l​e​M​a​n​a​g​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​N​S​D​i​r​e​c​t​o​r​y​E​n​u​m​e​r​a​t​o​r​ ​*​d​i​r​e​n​u​m​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​f​m​ ​e​n​u​m​e​r​a​t​o​r​A​t​U​R​L​:​ ​u​r​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​c​l​u​d​i​n​g​P​r​o​p​e​r​t​i​e​s​F​o​r​K​e​y​s​:​ ​k​e​y​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​p​t​i​o​n​s​:​ ​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​H​a​n​d​l​e​r​:​ ​^​(​N​S​U​R​L​ ​*​u​r​l​,​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​e​r​r​o​r​ ​f​o​r​ ​%​@​ ​:​ ​%​@​"​,​ ​u​r​l​,​ ​e​r​r​o​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​Y​E​S​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​]​;​
 ​ ​ ​ ​/​/​ ​N​o​w​ ​a​c​t​u​a​l​l​y​ ​i​t​e​r​a​t​e​.​ ​ ​T​h​e​ ​b​l​o​c​k​ ​w​i​l​l​ ​b​e​ ​i​n​v​o​k​e​d​ ​i​n​ ​t​h​i​s​ ​l​o​o​p​ ​i​f​ ​a​n​
 ​ ​ ​ ​/​/​ ​e​r​r​o​r​ ​h​a​p​p​e​n​s​.​

 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​l​o​n​g​ ​t​o​t​a​l​S​i​z​e​ ​=​ ​0​;​
 ​ ​ ​ ​f​o​r​ ​(​N​S​U​R​L​ ​*​u​r​l​ ​i​n​ ​d​i​r​e​n​u​m​)​ ​{​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​a​t​t​r​i​b​u​t​e​s​ ​f​o​r​ ​t​h​e​ ​U​R​L​.​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​a​t​t​r​s​ ​=​ ​[​u​r​l​ ​r​e​s​o​u​r​c​e​V​a​l​u​e​s​F​o​r​K​e​y​s​:​ ​k​e​y​s​ ​ ​e​r​r​o​r​:​ ​&​e​r​r​o​r​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​!​a​t​t​r​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​o​u​l​d​ ​n​o​t​ ​g​e​t​ ​a​t​t​r​i​b​u​t​e​s​ ​f​o​r​ ​%​@​ ​:​ ​%​@​"​,​ ​u​r​l​,​ ​e​r​r​o​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​i​n​u​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​I​n​d​e​n​t​ ​t​h​e​ ​r​o​w​.​
 ​ ​ ​ ​ ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​d​i​r​e​n​u​m​.​l​e​v​e​l​ ​-​ ​1​;​ ​i​+​+​)​ ​p​r​i​n​t​f​ ​(​"​ ​ ​"​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​D​i​s​p​l​a​y​ ​t​h​e​ ​n​a​m​e​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​n​a​m​e​ ​=​ ​[​a​t​t​r​s​ ​o​b​j​e​c​t​F​o​r​K​e​y​:​ ​N​S​U​R​L​L​o​c​a​l​i​z​e​d​N​a​m​e​K​e​y​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​s​ ​"​,​ ​[​n​a​m​e​ ​U​T​F​8​S​t​r​i​n​g​]​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​N​e​w​l​i​n​e​ ​a​n​d​ ​c​o​n​t​i​n​u​e​ ​i​f​ ​i​t​'​s​ ​a​ ​d​i​r​e​c​t​o​r​y​.​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​N​u​m​b​e​r​ ​*​i​s​D​i​r​e​c​t​o​r​y​ ​=​ ​[​a​t​t​r​s​ ​o​b​j​e​c​t​F​o​r​K​e​y​:​ ​N​S​U​R​L​I​s​D​i​r​e​c​t​o​r​y​K​e​y​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​[​i​s​D​i​r​e​c​t​o​r​y​ ​b​o​o​l​V​a​l​u​e​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​i​n​u​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​O​t​h​e​r​w​i​s​e​ ​p​r​i​n​t​ ​t​h​e​ ​s​i​z​e​,​ ​a​n​d​ ​a​c​c​u​m​u​l​a​t​e​ ​t​h​e​ ​s​i​z​e​ ​s​u​m​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​N​u​m​b​e​r​ ​*​f​i​l​e​s​i​z​e​ ​=​ ​[​a​t​t​r​s​ ​o​b​j​e​c​t​F​o​r​K​e​y​:​ ​N​S​U​R​L​F​i​l​e​S​i​z​e​K​e​y​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​l​o​n​g​ ​s​i​z​e​ ​=​ ​[​f​i​l​e​s​i​z​e​ ​l​o​n​g​L​o​n​g​V​a​l​u​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​s​i​z​e​ ​<​ ​1​0​2​4​)​ ​p​r​i​n​t​f​ ​(​"​%​l​l​u​\​n​"​,​ ​s​i​z​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​e​l​s​e​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​l​l​u​K​\​n​"​,​ ​s​i​z​e​ ​/​ ​1​0​2​4​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​t​o​t​a​l​S​i​z​e​ ​+​=​ ​s​i​z​e​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​n​T​o​t​a​l​ ​S​i​z​e​:​ ​%​l​l​u​M​\​n​"​,​ ​t​o​t​a​l​S​i​z​e​ ​/​ ​1​0​2​4​ ​/​ ​1​0​2​4​)​;​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​/​/​ ​m​a​i​n​

And a sample run:

$​ ​.​/​u​r​l​e​n​u​m​e​r​a​t​o​r​ ​/​u​s​r​/​i​n​c​l​u​d​e​
_​l​o​c​a​l​e​.​h​ ​2​K​
_​s​t​r​u​c​t​s​.​h​ ​1​0​0​7​
_​t​y​p​e​s​.​h​ ​2​K​
_​w​c​t​y​p​e​.​h​ ​4​K​
_​x​l​o​c​a​l​e​.​h​ ​1​K​
a​i​o​.​h​ ​1​K​
a​l​l​o​c​a​.​h​ ​1​K​
a​p​a​c​h​e​2​
 ​ ​a​p​_​c​o​m​p​a​t​.​h​ ​1​K​
 ​ ​a​p​_​c​o​n​f​i​g​.​h​ ​9​K​
 ​ ​a​p​_​c​o​n​f​i​g​_​a​u​t​o​.​h​ ​7​K​
.​.​.​
x​l​o​c​a​l​e​.​h​ ​2​K​
X​p​l​u​g​i​n​.​h​ ​2​0​K​
z​c​o​n​f​.​h​ ​9​K​
z​l​i​b​.​h​ ​6​4​K​

T​o​t​a​l​ ​S​i​z​e​:​ ​2​8​M​

File References and Bookmarks

One problem with paths and regular file URLs is that they are fragile in the face of changes to the file system. You can create a path or URL that points to a file, and the user could move that file in the Finder behind your back. If you then try to get metadata or open the file, you could fail because the file is no longer living where you think it is living.

File references

A special kind of URL, called a file reference URL, tracks a file by its ID. This means the reference is still good even if the name or location of the file changes in the file system. Usually this is hidden behind classes like NSDocument, but it can be useful in other contexts aside from document files.

Get a file reference URL from a regular one by calling -fileReferenceURL:

- (NSURL *) fileReference;

And you can query the URL to see if it is a file reference:

- (BOOL) isFileReferenceURL;

The representations of the different kinds of URLs are different: a regular file URL, when NSLogged, looks like

f​i​l​e​:​/​/​l​o​c​a​l​h​o​s​t​/​U​s​e​r​s​/​m​a​r​k​d​/​t​r​a​c​k​.​k​m​l​

while a file reference URL looks like

f​i​l​e​:​/​/​/​.​f​i​l​e​/​i​d​=​6​5​7​1​3​6​7​.​4​3​0​6​2​8​8​

Because a file reference URL contains a file ID, the URL must actually point to a file. If you have a dangling file URL that points to a non-existent file, -fileReference will return nil.

Example 13.5 shows a regular and file reference URL pointing to the same file in /tmp and prints them. The program then renames the file via the mv command and prints them again.

Example 13.5. filerefurl.m

/​/​ ​f​i​l​e​r​e​f​u​r​l​.​m​ ​-​-​ ​P​l​a​y​ ​w​i​t​h​ ​f​i​l​e​ ​r​e​f​e​r​e​n​c​e​ ​u​r​l​s​.​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​o​ ​f​i​l​e​r​e​f​u​r​l​ ​f​i​l​e​r​e​f​u​r​l​.​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​t​h​e​ ​f​i​l​e​.​
 ​ ​ ​ ​s​y​s​t​e​m​ ​(​"​t​o​u​c​h​ ​/​t​m​p​/​o​o​p​a​c​k​"​)​;​
 ​ ​ ​ ​N​S​U​R​L​ ​*​u​r​l​ ​=​ ​[​N​S​U​R​L​ ​f​i​l​e​U​R​L​W​i​t​h​P​a​t​h​:​ ​@​"​/​t​m​p​/​o​o​p​a​c​k​"​]​;​
 ​ ​ ​ ​N​S​U​R​L​ ​*​f​i​l​e​R​e​f​e​r​e​n​c​e​ ​=​ ​[​u​r​l​ ​f​i​l​e​R​e​f​e​r​e​n​c​e​U​R​L​]​;​

 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​u​r​l​:​ ​%​@​ ​:​ ​%​@​"​,​ ​u​r​l​,​ ​u​r​l​.​p​a​t​h​)​;​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​r​e​f​:​ ​%​@​ ​:​ ​%​@​"​,​ ​f​i​l​e​R​e​f​e​r​e​n​c​e​,​ ​f​i​l​e​R​e​f​e​r​e​n​c​e​.​p​a​t​h​)​;​

 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​m​o​v​i​n​g​ ​t​h​e​ ​f​i​l​e​"​)​;​
 ​ ​ ​ ​s​y​s​t​e​m​ ​(​"​m​v​ ​/​t​m​p​/​o​o​p​a​c​k​ ​/​t​m​p​/​j​o​2​y​f​u​n​d​"​)​;​

 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​u​r​l​:​ ​%​@​ ​:​ ​%​@​"​,​ ​u​r​l​,​ ​u​r​l​.​p​a​t​h​)​;​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​r​e​f​:​ ​%​@​ ​:​ ​%​@​"​,​ ​f​i​l​e​R​e​f​e​r​e​n​c​e​,​ ​f​i​l​e​R​e​f​e​r​e​n​c​e​.​p​a​t​h​)​;​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

And a sample run:

$​ ​.​/​f​i​l​e​r​e​f​u​r​l​
u​r​l​:​ ​f​i​l​e​:​/​/​l​o​c​a​l​h​o​s​t​/​t​m​p​/​o​o​p​a​c​k​ ​:​ ​/​t​m​p​/​o​o​p​a​c​k​
r​e​f​:​ ​f​i​l​e​:​/​/​/​.​f​i​l​e​/​i​d​=​6​5​7​1​3​6​7​.​7​0​8​3​2​0​1​ ​:​ ​/​p​r​i​v​a​t​e​/​t​m​p​/​o​o​p​a​c​k​
m​o​v​i​n​g​ ​t​h​e​ ​f​i​l​e​
u​r​l​:​ ​f​i​l​e​:​/​/​l​o​c​a​l​h​o​s​t​/​t​m​p​/​o​o​p​a​c​k​ ​:​ ​/​t​m​p​/​o​o​p​a​c​k​
r​e​f​:​ ​f​i​l​e​:​/​/​/​.​f​i​l​e​/​i​d​=​6​5​7​1​3​6​7​.​7​0​8​3​2​0​1​ ​:​ ​/​p​r​i​v​a​t​e​/​t​m​p​/​j​o​2​y​f​u​n​d​

You can see how the original URL has not changed. It is pointing to /tmp/oopack, which no longer exists. The file reference URL tracks the move and shows you how the path changed.

When it comes time to actually use the path, like to read from it or write to it, you can get the path property and pass that to calls that want a path. A number of Cocoa convenience functions have grown URL-based API over the years and can consume the URL directly, such as NSData’s reading and writing to URL:

- (BOOL) writeToURL: (NSURL *) url  atomically: (BOOL) atomically;

+ (id) dataWithContentsOfURL: (NSURL *) url;

This avoids the race condition you would have if you got the path from the URL and then used that path to read or write the file. It is possible the file has moved between those two calls. Of course, there is not a guarantee as of this writing that Cocoa is not doing the same thing and is subject to the same race condition.

Bookmarks

Even though file reference URLs solve once class of problems, they still have one limitation. The value they use to track files does not necessarily survive reboots. Therefore, you should not store file reference URLs to disk, say as part of your document’s object graph, and retrieve them later. If file reference URLs won’t work, and regular URLs are fragile in the face of files moving, what can you do? You can use bookmarks, which are Cocoa’s equivalent to Carbon’s aliases.

Aliases, such as the ones you can create in the Finder, are a kind of sticky symbolic link. You can make an alias to a file, and the system will try its best to follow that file as it moves around from directory to directory and disk to disk. There is Carbon API for creating and resolving aliases, and if you must support OS X prior to 10.6, you will need to use that API, not discussed here. You can find open-source libraries that put a Cocoa wrapper around aliases.

Bookmarks are a serialized version of a file reference URL (along with some extra metadata to help identify the file) that can be saved to disk. If you save the bookmark in a particular way, it can be used as an alias by the Finder and Carbon code.

Create the bookmark data with this NSURL method:

- (NSData *) bookmarkDataWithOptions: (NSURLBookmarkCreationOptions) options
      includingResourceValuesForKeys: (NSArray *) keys
                       relativeToURL: (NSURL *) relativeURL
                               error: (NSError **) error;

The bookmark creation options are bitflags that can be combined:

N​S​U​R​L​B​o​o​k​m​a​r​k​C​r​e​a​t​i​o​n​P​r​e​f​e​r​F​i​l​e​I​D​R​e​s​o​l​u​t​i​o​n​
N​S​U​R​L​B​o​o​k​m​a​r​k​C​r​e​a​t​i​o​n​M​i​n​i​m​a​l​B​o​o​k​m​a​r​k​
N​S​U​R​L​B​o​o​k​m​a​r​k​C​r​e​a​t​i​o​n​S​u​i​t​a​b​l​e​F​o​r​B​o​o​k​m​a​r​k​F​i​l​e​

PreferFileIDResolution makes the alias created prefer resolving with its embedded file ID. MinimalBookmark will minimize the data but still be resolvable. And SuitableForBookmarkFile is required to create Finder alias files.

You can specify some resource value keys to store values in the bookmark. This means you can query the data without fully resolving the bookmark or alias. The relativeURL is the URL the bookmark data will be relative to.

Once you have the NSData, you can write it to the file system. You can create a file reference URL from bookmark data by using this NSURL class method:

+ (id) URLByResolvingBookmarkData: (NSData *) bookmarkData
                          options: (NSURLBookmarkResolutionOptions) options
                    relativeToURL: (NSURL *) relativeURL
              bookmarkDataIsStale: (BOOL *) isStale
                            error: (NSError **) error;

The bookmark resolution options can be zero, for default behavior, or a bitwise-OR of these flags:

N​S​U​R​L​B​o​o​k​m​a​r​k​R​e​s​o​l​u​t​i​o​n​W​i​t​h​o​u​t​U​I​
N​S​U​R​L​B​o​o​k​m​a​r​k​R​e​s​o​l​u​t​i​o​n​W​i​t​h​o​u​t​M​o​u​n​t​i​n​g​

Sometimes there could be UI involved with resolving an alias. Aliases, and hence bookmarks, could cause a shared disk to be mounted. This can be very convenient for the user, but it could take an arbitrarily long period of time and could fail entirely.

Make a File Browser

In this chapter, you are going to create a simple file browser using NSFileManager and NSTreeController, as shown in Figure 13.3

Figure 13.3  The file browser

Create a new "Cocoa Application" project called Remover. Besides browsing, your app will allow the user to delete files and directories.

To represent the information about one directory entry, which may be a file or a directory, you will create a class called DirEntry. You will also be adding an NSTreeController, and the RemoverAppDelegate has already been created by the Xcode template. The class diagram is shown in Figure 13.4

Figure 13.4  Class diagram for Remover app

Create the DirEntry class

The file system is essentially a tree; each node of the tree is a directory or a file. You need to create a class to hold onto the information about one node, so create a class called DirEntry.

Each instance of DirEntry will know its parent and its file URL. If it’s a directory, it will know how to create an array containing DirEntrys representing its children. So, make DirEntry.h look like this:

Example 13.6. DirEntry.h

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​D​i​r​E​n​t​r​y​ ​:​ ​N​S​O​b​j​e​c​t​ ​{​
 ​ ​ ​ ​N​S​U​R​L​ ​*​_​f​i​l​e​U​R​L​;​
 ​ ​ ​ ​D​i​r​E​n​t​r​y​ ​*​_​p​a​r​e​n​t​;​
}​

+​ ​(​N​S​A​r​r​a​y​ ​*​)​ ​e​n​t​r​i​e​s​A​t​U​R​L​:​ ​(​N​S​U​R​L​ ​*​)​ ​u​r​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​t​h​P​a​r​e​n​t​:​ ​(​D​i​r​E​n​t​r​y​ ​*​)​ ​p​a​r​e​n​t​;​

@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​a​d​o​n​l​y​,​ ​r​e​t​a​i​n​)​ ​N​S​U​R​L​ ​*​f​i​l​e​U​R​L​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​a​d​o​n​l​y​,​ ​a​s​s​i​g​n​)​ ​D​i​r​E​n​t​r​y​ ​*​p​a​r​e​n​t​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​a​d​o​n​l​y​,​ ​r​e​t​a​i​n​)​ ​N​S​A​r​r​a​y​ ​*​c​h​i​l​d​r​e​n​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​a​d​o​n​l​y​,​ ​c​o​p​y​)​ ​N​S​S​t​r​i​n​g​ ​*​f​u​l​l​P​a​t​h​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​a​d​o​n​l​y​,​ ​c​o​p​y​)​ ​N​S​S​t​r​i​n​g​ ​*​f​i​l​e​n​a​m​e​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​a​d​o​n​l​y​,​ ​a​s​s​i​g​n​)​ ​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​l​o​n​g​ ​f​i​l​e​s​i​z​e​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​a​d​o​n​l​y​,​ ​a​s​s​i​g​n​)​ ​B​O​O​L​ ​i​s​D​i​r​e​c​t​o​r​y​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​a​d​o​n​l​y​,​ ​a​s​s​i​g​n​)​ ​B​O​O​L​ ​i​s​L​e​a​f​;​

@​e​n​d​ ​/​/​ ​D​i​r​E​n​t​r​y​

Now to implement these methods:

Example 13.7. DirEntry.m

#​i​m​p​o​r​t​ ​"​D​i​r​E​n​t​r​y​.​h​"​

@​i​n​t​e​r​f​a​c​e​ ​D​i​r​E​n​t​r​y​ ​(​)​
/​/​ ​L​e​t​ ​u​s​ ​s​e​t​ ​a​n​d​ ​g​e​t​ ​t​h​e​ ​p​a​r​e​n​t​,​ ​b​u​t​ ​i​t​'​s​ ​n​o​t​ ​r​e​a​l​l​y​ ​p​a​r​t​ ​o​f​ ​t​h​e​ ​p​u​b​l​i​c​ ​A​P​I​.​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​a​s​s​i​g​n​)​ ​D​i​r​E​n​t​r​y​ ​*​p​a​r​e​n​t​;​
@​e​n​d​ ​/​/​ ​D​i​r​E​n​t​r​y​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​D​i​r​E​n​t​r​y​

@​s​y​n​t​h​e​s​i​z​e​ ​f​i​l​e​U​R​L​ ​=​ ​_​f​i​l​e​U​R​L​;​
@​s​y​n​t​h​e​s​i​z​e​ ​p​a​r​e​n​t​ ​=​ ​_​p​a​r​e​n​t​;​

/​/​ ​T​h​e​ ​d​e​f​a​u​l​t​ ​s​e​t​ ​o​f​ ​a​t​t​r​i​b​u​t​e​s​ ​t​o​ ​p​r​e​-​f​e​t​c​h​.​
+​ ​(​N​S​A​r​r​a​y​ ​*​)​ ​a​t​t​r​i​b​u​t​e​K​e​y​s​ ​{​
 ​ ​ ​ ​s​t​a​t​i​c​ ​N​S​A​r​r​a​y​ ​*​s​_​k​e​y​s​;​
 ​ ​ ​ ​i​f​ ​(​s​_​k​e​y​s​ ​=​=​ ​n​i​l​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​_​k​e​y​s​ ​=​ ​[​[​N​S​A​r​r​a​y​ ​a​r​r​a​y​W​i​t​h​O​b​j​e​c​t​s​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​N​a​m​e​K​e​y​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​F​i​l​e​S​i​z​e​K​e​y​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​I​s​D​i​r​e​c​t​o​r​y​K​e​y​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​i​l​]​ ​r​e​t​a​i​n​]​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​_​k​e​y​s​;​
}​ ​/​/​ ​a​t​t​r​i​b​u​t​e​K​e​y​s​

#​p​r​a​g​m​a​ ​m​a​r​k​ ​C​r​e​a​t​i​o​n​ ​a​n​d​ ​I​n​i​t​i​a​l​i​z​a​t​i​o​n​

-​ ​(​i​d​)​ ​i​n​i​t​W​i​t​h​F​i​l​e​U​R​L​:​ ​(​N​S​U​R​L​ ​*​)​ ​f​i​l​e​U​R​L​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​a​r​e​n​t​:​ ​(​D​i​r​E​n​t​r​y​ ​*​)​ ​p​a​r​e​n​t​ ​{​
 ​ ​ ​ ​i​f​ ​(​(​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​_​f​i​l​e​U​R​L​ ​=​ ​[​f​i​l​e​U​R​L​ ​r​e​t​a​i​n​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​p​a​r​e​n​t​ ​=​ ​p​a​r​e​n​t​;​ ​ ​/​/​ ​D​o​n​'​t​ ​r​e​t​a​i​n​ ​p​a​r​e​n​t​,​ ​c​a​u​s​e​s​ ​r​e​t​a​i​n​ ​c​y​c​l​e​s​.​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​ ​/​/​ ​i​n​i​t​W​i​t​h​F​i​l​e​n​a​m​e​

+​ ​(​N​S​A​r​r​a​y​ ​*​)​ ​e​n​t​r​i​e​s​A​t​U​R​L​:​ ​(​N​S​U​R​L​ ​*​)​ ​u​r​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​t​h​P​a​r​e​n​t​:​ ​(​D​i​r​E​n​t​r​y​ ​*​)​ ​p​a​r​e​n​t​ ​{​
 ​ ​ ​ ​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​*​r​e​s​u​l​t​ ​=​ ​[​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​a​r​r​a​y​]​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​U​R​L​s​ ​f​o​r​ ​t​h​e​ ​c​o​n​t​e​n​t​s​ ​o​f​ ​t​h​e​ ​g​i​v​e​n​ ​d​i​r​e​c​t​o​r​y​.​
 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​a​t​t​r​i​b​u​t​e​K​e​y​s​ ​=​ ​[​s​e​l​f​ ​a​t​t​r​i​b​u​t​e​K​e​y​s​]​;​
 ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​;​
 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​f​i​l​e​U​R​L​s​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​N​S​F​i​l​e​M​a​n​a​g​e​r​ ​d​e​f​a​u​l​t​M​a​n​a​g​e​r​]​ ​c​o​n​t​e​n​t​s​O​f​D​i​r​e​c​t​o​r​y​A​t​U​R​L​:​ ​u​r​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​c​l​u​d​i​n​g​P​r​o​p​e​r​t​i​e​s​F​o​r​K​e​y​s​:​ ​a​t​t​r​i​b​u​t​e​K​e​y​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​p​t​i​o​n​s​:​ ​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​:​ ​&​e​r​r​o​r​]​;​
 ​ ​ ​ ​i​f​ ​(​f​i​l​e​U​R​L​s​ ​=​=​ ​n​i​l​)​ ​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​R​u​n​A​l​e​r​t​P​a​n​e​l​(​@​"​R​e​a​d​ ​f​a​i​l​e​d​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​"​U​n​a​b​l​e​ ​t​o​ ​r​e​a​d​ ​\​'​%​@​\​'​ ​w​i​t​h​ ​e​r​r​o​r​ ​%​@​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​i​l​,​ ​n​i​l​,​ ​n​i​l​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​r​l​,​ ​e​r​r​o​r​)​ ​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​r​e​s​u​l​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​D​i​r​E​n​t​r​i​e​s​ ​f​o​r​ ​e​a​c​h​ ​o​f​ ​r​e​s​u​l​t​i​n​g​ ​U​R​L​s​
 ​ ​ ​ ​[​f​i​l​e​U​R​L​s​ ​e​n​u​m​e​r​a​t​e​O​b​j​e​c​t​s​U​s​i​n​g​B​l​o​c​k​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​^​(​i​d​ ​f​i​l​e​U​R​L​,​ ​N​S​U​I​n​t​e​g​e​r​ ​i​n​d​e​x​,​ ​B​O​O​L​ ​*​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​D​i​r​E​n​t​r​y​ ​*​n​e​w​E​n​t​r​y​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​[​D​i​r​E​n​t​r​y​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​F​i​l​e​U​R​L​:​ ​f​i​l​e​U​R​L​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​a​r​e​n​t​:​ ​p​a​r​e​n​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​r​e​s​u​l​t​ ​a​d​d​O​b​j​e​c​t​:​ ​n​e​w​E​n​t​r​y​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​n​e​w​E​n​t​r​y​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​[​[​r​e​s​u​l​t​ ​c​o​p​y​]​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​ ​ ​/​/​ ​s​t​r​i​p​ ​o​f​f​ ​m​u​t​a​b​i​l​i​t​y​

}​ ​/​/​ ​e​n​t​r​i​e​s​A​t​U​R​L​

-​ ​(​v​o​i​d​)​ ​d​e​a​l​l​o​c​ ​{​
 ​ ​ ​ ​[​_​f​i​l​e​U​R​L​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​s​u​p​e​r​ ​d​e​a​l​l​o​c​]​;​
}​ ​/​/​ ​d​e​a​l​l​o​c​

#​p​r​a​g​m​a​ ​m​a​r​k​ ​F​i​l​e​ ​i​n​f​o​

-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​f​u​l​l​P​a​t​h​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​s​e​l​f​.​f​i​l​e​U​R​L​ ​p​a​t​h​]​;​
}​ ​/​/​ ​f​u​l​l​P​a​t​h​

-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​f​i​l​e​n​a​m​e​ ​{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​f​i​l​e​n​a​m​e​;​
 ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​;​
 ​ ​ ​ ​i​f​ ​(​[​s​e​l​f​.​f​i​l​e​U​R​L​ ​g​e​t​R​e​s​o​u​r​c​e​V​a​l​u​e​:​ ​&​f​i​l​e​n​a​m​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​r​K​e​y​:​ ​N​S​U​R​L​N​a​m​e​K​e​y​ ​ ​e​r​r​o​r​:​ ​&​e​r​r​o​r​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​f​i​l​e​n​a​m​e​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​R​u​n​A​l​e​r​t​P​a​n​e​l​ ​(​@​"​A​t​t​r​i​b​u​t​e​s​ ​f​a​i​l​e​d​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​"​U​n​a​b​l​e​ ​t​o​ ​g​e​t​ ​f​i​l​e​ ​n​a​m​e​ ​f​o​r​ ​\​'​%​@​\​'​ ​w​i​t​h​ ​e​r​r​o​r​ ​%​@​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​i​l​,​ ​n​i​l​,​ ​n​i​l​,​ ​s​e​l​f​.​f​i​l​e​U​R​L​,​ ​e​r​r​o​r​)​ ​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​n​i​l​;​
 ​ ​ ​ ​}​

}​ ​/​/​ ​f​i​l​e​n​a​m​e​

-​ ​(​B​O​O​L​)​ ​i​s​D​i​r​e​c​t​o​r​y​ ​{​
 ​ ​ ​ ​N​S​N​u​m​b​e​r​ ​*​i​s​D​i​r​e​c​t​o​r​y​;​
 ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​;​
 ​ ​ ​ ​i​f​ ​(​[​s​e​l​f​.​f​i​l​e​U​R​L​ ​g​e​t​R​e​s​o​u​r​c​e​V​a​l​u​e​:​ ​&​i​s​D​i​r​e​c​t​o​r​y​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​r​K​e​y​:​ ​N​S​U​R​L​I​s​D​i​r​e​c​t​o​r​y​K​e​y​ ​ ​e​r​r​o​r​:​ ​&​e​r​r​o​r​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​[​i​s​D​i​r​e​c​t​o​r​y​ ​b​o​o​l​V​a​l​u​e​]​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​R​u​n​A​l​e​r​t​P​a​n​e​l​ ​(​@​"​A​t​t​r​i​b​u​t​e​s​ ​f​a​i​l​e​d​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​"​U​n​a​b​l​e​ ​t​o​ ​g​e​t​ ​i​s​D​i​r​e​c​t​o​r​y​ ​f​o​r​ ​\​'​%​@​\​'​ ​w​i​t​h​ ​e​r​r​o​r​ ​%​@​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​i​l​,​ ​n​i​l​,​ ​n​i​l​,​ ​s​e​l​f​.​f​i​l​e​U​R​L​,​ ​e​r​r​o​r​)​ ​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​N​O​;​
 ​ ​ ​ ​}​

}​ ​/​/​ ​i​s​D​i​r​e​c​t​o​r​y​

-​ ​(​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​l​o​n​g​)​ ​f​i​l​e​s​i​z​e​ ​{​
 ​ ​ ​ ​N​S​N​u​m​b​e​r​ ​*​f​i​l​e​s​i​z​e​;​
 ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​;​
 ​ ​ ​ ​i​f​ ​(​[​s​e​l​f​.​f​i​l​e​U​R​L​ ​g​e​t​R​e​s​o​u​r​c​e​V​a​l​u​e​:​ ​&​f​i​l​e​s​i​z​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​r​K​e​y​:​ ​N​S​U​R​L​F​i​l​e​S​i​z​e​K​e​y​ ​ ​e​r​r​o​r​:​ ​&​e​r​r​o​r​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​[​f​i​l​e​s​i​z​e​ ​l​o​n​g​L​o​n​g​V​a​l​u​e​]​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​R​u​n​A​l​e​r​t​P​a​n​e​l​ ​(​@​"​A​t​t​r​i​b​u​t​e​s​ ​f​a​i​l​e​d​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​"​U​n​a​b​l​e​ ​t​o​ ​g​e​t​ ​f​i​l​e​ ​s​i​z​e​ ​f​o​r​ ​\​'​%​@​\​'​ ​w​i​t​h​ ​e​r​r​o​r​ ​%​@​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​i​l​,​ ​n​i​l​,​ ​n​i​l​,​ ​s​e​l​f​.​f​i​l​e​U​R​L​,​ ​e​r​r​o​r​)​ ​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​f​i​l​e​s​i​z​e​

#​p​r​a​g​m​a​ ​m​a​r​k​ ​F​o​r​ ​u​s​e​ ​i​n​ ​b​i​n​d​i​n​g​s​

-​ ​(​B​O​O​L​)​ ​i​s​L​e​a​f​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​!​s​e​l​f​.​i​s​D​i​r​e​c​t​o​r​y​;​
}​ ​/​/​ ​i​s​L​e​a​f​

-​ ​(​N​S​A​r​r​a​y​ ​*​)​ ​c​h​i​l​d​r​e​n​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​D​i​r​E​n​t​r​y​ ​e​n​t​r​i​e​s​A​t​U​R​L​:​ ​s​e​l​f​.​f​i​l​e​U​R​L​ ​ ​w​i​t​h​P​a​r​e​n​t​:​ ​s​e​l​f​]​;​
}​ ​/​/​ ​c​h​i​l​d​r​e​n​

@​e​n​d​ ​/​/​ ​D​i​r​E​n​t​r​y​

Edit the nib file adding NSTreeController

The NSTreeController is for displaying hierarchical data in an NSBrowser or an NSOutlineView. Besides setting the contentArray as you would for an NSArrayController, you also define a childrenKeyPath and a leafKeyPath.

In our case, the NSTreeController will be displaying DirEntry objects. Note that the DirEntry objects have a -children method and an -isLeaf method. These will be our childrenKeyPath and our leafKeyPath respectively.

Edit RemoverAppDelegate and add an action called -deleteSelection: and one outlet of type NSTreeController called treeController. Also, add a topLevelDirectories NSArray.

#​i​m​p​o​r​t​ ​<​C​o​c​o​a​/​C​o​c​o​a​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​R​e​m​o​v​e​r​A​p​p​D​e​l​e​g​a​t​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​<​N​S​A​p​p​l​i​c​a​t​i​o​n​D​e​l​e​g​a​t​e​>​ ​{​
 ​ ​ ​ ​N​S​W​i​n​d​o​w​ ​*​w​i​n​d​o​w​;​
 ​ ​ ​ ​N​S​T​r​e​e​C​o​n​t​r​o​l​l​e​r​ ​*​_​t​r​e​e​C​o​n​t​r​o​l​l​e​r​;​
 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​_​t​o​p​L​e​v​e​l​D​i​r​e​c​t​o​r​i​e​s​;​
}​

@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​W​i​n​d​o​w​ ​*​w​i​n​d​o​w​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​T​r​e​e​C​o​n​t​r​o​l​l​e​r​ ​*​t​r​e​e​C​o​n​t​r​o​l​l​e​r​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​a​d​o​n​l​y​,​ ​r​e​t​a​i​n​)​ ​N​S​A​r​r​a​y​ ​*​t​o​p​L​e​v​e​l​D​i​r​e​c​t​o​r​i​e​s​;​

-​ ​(​I​B​A​c​t​i​o​n​)​ ​d​e​l​e​t​e​S​e​l​e​c​t​i​o​n​:​ ​(​i​d​)​ ​s​e​n​d​e​r​;​

@​e​n​d​ ​/​/​ ​R​e​m​o​v​e​r​A​p​p​D​e​l​e​g​a​t​e​

Open the MainMenu.xib file. To instantiate a tree controller, drag one out the objects library. Make it non-editable and allow the empty selection. Set the children key path to be children and set the leaf key path to be isLeaf. The objects being displayed are DirEntry objects. They have a filename attribute and a filesize attribute.

Figure 13.5  Configuring the tree controller's bindings

Drop an NSBrowser and an NSButton on the window. In the inspector, set the NSBrowser so that it does not display titles. Make RemoverAppDelegate the target of the button and set the action to be -deleteSelection:.

Set the autosizing for the NSBrowser to grow with the window and for the NSButton to remain in place. You might want to change the button’s title to “Delete” at this time.

Connect the treeController outlet of RemoverAppDelegate to point to the NSTreeController.

Bind the contentArray of the tree controller to the topLevelDirectories attribute of the RemoverAppDelegate. (Note that you will actually have to type “topLevelDirectories” into the combo box.)

Figure 13.6  Binding the tree controller's contentArray

Select the browser and view its bindings. Bind the content to the tree controller’s arrangedObjects and bind the contentValues to the tree controller’s arrangedObjects’ fileName.

Figure 13.7  Binding the browser's arrangedObjects

Bind the selectionIndexPaths to the tree controller’s selectionIndexPaths.

Figure 13.8  Binding the browser's selection index paths

For fun, let’s also show the size of the selected file. Drop a Label onto the window. Make sure the label is wide enough to hold a large number and set the autosizing so that the label stays in the bottom-left corner. Next drop a number formatter on top of the label. Select the Mac OS X 10.4+ Custom behavior if it is not already selected.

The size is a number of bytes, so give it a Format (+) of “# bytes.” Check the Grouping Separator checkbox and increase the Primary Grouping to 3 and leave the Secondary Grouping at zero. Uncheck Allows Floats because files cannot contain fractional bytes.

Figure 13.9  Configuring the label's number formatter

Bind the value of the text field to the tree controller’s selection’s fileSize.

Figure 13.10  Binding the label's contents.

Using NSBrowser and DirEntry

In the RemoverAppDelegate.m file, implement these methods:

Example 13.8. RemoverAppDelegate.m

#​i​m​p​o​r​t​ ​"​R​e​m​o​v​e​r​A​p​p​D​e​l​e​g​a​t​e​.​h​"​
#​i​m​p​o​r​t​ ​"​D​i​r​E​n​t​r​y​.​h​"​

@​i​n​t​e​r​f​a​c​e​ ​R​e​m​o​v​e​r​A​p​p​D​e​l​e​g​a​t​e​ ​(​)​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​a​d​w​r​i​t​e​,​ ​r​e​t​a​i​n​)​ ​N​S​A​r​r​a​y​ ​*​t​o​p​L​e​v​e​l​D​i​r​e​c​t​o​r​i​e​s​;​
@​e​n​d​ ​/​/​ ​R​e​m​o​v​e​A​p​p​D​e​l​e​g​a​t​e​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​R​e​m​o​v​e​r​A​p​p​D​e​l​e​g​a​t​e​

@​s​y​n​t​h​e​s​i​z​e​ ​w​i​n​d​o​w​;​
@​s​y​n​t​h​e​s​i​z​e​ ​t​o​p​L​e​v​e​l​D​i​r​e​c​t​o​r​i​e​s​ ​=​ ​_​t​o​p​L​e​v​e​l​D​i​r​e​c​t​o​r​i​e​s​;​
@​s​y​n​t​h​e​s​i​z​e​ ​t​r​e​e​C​o​n​t​r​o​l​l​e​r​ ​=​ ​_​t​r​e​e​C​o​n​t​r​o​l​l​e​r​;​

-​ ​(​i​d​)​ ​i​n​i​t​ ​{​
 ​ ​ ​ ​i​f​ ​(​(​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​S​e​e​d​ ​t​h​e​ ​d​i​r​e​c​t​o​r​i​e​s​ ​t​o​ ​d​i​s​p​l​a​y​.​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​ ​*​r​o​o​t​U​R​L​ ​=​ ​[​N​S​U​R​L​ ​f​i​l​e​U​R​L​W​i​t​h​P​a​t​h​:​ ​@​"​/​"​ ​ ​i​s​D​i​r​e​c​t​o​r​y​:​ ​Y​E​S​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​t​o​p​L​e​v​e​l​D​i​r​e​c​t​o​r​i​e​s​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​D​i​r​E​n​t​r​y​ ​e​n​t​r​i​e​s​A​t​U​R​L​:​ ​r​o​o​t​U​R​L​ ​ ​w​i​t​h​P​a​r​e​n​t​:​ ​n​i​l​]​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​ ​/​/​ ​i​n​i​t​

-​ ​(​v​o​i​d​)​ ​d​e​a​l​l​o​c​ ​{​
 ​ ​ ​ ​[​_​t​o​p​L​e​v​e​l​D​i​r​e​c​t​o​r​i​e​s​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​s​u​p​e​r​ ​d​e​a​l​l​o​c​]​;​
}​ ​/​/​ ​d​e​a​l​l​o​c​

-​ ​(​I​B​A​c​t​i​o​n​)​ ​d​e​l​e​t​e​S​e​l​e​c​t​i​o​n​:​ ​(​i​d​)​ ​s​e​n​d​e​r​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​%​s​ ​t​o​ ​b​e​ ​i​m​p​l​e​m​e​n​t​e​d​"​,​ ​_​_​F​U​N​C​T​I​O​N​_​_​)​;​
}​ ​/​/​ ​d​e​l​e​t​e​S​e​l​e​c​t​i​o​n​

@​e​n​d​ ​/​/​ ​R​e​m​o​v​e​r​A​p​p​D​e​l​e​g​a​t​e​

Build and run the app. You should be able to browse but not delete.

Adding deletion

Replace the -deleteSelection: method in RemoverAppDelegate to enable deletion:

Example 13.9. -deleteSelection:

-​ ​(​I​B​A​c​t​i​o​n​)​ ​d​e​l​e​t​e​S​e​l​e​c​t​i​o​n​:​ ​(​i​d​)​ ​s​e​n​d​e​r​ ​{​
 ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​s​e​l​e​c​t​i​o​n​
 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​s​e​l​e​c​t​i​o​n​ ​=​ ​[​[​[​_​t​r​e​e​C​o​n​t​r​o​l​l​e​r​ ​s​e​l​e​c​t​e​d​O​b​j​e​c​t​s​]​ ​r​e​t​a​i​n​]​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​N​S​U​I​n​t​e​g​e​r​ ​c​o​u​n​t​ ​=​ ​[​s​e​l​e​c​t​i​o​n​ ​c​o​u​n​t​]​;​

 ​ ​ ​ ​/​/​ ​B​a​i​l​ ​i​f​ ​t​h​e​r​e​'​s​ ​a​n​ ​e​m​p​t​y​ ​s​e​l​e​c​t​i​o​n​?​
 ​ ​ ​ ​i​f​ ​(​c​o​u​n​t​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​R​u​n​A​l​e​r​t​P​a​n​e​l​ ​(​@​"​N​o​t​h​i​n​g​ ​t​o​ ​D​e​l​e​t​e​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​"​S​e​l​e​c​t​ ​a​n​ ​i​t​e​m​ ​t​o​ ​d​e​l​e​t​e​ ​a​n​d​ ​t​r​y​ ​a​g​a​i​n​.​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​i​l​,​ ​n​i​l​,​ ​n​i​l​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​N​S​F​i​l​e​M​a​n​a​g​e​r​ ​*​f​m​ ​=​ ​[​[​N​S​F​i​l​e​M​a​n​a​g​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​f​m​.​d​e​l​e​g​a​t​e​ ​=​ ​s​e​l​f​;​

 ​ ​ ​ ​[​s​e​l​e​c​t​i​o​n​ ​e​n​u​m​e​r​a​t​e​O​b​j​e​c​t​s​U​s​i​n​g​B​l​o​c​k​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​^​(​D​i​r​E​n​t​r​y​ ​*​d​i​r​E​n​t​r​y​,​ ​N​S​U​I​n​t​e​g​e​r​ ​i​n​d​e​x​,​ ​B​O​O​L​ ​*​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​ ​=​ ​d​i​r​E​n​t​r​y​.​f​u​l​l​P​a​t​h​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​t​i​t​l​e​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​ ​@​"​D​e​l​e​t​e​ ​\​"​%​@​\​"​?​"​,​ ​p​a​t​h​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​I​n​t​e​g​e​r​ ​c​h​o​i​c​e​ ​=​ ​N​S​R​u​n​A​l​e​r​t​P​a​n​e​l​ ​(​t​i​t​l​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​"​D​e​l​e​t​i​o​n​ ​c​a​n​n​o​t​ ​b​e​ ​u​n​d​o​n​e​.​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​"​D​e​l​e​t​e​"​,​ ​@​"​C​a​n​c​e​l​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​i​l​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​c​h​o​i​c​e​ ​!​=​ ​N​S​A​l​e​r​t​D​e​f​a​u​l​t​R​e​t​u​r​n​)​ ​r​e​t​u​r​n​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​S​e​n​d​ ​n​o​t​i​f​i​c​a​t​i​o​n​s​ ​t​h​a​t​ ​t​r​i​g​g​e​r​ ​K​V​O​ ​t​o​ ​u​p​d​a​t​e​ ​t​h​e​ ​b​r​o​w​s​e​r​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​B​O​O​L​ ​d​i​d​D​e​l​e​t​e​ ​=​ ​N​O​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​d​i​r​E​n​t​r​y​.​p​a​r​e​n​t​ ​w​i​l​l​C​h​a​n​g​e​V​a​l​u​e​F​o​r​K​e​y​:​ ​@​"​c​h​i​l​d​r​e​n​"​]​;​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​A​c​t​u​a​l​l​y​ ​d​e​l​e​t​e​ ​t​h​e​ ​f​i​l​e​ ​o​r​ ​d​i​r​e​c​t​o​r​y​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​d​D​e​l​e​t​e​ ​=​ ​[​f​m​ ​r​e​m​o​v​e​I​t​e​m​A​t​U​R​L​:​ ​d​i​r​E​n​t​r​y​.​f​i​l​e​U​R​L​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​:​ ​&​e​r​r​o​r​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​ ​[​d​i​r​E​n​t​r​y​.​p​a​r​e​n​t​ ​d​i​d​C​h​a​n​g​e​V​a​l​u​e​F​o​r​K​e​y​:​ ​@​"​c​h​i​l​d​r​e​n​"​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​a​s​ ​t​h​e​ ​d​e​l​e​t​i​o​n​ ​a​ ​f​a​i​l​u​r​e​?​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​!​d​i​d​D​e​l​e​t​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​[​N​S​A​p​p​l​i​c​a​t​i​o​n​ ​s​h​a​r​e​d​A​p​p​l​i​c​a​t​i​o​n​]​ ​p​r​e​s​e​n​t​E​r​r​o​r​:​ ​e​r​r​o​r​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​}​]​;​
 ​ ​ ​ ​[​f​m​ ​r​e​l​e​a​s​e​]​;​

}​ ​/​/​ ​d​e​l​e​t​e​S​e​l​e​c​t​i​o​n​

/​/​ ​N​S​F​i​l​e​M​a​n​a​g​e​r​ ​c​a​l​l​s​ ​t​h​i​s​ ​d​e​l​e​g​a​t​e​ ​m​e​t​h​o​d​ ​i​f​ ​s​o​m​e​t​h​i​n​g​ ​g​o​e​s​ ​w​r​o​n​g​.​
-​ ​(​B​O​O​L​)​ ​f​i​l​e​M​a​n​a​g​e​r​:​ ​(​N​S​F​i​l​e​M​a​n​a​g​e​r​ ​*​)​ ​m​a​n​a​g​e​r​
 ​ ​s​h​o​u​l​d​P​r​o​c​e​e​d​A​f​t​e​r​E​r​r​o​r​:​ ​(​N​S​E​r​r​o​r​ ​*​)​ ​e​r​r​o​r​
 ​ ​ ​ ​ ​ ​ ​r​e​m​o​v​i​n​g​I​t​e​m​A​t​P​a​t​h​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​p​a​t​h​ ​{​

 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​%​s​:​ ​e​r​r​o​r​ ​=​ ​%​@​"​,​ ​_​_​F​U​N​C​T​I​O​N​_​_​,​ ​e​r​r​o​r​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​N​O​;​

}​ ​/​/​ ​s​h​o​u​l​d​P​r​o​c​e​e​d​A​f​t​e​r​E​r​r​o​r​

Build and test this carefully. It will happily delete entire directories.

Figure 13.11  Remover in action

When doing the deletion, you need to tell any KVO observers of the parent DirEntry that the children have changed. Because the browser is observing the DirEntrys, you need to call the -willChangeValueForKey: and -didChangeValueForKey: methods. The added scope used here is not necessary, but it makes it very clear what methods are being called between will-change and did-change.

Lastly, you allocate the NSFileManager and use it inside of the enumeration block. This makes using the delegate of the file manager easier. Someone else may have set the delegate of the shared file manager, so you would have to save off the delegate beforehand and restore it later – and hope that someone else hasn’t changed it in the meantime. If you use your own file manager and set its delegate, NSFileManager will let us know when there are problems removing a file.

NSWorkspace

Back in the mists of time, the original NeXT-system UI was called the Workspace. These days it’s a combination of the Finder and the Dock. Cocoa’s NSWorkspace class provides features that were originally available in the NeXT workspace. NSWorkspace, actually, is a combination of Finder features as well as being a higher level API on top of Apple’s Launch Services. Launch Services is a database of applications, file types, icons, and associated data.

Because the system has one Launch Services database and the user has one Finder, you interact with NSWorkspace via a shared instance:

+ (NSWorkspace *) sharedWorkspace;

You can open files and URLs, and launch applications as if you had double-clicked them in the Finder. NSWorkspace will consult with Launch Services to figure out the proper application to run.

- (BOOL) openFile: (NSString *) fullPath;

Open the file as if it were double-clicked.

- (BOOL) openFile: (NSString *) fullPath
  withApplication: (NSString *) appName
    andDeactivate: (BOOL) deactivate;

Open the file with the given application. You can specify a path to an application or just the name. Passing YES for deactivate means that the calling application will deactivate itself so that the new application can appear in the foreground.

If you are more into URLs, you can open a file easily:

- (BOOL) openURL: (NSURL *) url;

-launchApplicationAtURL:​options:​configuration:​error: is a more sophisticated version, but it is more complex and beyond the scope of the current discussion.

You can get a file’s icon:

- (NSImage *) iconForFile: (NSString *) fullPath;

NSWorkspace also has its own notification center:

- (NSNotificationCenter *) notificationCenter;

You can register handlers for the different notifications it can post. Here is a sampling:

N​S​W​o​r​k​s​p​a​c​e​W​i​l​l​L​a​u​n​c​h​A​p​p​l​i​c​a​t​i​o​n​N​o​t​i​f​i​c​a​t​i​o​n​
N​S​W​o​r​k​s​p​a​c​e​D​i​d​L​a​u​n​c​h​A​p​p​l​i​c​a​t​i​o​n​N​o​t​i​f​i​c​a​t​i​o​n​
N​S​W​o​r​k​s​p​a​c​e​D​i​d​T​e​r​m​i​n​a​t​e​A​p​p​l​i​c​a​t​i​o​n​N​o​t​i​f​i​c​a​t​i​o​n​
N​S​W​o​r​k​s​p​a​c​e​W​i​l​l​P​o​w​e​r​O​f​f​N​o​t​i​f​i​c​a​t​i​o​n​
N​S​W​o​r​k​s​p​a​c​e​D​i​d​M​o​u​n​t​N​o​t​i​f​i​c​a​t​i​o​n​
N​S​W​o​r​k​s​p​a​c​e​S​e​s​s​i​o​n​D​i​d​B​e​c​o​m​e​A​c​t​i​v​e​N​o​t​i​f​i​c​a​t​i​o​n​
N​S​W​o​r​k​s​p​a​c​e​D​i​d​P​e​r​f​o​r​m​F​i​l​e​O​p​e​r​a​t​i​o​n​N​o​t​i​f​i​c​a​t​i​o​n​

Exercises

Use NSDirectoryEnumerator to calculate the true size of the selected directory.

Use NSWorkspace to find and display an icon for the selected file or directory in an NSImageView.

If the user double-clicks a file, open it. This also uses NSWorkspace.

Use NSWorkspace to recycle the selected item (move it to the trash) rather than deleting it.

Use DirEntry in an iPhone app to display the contents of the application’s sandbox.

Store the user’s current location in Remover and restore it on next program launch. Make sure it works after reboots.

Remover currently starts up showing the top of the file system. Change Remover so that it starts by showing all mounted volumes at the top level instead.

14
Network Programming With Sockets

The native Mac OS X networking API is Berkeley sockets, variants of which are available on just about every platform available today. The sockets API is simultaneously elegant, needing just a couple of calls to set up network communications of different kinds, and ugly. The data structures involved are awkward because there are few typedefs, so you have to use the struct keyword frequently along with frequent casting.

One of the problems with networking in general, and sockets in particular, is that there is a lot of documentation and a lot of features of varying levels of complexity and obscurity. It is easy to get lost in the details and be unable to get the basics working. In this chapter, we are going to be concerned primarily with basic stream-oriented network communication.

Addresses

Network interfaces, such as ethernet cards or airport cards, are identified by an IP (Internet Protocol) address. An IP address is a numeric value that is used as the source or destination of a network message. A typical network message would be something like, “An interface at IP address 10.0.1.123 is sending 30 bytes of data to another interface at IP 192.168.254.42.” There are two major flavors of IP addresses: IPv4 and IPv6, which are ways of saying “Internet Protocol version 4” and “Internet Protocol version 6.”

IPv4 are 32-bit addresses, the original addressing mode of the Internet. We have exhausted the 32-bit address space, meaning that there are not enough addresses available to address the current number of computers wanting to connect to the Internet. There are some workarounds, such as Network Address Translation (NAT) and local addressing, that allow multiple machines share the same public IP address, but none is a sustainable solution. The next generation of the Internet Protocol is IPv6, which uses 128-bit addresses. 128 bits can hold 340 undecillion values, where undecillion is ten raised to the 36th power. Hopefully, we will not be running out of IPv6 addresses any time soon.

IP addresses are numeric values. To make them a little easier to read, there are human-readable “presentation” formats. IPv4 addresses are usually represented in “dotted-quad” format, which is the four bytes of the address represented by decimal numbers, separated by periods. 127.0.0.2 is in dotted-quad format, as is 66.227.8.230.

IPv6 addresses are represented by sixteen-bit quantities separated by colons. FDEC:BA98:7654:3210:FEDC:BA98:7654:3210 could be an IPv6 address, as well as 1080:0000:0000:0000:0008:0800:2000C:417A. You can omit leading zeros in each group to reduce the amount of clutter, but there has to be at least one digit in every field. The previous address could be written as 1080:0:0:0:8:800:200C:417A. If there are multiple consecutive 16-bit groups of zeros, they can be elided and replaced with ::. The previous address can therefore be written 1080::8:800:200C:417A. To prevent ambiguities, there can only be one :: per address. The last 32 bits can also be written in dotted-quad format, leading to an IPv6 address like 1080::8:800:32.12.65.116.

There are two special IPv6 address formats to help IPv4 and IPv6 interoperate. The first is an IPv4-compatible IPv6 address of the form ::d.d.d.d. This format is leading zeros, followed by a 32-bit IPv4 address in dotted-quad format. It is typically used by hosts and routers to dynamically tunnel IPv6 packets over IPv4 routing.

The other is an IPv4-mapped IPv6 address, which takes the form ::FFFF:d.d.d.d. This address format is leading zeros, followed by 16 bits of ones, followed by a 32-bit IPv4 address. It is how IPv4-only nodes (those that do not support IPv6) are represented to IPv6-aware software. This format allows IPv6 systems to interoperate with IPv4-only systems.

Sockets Address Data Structures

There are a number of data structures used to represent network addresses, along with a number of functions for converting addresses between different formats.

The fundamental data structure in the sockets API is struct sockaddr:

s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​ ​{​
 ​ ​ ​ ​u​i​n​t​8​_​t​ ​ ​ ​ ​ ​s​a​_​l​e​n​;​
 ​ ​ ​ ​s​a​_​f​a​m​i​l​y​_​t​ ​s​a​_​f​a​m​i​l​y​;​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​ ​ ​ ​ ​s​a​_​d​a​t​a​[​1​4​]​;​
}​;​

struct sockaddr acts like an abstract base class in object-oriented languages. It defines two fields that are common amongst all of the different address types. By examining these fields you can see how large the address structure is by looking at sa_len, and you can see what kind of address it is (IPv4, IPv6, local address for Unix sockets, X.25, or other kinds of addresses that have not been invented yet) by looking at the sa_family field. sa_data is there to say that address-specific data follows these two common fields. That data is specified by other structures.

IPv4 address structures

The address structure for IPv4 addresses is struct sockaddr_in, where the _in suffix is short for “Internet”:

s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​{​
 ​ ​ ​ ​u​i​n​t​8​_​t​ ​ ​ ​ ​ ​ ​ ​ ​s​i​n​_​l​e​n​;​
 ​ ​ ​ ​s​a​_​f​a​m​i​l​y​_​t​ ​ ​ ​ ​s​i​n​_​f​a​m​i​l​y​;​
 ​ ​ ​ ​i​n​_​p​o​r​t​_​t​ ​ ​ ​ ​ ​ ​s​i​n​_​p​o​r​t​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​i​n​_​a​d​d​r​ ​s​i​n​_​a​d​d​r​;​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​n​_​z​e​r​o​[​8​]​;​
}​;​

The first two fields match the first two fields of struct sockaddr. Store the constant AF_INET in sin_family for IPv4 addresses. The last element, sin_zero, is padding so that struct sockaddr_in matches the size of struct sockaddr. You must always clear it to zero before passing a sockaddr_in structure to any function.

TCP/IP communication deals with addresses and ports. Every IP address has 65535 ports associated with it. Communication actually happens between address-port pairs. Communicating with the web server at www.bignerdranch.com involves going to address 209.20.82.22 and connecting to port 80. Locally, your web browser will be communicating from a port at your local IP address. The sin_port field indicates which port should be used. The IP address lives in sin_addr.

sin_addr is a struct in_addr, which is one of the annoying parts of the sockets API:

s​t​r​u​c​t​ ​i​n​_​a​d​d​r​ ​{​
 ​ ​ ​ ​i​n​_​a​d​d​r​_​t​ ​s​_​a​d​d​r​;​
}​;​

This is a structure with only a single element. The actual address type, in_addr_t, is an unsigned 32-bit integer.

There is one tricky detail: both the port (2 bytes) and the address (4 bytes) need to be in network (big-endian) byte order. The functions that simplify byte swapping to and from the network byte order are described later.

IPv6 address structures

IPv6 addresses have a new address structure, struct sockaddr_in6.

The IPv6 addresses are larger, and they have some features that IPv4 doesn’t support. struct sockaddr_in6 looks like this:

s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​6​ ​{​
 ​ ​ ​ ​u​i​n​t​8​_​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​n​6​_​l​e​n​;​
 ​ ​ ​ ​s​a​_​f​a​m​i​l​y​_​t​ ​ ​ ​ ​ ​s​i​n​6​_​f​a​m​i​l​y​;​
 ​ ​ ​ ​i​n​_​p​o​r​t​_​t​ ​ ​ ​ ​ ​ ​ ​s​i​n​6​_​p​o​r​t​;​
 ​ ​ ​ ​u​i​n​t​3​2​_​t​ ​ ​ ​ ​ ​ ​ ​ ​s​i​n​6​_​f​l​o​w​i​n​f​o​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​i​n​6​_​a​d​d​r​ ​s​i​n​6​_​a​d​d​r​;​
 ​ ​ ​ ​u​i​n​t​3​2​_​t​ ​ ​ ​ ​ ​ ​ ​ ​s​i​n​6​_​s​c​o​p​e​_​i​d​;​
}​;​

Like struct sockaddr_in, the first field is the length of the structure and the second field is the family, which should be set to AF_INET6. After that comes the port, which is just like the port in IPv4. We will not talk about sin6_flowinfo or sin6_scope_id. These are more advanced IPv6 features that are not necessary to deal with on a daily basis. One interesting thing to notice is that sockaddr_in6 uses more descriptive types than the older sockaddr_in.

sin6_addr is the actual IP address of type struct in6_addr. Like struct in_addr, struct in6_addr is a structure that holds the address:

s​t​r​u​c​t​ ​i​n​6​_​a​d​d​r​ ​{​
 ​ ​ ​ ​u​i​n​t​8​_​t​ ​s​6​_​a​d​d​r​[​1​6​]​;​
}​

If you have been following along carefully, you will notice that sockadd_in6 weighs in at 28 bytes, which will not fit into a sockaddr that is 16 bytes large. This makes it awkward to have an array of sockaddrs to hold IPv4 and IPv6 addresses. struct sockaddr_storage was introduced to alleviate this problem:

s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​s​t​o​r​a​g​e​ ​{​
 ​ ​ ​ ​u​i​n​t​8​_​t​ ​ ​ ​ ​ ​s​s​_​l​e​n​;​
 ​ ​ ​ ​s​a​_​f​a​m​i​l​y​_​t​ ​s​s​_​f​a​m​i​l​y​;​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​ ​ ​ ​ ​_​_​s​s​_​p​a​d​1​[​_​_​S​S​_​P​A​D​1​S​I​Z​E​]​;​
 ​ ​ ​ ​i​n​t​6​4​_​t​ ​ ​ ​ ​ ​_​_​s​s​_​a​l​i​g​n​;​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​ ​ ​ ​ ​_​_​s​s​_​p​a​d​2​[​_​S​S​_​P​A​D​2​S​I​Z​E​]​;​
}​

The only public fields of this structure are those it shares with all socket structures: length and family. The other fields exist purely to pad out the structure and influence its alignment in memory. sockaddr_storage is 128 bytes long, which allows some breathing room for future expansion. This means that you can declare a variable of type sockaddr_storage, cast it to any other socket structure type, and access any field of that structure without violating alignment constraints or worrying about overrunning a chunk of memory.

Network Byte Order

The way bytes are ordered in multi-byte integers can vary from platform to platform. The bytes of the integer value of 0x12345678 could be stored in memory in that order with the most significant byte occurring at a lower address. This is called big endian because the big part of the integer (the most significant byte) occurs first. The same value could also be stored in little-endian byte order as 0x78563412; here, the least significant byte comes first. The PowerPC stored its integers in big endian byte order, while Intel x86 machines store integers in little endian byte order. The two parties communicating must agree on what order the specific bytes will have when transferring integers between machines over the network. Usually you will use “network byte order,” which is defined to be big endian.

Because many Mac developers still support software on both architectures, we have to worry about network byte order, not only with regards to transferring data over a network, but also when writing binary files. It is always a good idea to store your integers in network byte order to prevent surprises later on. There are some useful functions provided that ensure that your data is in network byte order:

uint16_t htons (uint16_t hostshort);

uint32_t htonl (uint32_t hostlong);

uint16_t ntohs (uint16_t netshort);

uint32_t ntohl (uint32_t netlong);

The nomenclature uses these conventions: h stands for “host,” n for “network,” s for “short,” and l for “long.”

So, htonl() is “host to network long,” and the converse is ntohl(), “network to host long.” You do not need to worry about network byte order when sending text data or single bytes.

Short and long are used here from the standpoint of an ILP32 environment, not LP64; the functions are defined using unsigned integral types of a specific number of bits rather than using short and long themselves to avoid any further confusion as to the size of short and long.

Core Foundation provides a number of byte swapping routines as well. There are 15 functions for swapping the bytes of 16, 32, and 64 bit integers. The format of the functions is CFSwapInt + word size + direction of conversion. The word size is 16, 32, or 64. Direction of conversion can be omitted, in which case the bytes are swapped, or it can be one of BigToHost (big endian to the host’s native byte order, similar to the ntoh functions), HostToBig (native byte order to big endian, similar to the hton functions), HostToLittle, or LittleToHost. The last two convert between the host’s native byte order and little-endian byte order.

Some valid byte swapping functions would then be CFSwapInt16(), CFSwapInt32BigToHost(), CFSwapInt64HostToLittle(), and so on.

Address Conversions

There are two fundamental forms for IP addresses. The first is a human-readable “presentation” form, such as the IPv4 dotted-quad or the IPv6 colon-delimited hex address. The other is a binary format “network” form that can be used by the sockets API to actually send data across the network. The presentation address is what the user interacts with, and the network form is used by the program’s code.

IPv4- and IPv6-compatible functions

The inet_pton() function converts from an address in presentation format to one in network format, hence the “P to N” in the function name.

i​n​t​ ​i​n​e​t​_​p​t​o​n​ ​(​i​n​t​ ​a​d​d​r​e​s​s​F​a​m​i​l​y​,​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​s​o​u​r​c​e​B​u​f​f​e​r​,​ ​v​o​i​d​ ​*​d​e​s​t​i​n​a​t​i​o​n​A​d​d​r​e​s​s​)​;​

This converts from an ASCII representation to a network address. addressFamily is the family of the address being passed in and should be either AF_INET or AF_INET6. sourceBuffer is a zero-terminated ASCII string that holds the presentation address. destinationAddress should be a struct in_addr for an AF_INET address or a struct in6_addr for an AF_INET6 address.

inet_pton() returns 1 if the address was valid for the specified address family, 0 if the address was not parsable in the specified address family, or -1 if some system error occurred. Consult errno for the specific error.

The inet_ntop() converts from a network address to a presentation form.

c​o​n​s​t​ ​c​h​a​r​ ​*​i​n​e​t​_​n​t​o​p​ ​(​i​n​t​ ​a​d​d​r​e​s​s​F​a​m​i​l​y​,​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​s​o​u​r​c​e​A​d​d​r​e​s​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​*​d​e​s​t​i​n​a​t​i​o​n​B​u​f​f​e​r​,​ ​s​o​c​k​l​e​n​_​t​ ​b​u​f​f​e​r​S​i​z​e​)​;​

This converts from a network address to an ASCII representation. addressFamily is the family of the address and should be either AF_INET or AF_INET6. The sourceAddress parameter is an address in network format, such as a struct in_addr or a struct in6_addr. The destination buffer is a character buffer that you provide (you cannot pass NULL here) which will be filled in with the presentation-format address. The buffer should be at least as large as INET_ADDRSTRLEN when using the AF_INET address family or at least as large as INET6_ADDRSTRLEN when using AF_INET6. Using these constants guarantees that the buffer will be large enough to hold the largest possible presentation address.

Upon success, the inet_ntop() returns the address of your character buffer. On failure, it returns NULL, in which case you should consult errno to learn what went wrong.

Example 14.1 shows inet_pton and inet_ntop in action: an address in ASCII-presentation format is converted into a binary address and back again.

Example 14.1. ptontoa.m

/​/​ ​p​t​o​n​t​o​a​.​m​ ​-​-​ ​E​x​e​r​c​i​s​e​ ​i​n​e​t​_​p​t​o​n​ ​a​n​d​ ​i​n​e​t​_​n​t​o​p​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​p​t​o​n​t​o​a​ ​p​t​o​n​t​o​a​.​m​

#​i​m​p​o​r​t​ ​<​a​r​p​a​/​i​n​e​t​.​h​>​ ​ ​/​/​ ​f​o​r​ ​i​n​e​t​_​*​
#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​n​e​t​i​n​e​t​/​i​n​.​h​>​ ​/​/​ ​f​o​r​ ​i​n​_​a​d​d​r​ ​a​n​d​ ​i​n​6​_​a​d​d​r​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​o​c​k​e​t​.​h​>​ ​/​/​ ​f​o​r​ ​A​F​_​I​N​E​T​[​6​]​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​/​/​ ​f​o​r​ ​t​y​p​e​ ​d​e​f​i​n​i​t​i​o​n​s​,​ ​l​i​k​e​ ​u​_​c​h​a​r​

/​/​ ​C​o​n​v​e​r​t​ ​a​n​ ​I​P​v​6​ ​a​d​d​r​e​s​s​ ​t​o​ ​a​ ​s​t​r​i​n​g​.​
c​o​n​s​t​ ​c​h​a​r​ ​*​i​n​6​T​o​C​h​a​r​ ​(​s​t​r​u​c​t​ ​i​n​6​_​a​d​d​r​ ​*​a​d​d​r​)​ ​{​
 ​ ​ ​ ​s​t​a​t​i​c​ ​c​h​a​r​ ​s​_​a​d​d​r​e​s​s​[​I​N​E​T​6​_​A​D​D​R​S​T​R​L​E​N​]​;​

 ​ ​ ​ ​u​i​n​t​3​2​_​t​ ​*​b​a​s​e​ ​=​ ​(​u​i​n​t​3​2​_​t​ ​*​)​a​d​d​r​-​>​s​6​_​a​d​d​r​;​

 ​ ​ ​ ​s​n​p​r​i​n​t​f​(​s​_​a​d​d​r​e​s​s​,​ ​s​i​z​e​o​f​(​s​_​a​d​d​r​e​s​s​)​,​ ​"​%​x​%​x​%​x​%​x​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​t​o​h​l​(​b​a​s​e​[​0​]​)​,​ ​n​t​o​h​l​(​b​a​s​e​[​1​]​)​,​ ​n​t​o​h​l​(​b​a​s​e​[​2​]​)​,​ ​n​t​o​h​l​(​b​a​s​e​[​3​]​)​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​_​a​d​d​r​e​s​s​;​
}​ ​/​/​ ​i​n​6​T​o​C​h​a​r​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​/​/​ ​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
 ​ ​ ​ ​/​/​ ​I​P​v​4​

 ​ ​ ​ ​/​/​ ​C​o​n​v​e​r​t​ ​f​r​o​m​ ​p​r​e​s​e​n​t​a​t​i​o​n​ ​t​o​ ​n​u​m​e​r​i​c​,​ ​i​n​e​t​_​n​a​m​e​ ​-​>​ ​i​n​e​t​_​a​d​d​r​
 ​ ​ ​ ​c​h​a​r​ ​i​n​e​t​_​n​a​m​e​[​I​N​E​T​_​A​D​D​R​S​T​R​L​E​N​]​;​
 ​ ​ ​ ​s​t​r​n​c​p​y​ ​(​i​n​e​t​_​n​a​m​e​,​ ​"​1​9​2​.​1​6​8​.​2​5​4​.​1​2​3​"​,​ ​I​N​E​T​_​A​D​D​R​S​T​R​L​E​N​)​;​
 ​ ​ ​ ​i​n​e​t​_​n​a​m​e​[​I​N​E​T​_​A​D​D​R​S​T​R​L​E​N​ ​-​ ​1​]​ ​=​ ​'​\​0​'​;​

 ​ ​ ​ ​s​t​r​u​c​t​ ​i​n​_​a​d​d​r​ ​i​n​e​t​_​a​d​d​r​;​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​i​n​e​t​_​p​t​o​n​ ​(​A​F​_​I​N​E​T​,​ ​i​n​e​t​_​n​a​m​e​,​ ​&​i​n​e​t​_​a​d​d​r​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​(​"​a​d​d​r​e​s​s​ ​'​%​s​'​ ​i​n​ ​b​i​n​a​r​y​:​ ​%​x​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​e​t​_​n​a​m​e​,​ ​n​t​o​h​l​(​i​n​e​t​_​a​d​d​r​.​s​_​a​d​d​r​)​)​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​(​"​*​*​*​ ​a​d​d​r​e​s​s​ ​'​%​s​'​ ​n​o​t​ ​p​a​r​s​a​b​l​e​\​n​\​n​"​,​ ​i​n​e​t​_​n​a​m​e​)​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​(​"​*​*​*​ ​i​n​e​t​_​p​t​o​n​:​ ​e​r​r​o​r​ ​%​d​:​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​C​o​n​v​e​r​t​ ​f​r​o​m​ ​n​u​m​e​r​i​c​ ​t​o​ ​p​r​e​s​e​n​t​a​t​i​o​n​,​ ​i​n​e​t​_​a​d​d​r​ ​-​>​ ​i​n​e​t​_​n​a​m​e​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​n​t​o​p​_​r​e​s​u​l​t​ ​=​ ​i​n​e​t​_​n​t​o​p​ ​(​A​F​_​I​N​E​T​,​ ​&​i​n​e​t​_​a​d​d​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​e​t​_​n​a​m​e​,​ ​s​i​z​e​o​f​(​i​n​e​t​_​n​a​m​e​)​)​;​

 ​ ​ ​ ​i​f​ ​(​n​t​o​p​_​r​e​s​u​l​t​ ​!​=​ ​N​U​L​L​)​ ​{​ ​ ​/​/​ ​n​t​o​p​_​r​e​s​u​l​t​ ​=​=​ ​i​n​e​t​_​n​a​m​e​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​a​d​d​r​e​s​s​ ​'​%​x​'​ ​p​r​e​s​e​n​t​a​t​i​o​n​:​ ​'​%​s​'​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​t​o​h​l​(​i​n​e​t​_​a​d​d​r​.​s​_​a​d​d​r​)​,​ ​i​n​e​t​_​n​a​m​e​)​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​*​*​*​ ​i​n​e​t​_​n​t​o​p​:​ ​e​r​r​o​r​ ​%​d​:​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
 ​ ​ ​ ​/​/​ ​I​P​v​6​

 ​ ​ ​ ​/​/​ ​C​o​n​v​e​r​t​ ​f​r​o​m​ ​p​r​e​s​e​n​t​a​t​i​o​n​ ​t​o​ ​n​u​m​e​r​i​c​,​ ​ ​i​n​e​t​6​_​n​a​m​e​ ​-​>​ ​i​n​e​t​6​_​a​d​d​r​
 ​ ​ ​ ​c​h​a​r​ ​i​n​e​t​6​_​n​a​m​e​[​I​N​E​T​6​_​A​D​D​R​S​T​R​L​E​N​]​;​
 ​ ​ ​ ​s​t​r​n​c​p​y​ ​(​i​n​e​t​6​_​n​a​m​e​,​ ​"​F​E​8​0​:​0​0​0​0​:​0​0​0​0​:​0​0​0​0​:​0​2​3​0​:​6​5​F​F​:​F​E​0​6​:​6​5​2​3​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​I​N​E​T​6​_​A​D​D​R​S​T​R​L​E​N​)​;​
 ​ ​ ​ ​i​n​e​t​6​_​n​a​m​e​[​I​N​E​T​6​_​A​D​D​R​S​T​R​L​E​N​ ​-​ ​1​]​ ​=​ ​'​\​0​'​;​

 ​ ​ ​ ​s​t​r​u​c​t​ ​i​n​6​_​a​d​d​r​ ​i​n​e​t​6​_​a​d​d​r​;​
 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​i​n​e​t​_​p​t​o​n​ ​(​A​F​_​I​N​E​T​6​,​ ​i​n​e​t​6​_​n​a​m​e​,​ ​&​i​n​e​t​6​_​a​d​d​r​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​a​d​d​r​e​s​s​ ​'​%​s​'​\​n​ ​ ​ ​ ​i​n​ ​b​i​n​a​r​y​:​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​e​t​6​_​n​a​m​e​,​ ​i​n​6​T​o​C​h​a​r​(​&​i​n​e​t​6​_​a​d​d​r​)​)​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​*​*​*​ ​a​d​d​r​e​s​s​ ​'​%​s​'​ ​n​o​t​ ​p​a​r​s​a​b​l​e​\​n​\​n​"​,​ ​i​n​e​t​_​n​a​m​e​)​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​*​*​*​ ​i​n​e​t​_​p​t​o​n​:​ ​e​r​r​o​r​ ​%​d​:​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​C​o​n​v​e​r​t​ ​f​r​o​m​ ​n​u​m​e​r​i​c​ ​t​o​ ​p​r​e​s​e​n​t​a​t​i​o​n​,​ ​i​n​e​t​6​_​a​d​d​r​ ​-​>​ ​i​n​e​t​6​_​n​a​m​e​
 ​ ​ ​ ​n​t​o​p​_​r​e​s​u​l​t​ ​=​ ​i​n​e​t​_​n​t​o​p​ ​(​A​F​_​I​N​E​T​6​,​ ​&​i​n​e​t​6​_​a​d​d​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​e​t​6​_​n​a​m​e​,​ ​s​i​z​e​o​f​(​i​n​e​t​6​_​n​a​m​e​)​)​;​

 ​ ​ ​ ​i​f​ ​(​n​t​o​p​_​r​e​s​u​l​t​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​(​"​a​d​d​r​e​s​s​ ​'​%​s​'​\​n​ ​ ​ ​ ​p​r​e​s​e​n​t​a​t​i​o​n​:​ ​'​%​s​'​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​6​T​o​C​h​a​r​(​&​i​n​e​t​6​_​a​d​d​r​)​,​ ​i​n​e​t​6​_​n​a​m​e​)​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​(​"​*​*​*​ ​i​n​e​t​_​n​t​o​p​:​ ​e​r​r​o​r​ ​%​d​:​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
}​ ​ ​/​/​ ​m​a​i​n​

And here is a sample run:

$​ ​.​/​p​t​o​n​t​o​a​
a​d​d​r​e​s​s​ ​'​1​9​2​.​1​6​8​.​2​5​4​.​1​2​3​'​ ​i​n​ ​b​i​n​a​r​y​:​ ​c​0​a​8​f​e​7​b​
a​d​d​r​e​s​s​ ​'​c​0​a​8​f​e​7​b​'​ ​p​r​e​s​e​n​t​a​t​i​o​n​:​ ​'​1​9​2​.​1​6​8​.​2​5​4​.​1​2​3​'​
a​d​d​r​e​s​s​ ​'​F​E​8​0​:​0​0​0​0​:​0​0​0​0​:​0​0​0​0​:​0​2​3​0​:​6​5​F​F​:​F​E​0​6​:​6​5​2​3​'​
 ​ ​ ​ ​i​n​ ​b​i​n​a​r​y​:​ ​f​e​8​0​0​0​0​0​0​2​3​0​6​5​f​f​f​e​0​6​6​5​2​3​
a​d​d​r​e​s​s​ ​'​f​e​8​0​0​0​0​0​0​2​3​0​6​5​f​f​f​e​0​6​6​5​2​3​'​
 ​ ​ ​ ​p​r​e​s​e​n​t​a​t​i​o​n​:​ ​'​f​e​8​0​:​:​2​3​0​:​6​5​f​f​:​f​e​0​6​:​6​5​2​3​'​

inet_ntop() is nice enough to render the IPv6 address in its minimal format, omitting extra zeros.

IPv4-specific functions

The functions described earlier can handle all of your address conversion needs. If you are working on an older platform that does not have those functions or if you are maintaining code that was written before IPv6, you may encounter a couple other address conversion functions.

These functions deal exclusively with 32-bit addresses and the dotted-quad presentation format. inet_aton converts from an ASCII to a binary representation, and inet_ntoa converts in the opposite direction.

i​n​t​ ​i​n​e​t​_​a​t​o​n​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​q​u​a​d​S​t​r​i​n​g​,​ ​s​t​r​u​c​t​ ​i​n​_​a​d​d​r​ ​*​a​d​d​r​e​s​s​)​;​

This converts an ASCII representation of a dotted quad to a numeric address (hence a-to-n). The numeric address is returned by reference. The function returns 1 if the conversion was successful and 0 if the string is invalid.

c​h​a​r​ ​*​i​n​e​t​_​n​t​o​a​ ​(​s​t​r​u​c​t​ ​i​n​_​a​d​d​r​ ​a​d​d​r​e​s​s​)​;​

This converts the numeric address to an ASCII representation. The string returned resides in static memory, so it will be clobbered by the next call to inet_ntoa().

Domain Name Lookup

Human beings tend to prefer dealing with hostnames rather than working with raw IP addresses, even if the IP address is in a nice user-friendly presentation format. DNS, short for Domain Name Service, is the mechanism for translating human-readable hostnames, like www.bignerdranch.com, into a corresponding IP address.

You will use struct hostent when looking up hostnames to resolve the address.

s​t​r​u​c​t​ ​h​o​s​t​e​n​t​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​*​h​_​n​a​m​e​;​
 ​ ​ ​ ​c​h​a​r​ ​ ​*​*​h​_​a​l​i​a​s​e​s​;​
 ​ ​ ​ ​i​n​t​ ​ ​ ​ ​ ​h​_​a​d​d​r​t​y​p​e​;​
 ​ ​ ​ ​i​n​t​ ​ ​ ​ ​ ​h​_​l​e​n​g​t​h​;​
 ​ ​ ​ ​c​h​a​r​ ​ ​*​*​h​_​a​d​d​r​_​l​i​s​t​;​
 ​ ​ ​ ​#​d​e​f​i​n​e​ ​h​_​a​d​d​r​ ​ ​h​_​a​d​d​r​_​l​i​s​t​[​0​]​
}​;​

The elements of the structure are:

h_name | Official name of the host.
h_aliases | A NULL-terminated array of alternate names for the host.
h_addrtype | The type of address being returned, AF_INET for Internet addresses.
h_length | The length, in bytes, of the address.
h_addr_list | A NULL-terminated array of network addresses (struct in_addr or struct in6_addr) for the host, in network byte order.
h_addr | The first address in h_addr_list. Here for backward compatibility, or as a convenience if you just want to pick up the first address in the list.

gethostbyname2() looks up a hostname and returns a filled-in hostent, which like inet_ntoa is allocated in static space, so it will get clobbered on the next call. It is also not thread-safe.

s​t​r​u​c​t​ ​h​o​s​t​e​n​t​ ​*​g​e​t​h​o​s​t​b​y​n​a​m​e​2​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​h​o​s​t​n​a​m​e​,​ ​i​n​t​ ​a​d​d​r​e​s​s​F​a​m​i​l​y​)​;​

Returns a pointer to a struct hostent that contains the resolved address for hostname, using the specified addressFamily. There is also an older gethostbyname() function which takes a hostname argument, but no addressFamily

gethostbyname2() does not return an error in errno. It provides its own variant of errno called h_errno, and you can use hstrerror() to get a human-readable message.

Example 14.2 is a program that exercises the address conversion functions.

Example 14.2. resolve.m

/​/​ ​r​e​s​o​l​v​e​.​m​ ​-​-​ ​r​e​s​o​l​v​e​ ​a​n​ ​a​d​d​r​e​s​s​ ​u​s​i​n​g​ ​g​e​t​h​o​s​t​b​y​n​a​m​e​2​(​)​

/​/​ ​g​c​c​ ​-​s​t​d​=​c​9​9​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​r​e​s​o​l​v​e​ ​r​e​s​o​l​v​e​.​m​

#​i​m​p​o​r​t​ ​<​a​r​p​a​/​i​n​e​t​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​i​n​e​t​_​n​t​o​p​
#​i​m​p​o​r​t​ ​<​n​e​t​d​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​g​e​t​h​o​s​t​b​y​n​a​m​e​
#​i​m​p​o​r​t​ ​<​n​e​t​i​n​e​t​/​i​n​.​h​>​ ​ ​/​/​ ​c​o​n​s​t​a​n​t​s​ ​a​n​d​ ​t​y​p​e​s​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​f​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​o​c​k​e​t​.​h​>​ ​ ​/​/​ ​f​o​r​ ​A​F​_​I​N​E​T​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​ ​/​/​ ​r​a​n​d​o​m​ ​t​y​p​e​s​

s​t​a​t​i​c​ ​v​o​i​d​ ​P​r​i​n​t​H​o​s​t​E​n​t​ ​(​c​o​n​s​t​ ​s​t​r​u​c​t​ ​h​o​s​t​e​n​t​ ​*​h​o​s​t​)​ ​{​
 ​ ​ ​ ​/​/​ ​F​i​r​s​t​ ​t​h​e​ ​n​a​m​e​.​
 ​ ​ ​ ​p​r​i​n​t​f​(​"​ ​ ​ ​ ​o​f​f​i​c​i​a​l​ ​n​a​m​e​:​ ​%​s​\​n​"​,​ ​h​o​s​t​-​>​h​_​n​a​m​e​)​;​

 ​ ​ ​ ​/​/​ ​T​h​e​n​ ​a​n​y​ ​a​l​i​a​s​e​s​.​
 ​ ​ ​ ​i​f​ ​(​h​o​s​t​-​>​h​_​a​l​i​a​s​e​s​[​0​]​ ​=​=​ ​N​U​L​L​)​ ​p​r​i​n​t​f​ ​(​"​ ​ ​ ​ ​n​o​ ​a​l​i​a​s​e​s​\​n​"​)​;​
 ​ ​ ​ ​e​l​s​e​ ​p​r​i​n​t​f​ ​(​"​ ​ ​ ​ ​h​a​s​ ​a​l​i​a​s​e​s​\​n​"​)​;​

 ​ ​ ​ ​f​o​r​ ​(​c​h​a​r​ ​*​*​s​c​a​n​ ​=​ ​h​o​s​t​-​>​h​_​a​l​i​a​s​e​s​;​ ​*​s​c​a​n​ ​!​=​ ​N​U​L​L​;​ ​+​+​s​c​a​n​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​ ​ ​ ​ ​ ​ ​%​s​\​n​"​,​ ​*​s​c​a​n​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​a​d​d​r​e​s​s​ ​t​y​p​e​.​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​a​d​d​r​e​s​s​T​y​p​e​ ​=​ ​"​U​N​K​N​O​W​N​"​;​
 ​ ​ ​ ​i​f​ ​(​h​o​s​t​-​>​h​_​a​d​d​r​t​y​p​e​ ​=​=​ ​A​F​_​I​N​E​T​)​ ​a​d​d​r​e​s​s​T​y​p​e​ ​=​ ​"​A​F​_​I​N​E​T​"​;​
 ​ ​ ​ ​e​l​s​e​ ​i​f​ ​(​h​o​s​t​-​>​h​_​a​d​d​r​t​y​p​e​ ​=​=​ ​A​F​_​I​N​E​T​6​)​ ​a​d​d​r​e​s​s​T​y​p​e​ ​=​ ​"​A​F​_​I​N​E​T​6​"​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​ ​ ​a​d​d​r​t​y​p​e​:​ ​%​d​ ​(​%​s​)​\​n​"​,​ ​h​o​s​t​-​>​h​_​a​d​d​r​t​y​p​e​,​ ​a​d​d​r​e​s​s​T​y​p​e​)​;​

 ​ ​ ​ ​/​/​ ​W​a​l​k​ ​h​_​a​d​d​r​_​l​i​s​t​ ​a​n​d​ ​p​r​i​n​t​ ​t​h​e​ ​a​d​d​r​e​s​s​e​s​
 ​ ​ ​ ​i​f​ ​(​h​o​s​t​-​>​h​_​a​d​d​r​ ​=​=​ ​N​U​L​L​)​ ​p​r​i​n​t​f​ ​(​"​ ​ ​ ​ ​n​o​ ​a​d​d​r​e​s​s​e​s​\​n​"​)​;​
 ​ ​ ​ ​e​l​s​e​ ​p​r​i​n​t​f​ ​(​"​ ​ ​ ​ ​a​d​d​r​e​s​s​e​s​:​\​n​"​)​;​

 ​ ​ ​ ​f​o​r​ ​(​c​h​a​r​ ​*​*​s​c​a​n​ ​=​ ​h​o​s​t​-​>​h​_​a​d​d​r​_​l​i​s​t​;​ ​*​s​c​a​n​ ​!​=​ ​N​U​L​L​;​ ​s​c​a​n​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​a​d​d​r​_​n​a​m​e​[​I​N​E​T​_​A​D​D​R​S​T​R​L​E​N​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​i​n​e​t​_​n​t​o​p​(​h​o​s​t​-​>​h​_​a​d​d​r​t​y​p​e​,​ ​*​s​c​a​n​,​ ​a​d​d​r​_​n​a​m​e​,​ ​s​i​z​e​o​f​(​a​d​d​r​_​n​a​m​e​)​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​ ​ ​ ​ ​ ​ ​%​s​\​n​"​,​ ​a​d​d​r​_​n​a​m​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

}​ ​/​/​ ​P​r​i​n​t​H​o​s​t​E​n​t​

i​n​t​ ​m​a​i​n​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​h​o​s​t​n​a​m​e​;​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​=​=​ ​1​)​ ​h​o​s​t​n​a​m​e​ ​=​ ​"​w​w​w​.​a​p​p​l​e​.​c​o​m​"​;​
 ​ ​ ​ ​e​l​s​e​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​o​s​t​n​a​m​e​ ​=​ ​a​r​g​v​[​1​]​;​

 ​ ​ ​ ​s​t​r​u​c​t​ ​h​o​s​t​e​n​t​ ​*​h​o​s​t​i​n​f​o​ ​=​ ​g​e​t​h​o​s​t​b​y​n​a​m​e​2​ ​(​h​o​s​t​n​a​m​e​,​ ​A​F​_​I​N​E​T​)​;​

 ​ ​ ​ ​i​f​ ​(​h​o​s​t​i​n​f​o​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​g​e​t​h​o​s​t​b​y​n​a​m​e​2​(​%​s​)​:​ ​*​*​*​ ​e​r​r​o​r​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​o​s​t​n​a​m​e​,​ ​h​s​t​r​e​r​r​o​r​(​h​_​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​g​e​t​h​o​s​t​b​y​n​a​m​e​2​:​ ​%​s​\​n​"​,​ ​h​o​s​t​n​a​m​e​)​;​
 ​ ​ ​ ​P​r​i​n​t​H​o​s​t​E​n​t​ ​(​h​o​s​t​i​n​f​o​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

and a sample run:

$​ ​.​/​r​e​s​o​l​v​e​
g​e​t​h​o​s​t​b​y​n​a​m​e​2​ ​w​w​w​.​a​p​p​l​e​.​c​o​m​
 ​ ​ ​ ​o​f​f​i​c​i​a​l​ ​n​a​m​e​:​ ​e​3​1​9​1​.​c​.​a​k​a​m​a​i​e​d​g​e​.​n​e​t​
 ​ ​ ​ ​a​l​i​a​s​e​s​:​
 ​ ​ ​ ​ ​ ​ ​ ​w​w​w​.​a​p​p​l​e​.​c​o​m​
 ​ ​ ​ ​ ​ ​ ​ ​w​w​w​.​a​p​p​l​e​.​c​o​m​.​a​k​a​d​n​s​.​n​e​t​
 ​ ​ ​ ​ ​ ​ ​ ​w​w​w​.​a​p​p​l​e​.​c​o​m​.​e​d​g​e​k​e​y​.​n​e​t​
 ​ ​ ​ ​a​d​d​r​t​y​p​e​:​ ​2​ ​(​A​F​_​I​N​E​T​)​
 ​ ​ ​ ​a​d​d​r​e​s​s​e​s​:​
 ​ ​ ​ ​ ​ ​ ​ ​7​2​.​2​4​7​.​1​0​9​.​1​5​

That weird official name for www.apple.com that ends with akamaiedge.net is the Akamai service to find the closest host to you when it serves up the page. The first h_aliases entry of www.apple.com has the name that a web browser would display to the user when they go to the site.

And a second run:

%​ ​.​/​r​e​s​o​l​v​e​ ​g​o​o​g​l​e​.​c​o​m​
g​e​t​h​o​s​t​b​y​n​a​m​e​2​:​ ​g​o​o​g​l​e​.​c​o​m​
 ​ ​ ​ ​o​f​f​i​c​i​a​l​ ​n​a​m​e​:​ ​g​o​o​g​l​e​.​c​o​m​
 ​ ​ ​ ​n​o​ ​a​l​i​a​s​e​s​
 ​ ​ ​ ​a​d​d​r​t​y​p​e​:​ ​2​ ​(​A​F​_​I​N​E​T​)​
 ​ ​ ​ ​a​d​d​r​e​s​s​e​s​:​
 ​ ​ ​ ​ ​ ​ ​ ​7​4​.​1​2​5​.​9​3​.​1​4​7​
 ​ ​ ​ ​ ​ ​ ​ ​7​4​.​1​2​5​.​9​3​.​1​0​4​
 ​ ​ ​ ​ ​ ​ ​ ​7​4​.​1​2​5​.​9​3​.​1​0​3​
 ​ ​ ​ ​ ​ ​ ​ ​7​4​.​1​2​5​.​9​3​.​1​0​5​
 ​ ​ ​ ​ ​ ​ ​ ​7​4​.​1​2​5​.​9​3​.​1​0​6​
 ​ ​ ​ ​ ​ ​ ​ ​7​4​.​1​2​5​.​9​3​.​9​9​

Here there are no aliases, but there are multiple addresses that can be used to reach the Google search page.

These name resolution calls are blocking calls and should not be used on the main thread of an iOS program. The DNS timeout is longer than the watchdog timer that kills unresponsive applications. You should either put name lookup into another thread or another process or use the CFHost functions described in Chapter 15: CFRunLoop

Simple Network Programming

In networking, the terms “client” and “server” describe the party that is waiting for a new connection (the server) and the party that initiates a new connection (the client). Once the communications channel is set up, the client and server are peers that communicate with each other.

Server coding

Here is the set of calls and actions used when writing a server program:

Get a socket().

Construct an address–port pair that the server will listen on.

bind() the socket to the address.

Tell the socket to start listen()ing for new connections.

accept() new connections from clients.

Use read() and write() to receive and send data over the client connection.

close() the client connection’s socket.

int socket (int domain, int type, int protocol);

This creates a socket and returns a file descriptor, just like the file descriptors from Chapter 11: Files, Part 1: I/O and Permissions. You won’t actually read or write through this particular file descriptor for a server. Instead, you’ll use it as the focus for other networking calls. The accept() call described later returns a file descriptor you can read and write through.

For domain, use AF_INET for an internet socket or AF_INET6 for an IPv6 socket. For type, use SOCK_STREAM for a reliable connection, which means the bytes you send will be received in the same order you sent them and without error.

Apple recommends always making an IPv6 socket to listen on. The OS will automatically “downgrade” it to an IPv4 socket if necessary, so there is no need to listen on two different sockets.

For protocol, just pass zero to have the system use the default protocol.

This returns -1 on error and sets errno appropriately.

Constructing an address

To construct the address, create a struct sockaddr_in6 on the stack and fill out the various fields:

s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​6​ ​a​d​d​r​e​s​s​ ​=​ ​{​ ​0​ ​}​;​
a​d​d​r​e​s​s​.​s​i​n​6​_​l​e​n​ ​=​ ​s​i​z​e​o​f​(​a​d​d​r​e​s​s​)​;​
a​d​d​r​e​s​s​.​s​i​n​6​_​f​a​m​i​l​y​ ​=​ ​A​F​_​I​N​E​T​6​;​
a​d​d​r​e​s​s​.​s​i​n​6​_​p​o​r​t​ ​=​ ​h​t​o​n​s​(​2​3​4​2​)​;​
a​d​d​r​e​s​s​.​s​i​n​6​_​a​d​d​r​ ​=​ ​i​n​6​a​d​d​r​_​a​n​y​;​

The family for this address is AF_INET6, with AF standing for “address family.” in6addr_any is a global value that means that any IP address can be used. You would use INADDR_ANY for IPv4 addresses. This will cause the server program to listen on the network address of your machine. If you have multiple network addresses, it will listen on all of them. You can, of course, provide a particular address to listen on.

The port and address are put into network byte order, and the sin_zero portion of the structure is zeroed out.

bind

bind() brings the address and the socket together. The socket then owns that address-port pair.

int bind (int socket, const struct sockaddr *address, int addresslen);

This takes a pointer to a struct sockaddr, not a struct sockaddr_in6, so you need to cast. addresslen is the size of your struct sockaddr_in6.

bind() will return an error if the address-port pair is already in use. This can be frustrating if your program has crashed, but the previously bound socket still exists in the kernel. The kernel might not release this address-port pair until a timeout of a couple of minutes happens. You can tell the socket to reuse such an abandoned but not yet timed-out address using by calling setsockopt() as follows before calling bind():

i​n​t​ ​y​e​s​ ​=​ ​1​;​
r​e​s​u​l​t​ ​=​ ​s​e​t​s​o​c​k​o​p​t​ ​(​s​o​c​k​e​t​,​ ​S​O​L​_​S​O​C​K​E​T​,​ ​S​O​_​R​E​U​S​E​A​D​D​R​,​ ​&​y​e​s​,​ ​s​i​z​e​o​f​(​i​n​t​)​)​;​

setsockopt() is a generic interface for tweaking different socket parameters. Both setsockopt() and bind() return -1 on errors and set errno.

listen

The listen() system call informs the kernel to start accepting connections to the address and port specified in the address passed to bind().

int listen (int socket, int backlog);

The backlog parameter hints at the maximum length of the queue of pending connection requests. Once the queue has filled, all subsequent attempts to connect to the socket garner a “connection refused” error. The system caps the size of the backlog queue to prevent excessive use of system memory.

accept

The accept() call will wait for a new incoming connection. When a new connection is made, accept() will return with a file descriptor that you can read and write through. This file descriptor is just like any other, so you can use functions like read(), write(), send(), and readv(), and you can wrap the descriptor in a standard I/O FILE if you so desire. Some file operations, like seeking, will result in errors since you cannot seek with a network connection.

int accept (int socket, struct sockaddr *address, socklen_t *addressLength);

The address parameter will contain the address of the client making the request upon a successful accept(). It returns -1 on error and sets errno appropriately.

accept() will block until an incoming connection happens or an error happens. The socket can be set into a non-blocking mode to avoid blocking; you will read about that later when we discuss multiplexing connections.

Example 14.3 is a simple server that binds to port 2342 and listens for incoming connections. You can make such a connection using the telnet command. When a connection is made, the data from the other program is read and printed. Then it waits for another connection.

Example 14.3. simpleserver.m

/​/​ ​s​i​m​p​l​e​s​e​r​v​e​r​.​m​ ​-​-​ ​l​i​s​t​e​n​ ​o​n​ ​a​ ​p​o​r​t​ ​a​n​d​ ​p​r​i​n​t​ ​a​n​y​ ​b​y​t​e​s​ ​t​h​a​t​ ​c​o​m​e​ ​t​h​r​o​u​g​h​
/​/​ ​ ​ ​R​u​n​ ​w​i​t​h​o​u​t​ ​a​r​g​u​m​e​n​t​s​ ​t​o​ ​b​i​n​d​ ​t​o​ ​a​n​ ​I​P​v​4​ ​a​d​d​r​e​s​s​.​
/​/​ ​ ​ ​R​u​n​ ​w​i​t​h​ ​a​n​y​ ​a​r​g​u​m​e​n​t​ ​t​o​ ​b​i​n​d​ ​t​o​ ​a​n​ ​I​P​v​6​ ​a​d​d​r​e​s​s​.​

/​/​ ​g​c​c​ ​-​s​t​d​=​c​9​9​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​s​i​m​p​l​e​s​e​r​v​e​r​ ​s​i​m​p​l​e​s​e​r​v​e​r​.​m​

#​i​m​p​o​r​t​ ​<​a​r​p​a​/​i​n​e​t​.​h​>​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​i​n​e​t​_​n​t​o​p​
#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​n​e​t​i​n​e​t​/​i​n​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​o​c​k​a​d​d​r​_​i​n​
#​i​m​p​o​r​t​ ​<​n​e​t​i​n​e​t​6​/​i​n​6​.​h​>​ ​ ​/​/​ ​s​o​c​k​a​d​d​r​_​i​n​6​
#​i​m​p​o​r​t​ ​<​s​t​d​b​o​o​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​t​r​u​e​/​f​a​l​s​e​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​f​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​o​c​k​e​t​.​h​>​ ​ ​ ​ ​/​/​ ​s​o​c​k​e​t​(​)​,​ ​A​F​_​I​N​E​T​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​ ​ ​ ​/​/​ ​r​a​n​d​o​m​ ​t​y​p​e​s​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​c​l​o​s​e​(​)​

s​t​a​t​i​c​ ​c​o​n​s​t​ ​i​n​_​p​o​r​t​_​t​ ​k​P​o​r​t​N​u​m​b​e​r​ ​=​ ​2​3​4​2​;​
s​t​a​t​i​c​ ​c​o​n​s​t​ ​i​n​t​ ​k​A​c​c​e​p​t​Q​u​e​u​e​S​i​z​e​H​i​n​t​ ​=​ ​8​;​

s​t​a​t​i​c​ ​v​o​i​d​ ​A​c​c​e​p​t​C​l​i​e​n​t​F​r​o​m​S​o​c​k​e​t​ ​(​i​n​t​ ​l​i​s​t​e​n​F​d​)​;​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​e​x​i​t​C​o​d​e​ ​=​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​

 ​ ​ ​ ​c​o​n​s​t​ ​b​o​o​l​ ​u​s​e​I​P​v​6​ ​=​ ​(​a​r​g​c​ ​>​ ​1​)​;​

 ​ ​ ​ ​/​/​ ​g​e​t​ ​a​ ​s​o​c​k​e​t​
 ​ ​ ​ ​i​n​t​ ​f​d​;​
 ​ ​ ​ ​i​f​ ​(​u​s​e​I​P​v​6​)​ ​f​d​ ​=​ ​s​o​c​k​e​t​ ​(​A​F​_​I​N​E​T​6​,​ ​S​O​C​K​_​S​T​R​E​A​M​,​ ​0​)​;​
 ​ ​ ​ ​e​l​s​e​ ​f​d​ ​=​ ​s​o​c​k​e​t​ ​(​A​F​_​I​N​E​T​,​ ​S​O​C​K​_​S​T​R​E​A​M​,​ ​0​)​;​

 ​ ​ ​ ​i​f​ ​(​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​*​*​*​ ​s​o​c​k​e​t​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​c​l​e​a​n​u​p​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​R​e​u​s​e​ ​t​h​e​ ​a​d​d​r​e​s​s​ ​s​o​ ​s​t​a​l​e​ ​s​o​c​k​e​t​s​ ​w​o​n​'​t​ ​k​i​l​l​ ​u​s​.​
 ​ ​ ​ ​i​n​t​ ​y​e​s​ ​=​ ​1​;​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​s​e​t​s​o​c​k​o​p​t​ ​(​f​d​,​ ​S​O​L​_​S​O​C​K​E​T​,​ ​S​O​_​R​E​U​S​E​A​D​D​R​,​ ​&​y​e​s​,​ ​s​i​z​e​o​f​(​y​e​s​)​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​(​"​*​*​*​ ​s​e​t​s​o​c​k​o​p​t​(​S​O​_​R​E​U​S​E​A​D​D​R​)​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​c​l​e​a​n​u​p​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​B​i​n​d​ ​t​o​ ​a​n​ ​a​d​d​r​e​s​s​ ​a​n​d​ ​p​o​r​t​

 ​ ​ ​ ​/​/​ ​G​l​o​m​ ​b​o​t​h​ ​k​i​n​d​s​ ​o​f​ ​a​d​d​r​e​s​s​e​s​ ​i​n​t​o​ ​a​ ​u​n​i​o​n​ ​t​o​ ​a​v​o​i​d​ ​c​a​s​t​i​n​g​.​
 ​ ​ ​ ​u​n​i​o​n​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​ ​s​a​;​ ​ ​ ​ ​ ​ ​ ​/​/​ ​a​v​o​i​d​s​ ​c​a​s​t​i​n​g​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​i​n​;​ ​ ​ ​ ​/​/​ ​I​P​v​4​ ​s​u​p​p​o​r​t​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​6​ ​i​n​6​;​ ​ ​/​/​ ​I​P​v​6​ ​s​u​p​p​o​r​t​
 ​ ​ ​ ​}​ ​a​d​d​r​e​s​s​;​

 ​ ​ ​ ​i​f​ ​(​u​s​e​I​P​v​6​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​6​.​s​i​n​6​_​l​e​n​ ​=​ ​s​i​z​e​o​f​ ​(​a​d​d​r​e​s​s​.​i​n​6​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​6​.​s​i​n​6​_​f​a​m​i​l​y​ ​=​ ​A​F​_​I​N​E​T​6​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​6​.​s​i​n​6​_​p​o​r​t​ ​=​ ​h​t​o​n​s​ ​(​k​P​o​r​t​N​u​m​b​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​6​.​s​i​n​6​_​f​l​o​w​i​n​f​o​ ​=​ ​0​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​6​.​s​i​n​6​_​a​d​d​r​ ​=​ ​i​n​6​a​d​d​r​_​a​n​y​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​6​.​s​i​n​6​_​s​c​o​p​e​_​i​d​ ​=​ ​0​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​.​s​i​n​_​l​e​n​ ​=​ ​s​i​z​e​o​f​ ​(​a​d​d​r​e​s​s​.​i​n​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​.​s​i​n​_​f​a​m​i​l​y​ ​=​ ​A​F​_​I​N​E​T​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​.​s​i​n​_​p​o​r​t​ ​=​ ​h​t​o​n​s​ ​(​k​P​o​r​t​N​u​m​b​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​.​s​i​n​_​a​d​d​r​.​s​_​a​d​d​r​ ​=​ ​h​t​o​n​l​ ​(​I​N​A​D​D​R​_​A​N​Y​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​m​e​m​s​e​t​ ​(​a​d​d​r​e​s​s​.​i​n​.​s​i​n​_​z​e​r​o​,​ ​0​,​ ​s​i​z​e​o​f​ ​(​a​d​d​r​e​s​s​.​i​n​.​s​i​n​_​z​e​r​o​)​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​b​i​n​d​ ​(​f​d​,​ ​&​a​d​d​r​e​s​s​.​s​a​,​ ​a​d​d​r​e​s​s​.​s​a​.​s​a​_​l​e​n​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​(​"​*​*​*​ ​b​i​n​d​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​c​l​e​a​n​u​p​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​l​i​s​t​e​n​ ​(​f​d​,​ ​k​A​c​c​e​p​t​Q​u​e​u​e​S​i​z​e​H​i​n​t​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​(​"​*​*​*​ ​l​i​s​t​e​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​c​l​e​a​n​u​p​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​l​i​s​t​e​n​i​n​g​ ​o​n​ ​p​o​r​t​ ​%​d​\​n​"​,​ ​(​i​n​t​)​k​P​o​r​t​N​u​m​b​e​r​)​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​t​r​u​e​)​ ​A​c​c​e​p​t​C​l​i​e​n​t​F​r​o​m​S​o​c​k​e​t​(​f​d​)​;​
 ​ ​ ​ ​e​x​i​t​C​o​d​e​ ​=​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

c​l​e​a​n​u​p​:​
 ​ ​ ​ ​c​l​o​s​e​ ​(​f​d​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​e​x​i​t​C​o​d​e​;​
}​

s​t​a​t​i​c​ ​v​o​i​d​ ​A​c​c​e​p​t​C​l​i​e​n​t​F​r​o​m​S​o​c​k​e​t​ ​(​i​n​t​ ​l​i​s​t​e​n​F​d​)​ ​{​

 ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​s​t​o​r​a​g​e​ ​a​d​d​r​;​
 ​ ​ ​ ​s​o​c​k​l​e​n​_​t​ ​a​d​d​r​_​l​e​n​ ​=​ ​s​i​z​e​o​f​(​a​d​d​r​)​;​

 ​ ​ ​ ​/​/​ ​A​c​c​e​p​t​ ​a​n​d​ ​g​e​t​ ​t​h​e​ ​r​e​m​o​t​e​ ​a​d​d​r​e​s​s​
 ​ ​ ​ ​i​n​t​ ​c​l​i​e​n​t​F​d​ ​=​ ​a​c​c​e​p​t​ ​(​l​i​s​t​e​n​F​d​,​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​ ​*​)​&​a​d​d​r​,​ ​&​a​d​d​r​_​l​e​n​)​;​

 ​ ​ ​ ​i​f​ ​(​c​l​i​e​n​t​F​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​(​"​*​*​*​ ​a​c​c​e​p​t​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​p​o​r​t​ ​a​n​d​ ​a​ ​p​o​i​n​t​e​r​ ​t​o​ ​t​h​e​ ​n​e​t​w​o​r​k​ ​a​d​d​r​e​s​s​.​
 ​ ​ ​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​n​e​t​_​a​d​d​r​ ​=​ ​N​U​L​L​;​
 ​ ​ ​ ​i​n​_​p​o​r​t​_​t​ ​p​o​r​t​ ​=​ ​0​;​

 ​ ​ ​ ​i​f​ ​(​a​d​d​r​.​s​s​_​f​a​m​i​l​y​ ​=​=​ ​A​F​_​I​N​E​T​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​*​a​d​d​r​_​i​n​ ​=​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​*​)​&​a​d​d​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​n​e​t​_​a​d​d​r​ ​=​ ​&​a​d​d​r​_​i​n​-​>​s​i​n​_​a​d​d​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​o​r​t​ ​=​ ​a​d​d​r​_​i​n​-​>​s​i​n​_​p​o​r​t​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​6​ ​*​a​d​d​r​_​i​n​6​ ​=​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​6​ ​*​)​&​a​d​d​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​n​e​t​_​a​d​d​r​ ​=​ ​&​a​d​d​r​_​i​n​6​-​>​s​i​n​6​_​a​d​d​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​o​r​t​ ​=​ ​a​d​d​r​_​i​n​6​-​>​s​i​n​6​_​p​o​r​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​C​o​n​v​e​r​t​ ​a​d​d​r​e​s​s​ ​t​o​ ​s​o​m​e​t​h​i​n​g​ ​h​u​m​a​n​ ​r​e​a​d​a​b​l​e​.​
 ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​4​0​9​6​]​;​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​n​a​m​e​ ​=​ ​i​n​e​t​_​n​t​o​p​ ​(​a​d​d​r​.​s​s​_​f​a​m​i​l​y​,​ ​n​e​t​_​a​d​d​r​,​ ​b​u​f​f​e​r​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​[​%​s​ ​p​o​r​t​ ​%​d​ ​c​o​n​n​e​c​t​e​d​]​\​n​"​,​ ​n​a​m​e​ ​?​ ​n​a​m​e​ ​:​ ​"​(​u​n​k​n​o​w​n​)​"​,​ ​n​t​o​h​s​(​p​o​r​t​)​)​;​

 ​ ​ ​ ​/​/​ ​d​r​a​i​n​ ​t​h​e​ ​s​o​c​k​e​t​
 ​ ​ ​ ​w​h​i​l​e​ ​(​t​r​u​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​s​i​z​e​_​t​ ​r​e​a​d​_​c​o​u​n​t​ ​=​ ​r​e​a​d​ ​(​c​l​i​e​n​t​F​d​,​ ​b​u​f​f​e​r​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​ ​-​ ​1​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​0​ ​=​=​ ​r​e​a​d​_​c​o​u​n​t​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​ ​ ​/​/​ ​e​n​d​ ​o​f​ ​f​i​l​e​
 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​r​e​a​d​_​c​o​u​n​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​*​*​*​ ​r​e​a​d​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​ ​ ​/​/​ ​e​r​r​o​r​
 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​Z​e​r​o​-​t​e​r​m​i​n​a​t​e​ ​t​h​e​ ​s​t​r​i​n​g​ ​a​n​d​ ​p​r​i​n​t​ ​i​t​ ​o​u​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​u​f​f​e​r​[​r​e​a​d​_​c​o​u​n​t​]​ ​=​ ​'​\​0​'​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​s​"​,​ ​b​u​f​f​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​c​l​o​s​e​ ​(​c​l​i​e​n​t​F​d​)​;​
 ​ ​ ​ ​p​u​t​s​(​"​[​c​o​n​n​e​c​t​i​o​n​ ​c​l​o​s​e​d​]​"​)​;​

}​ ​/​/​ ​A​c​c​e​p​t​C​l​i​e​n​t​F​r​o​m​S​o​c​k​e​t​

A sample run:

In one terminal window, run simpleserver:

$​ ​.​/​s​i​m​p​l​e​s​e​r​v​e​r​

And in another, run telnet:

$​ ​t​e​l​n​e​t​ ​l​o​c​a​l​h​o​s​t​ ​2​3​4​2​
T​r​y​i​n​g​ ​:​:​1​.​.​.​
t​e​l​n​e​t​:​ ​c​o​n​n​e​c​t​ ​t​o​ ​a​d​d​r​e​s​s​ ​:​:​1​:​ ​C​o​n​n​e​c​t​i​o​n​ ​r​e​f​u​s​e​d​
T​r​y​i​n​g​ ​f​e​8​0​:​:​1​.​.​.​
t​e​l​n​e​t​:​ ​c​o​n​n​e​c​t​ ​t​o​ ​a​d​d​r​e​s​s​ ​f​e​8​0​:​:​1​:​ ​C​o​n​n​e​c​t​i​o​n​ ​r​e​f​u​s​e​d​
T​r​y​i​n​g​ ​1​2​7​.​0​.​0​.​1​.​.​.​
C​o​n​n​e​c​t​e​d​ ​t​o​ ​l​o​c​a​l​h​o​s​t​.​
E​s​c​a​p​e​ ​c​h​a​r​a​c​t​e​r​ ​i​s​ ​'​^​]​'​.​
G​r​e​e​t​i​n​g​s​ ​f​r​o​m​ ​a​n​o​t​h​e​r​ ​t​e​r​m​i​n​a​l​!​
F​a​r​e​w​e​l​l​!​
(​p​r​e​s​s​ ​C​o​n​t​r​o​l​-​]​ ​t​o​ ​g​e​t​ ​a​ ​t​e​l​n​e​t​ ​p​r​o​m​p​t​)​
t​e​l​n​e​t​>​ ​q​u​i​t​
C​o​n​n​e​c​t​i​o​n​ ​c​l​o​s​e​d​.​
$​ ​

If you have not used telnet before, it makes a connection to the address and port indicated. telnet will then send any text you enter to the program on the other side, and it will display any returned text (in this case, no text is received).

Notice that we started our simpleserver listening on an IPv4 address. When telnet tries to connect to localhost on port 2342, you can see from the transcript that it tries and fails to find anything listening to port 2342 on the IPv6 loopback and link-local addresses. It reaches our simple server when it finally tries connecting to port 2342 at 127.0.0.1.

Here is what simpleserver prints during that session:

$​ ​.​/​s​i​m​p​l​e​s​e​r​v​e​r​
l​i​s​t​e​n​i​n​g​ ​o​n​ ​p​o​r​t​ ​2​3​4​2​
[​1​2​7​.​0​.​0​.​1​ ​p​o​r​t​ ​6​2​8​2​0​ ​c​o​n​n​e​c​t​e​d​]​
G​r​e​e​t​i​n​g​s​ ​f​r​o​m​ ​a​n​o​t​h​e​r​ ​t​e​r​m​i​n​a​l​!​
F​a​r​e​w​e​l​l​!​
[​c​o​n​n​e​c​t​i​o​n​ ​c​l​o​s​e​d​]​

You can telnet again to the server and enter more text. To make the server exit, interrupt it with Control-C.

To run the server bound to an IPv6 address, pass any argument:

$​ ​.​/​s​i​m​p​l​e​s​e​r​v​e​r​ ​-​6​
l​i​s​t​e​n​i​n​g​ ​o​n​ ​p​o​r​t​ ​2​3​4​2​

When you try to connect to localhost this time, you’ll find the server listening on the IPv6 loopback address:

$​ ​t​e​l​n​e​t​ ​l​o​c​a​l​h​o​s​t​ ​2​3​4​2​
T​r​y​i​n​g​ ​:​:​1​.​.​.​
C​o​n​n​e​c​t​e​d​ ​t​o​ ​l​o​c​a​l​h​o​s​t​.​
E​s​c​a​p​e​ ​c​h​a​r​a​c​t​e​r​ ​i​s​ ​'​^​]​'​.​

Client Coding

Code on the client is very similar to the server:

Figure out the address information of the server.

Construct a struct sockaddr_in address.

Get a socket().

connect() to the server.

Use read() and write() to send and receive data.

connect

connect() makes the connection to the server. It returns a file descriptor that you read and write with.

int connect (int socket, const struct sockaddr *address, socklen_t addresslen);

Give it the address to connect to, and it returns zero if the connection succeeds or -1 for an error, with errno set.

Example 14.4 is a client that is a little more convenient than telnet. It follows telnet’s example in searching for the host address: first it does an IPv6 lookup and tries all addresses listed there; then it tries an IPv4 lookup. In practice, this turns into two nested loops: the outer walks the address families, while the inner walks the addresses listed with that family for the host name.

Example 14.4. simpleclient.m

/​/​ ​s​i​m​p​l​e​c​l​i​e​n​t​.​m​ ​-​-​ ​r​e​a​d​ ​f​r​o​m​ ​s​t​d​i​n​ ​a​n​d​ ​s​e​n​d​ ​t​o​ ​t​h​e​ ​s​i​m​p​l​e​s​e​r​v​e​r​

/​/​ ​g​c​c​ ​-​s​t​d​=​c​9​9​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​s​i​m​p​l​e​c​l​i​e​n​t​ ​s​i​m​p​l​e​c​l​i​e​n​t​.​m​

#​i​m​p​o​r​t​ ​<​a​r​p​a​/​i​n​e​t​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​i​n​e​t​_​n​t​o​p​
#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​n​e​t​d​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​g​e​t​h​o​s​t​b​y​n​a​m​e​2​(​)​,​ ​h​_​e​r​r​n​o​,​ ​e​t​c​.​
#​i​m​p​o​r​t​ ​<​n​e​t​i​n​e​t​/​i​n​.​h​>​ ​ ​/​/​ ​s​o​c​k​a​d​d​r​_​i​n​
#​i​m​p​o​r​t​ ​<​s​t​d​b​o​o​l​.​h​>​ ​ ​ ​ ​ ​/​/​ ​t​r​u​e​/​f​a​l​s​e​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​s​t​r​e​r​r​o​r​(​)​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​o​c​k​e​t​.​h​>​ ​ ​/​/​ ​s​o​c​k​e​t​(​)​,​ ​A​F​_​I​N​E​T​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​ ​/​/​ ​r​a​n​d​o​m​ ​t​y​p​e​s​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​c​l​o​s​e​(​)​

s​t​a​t​i​c​ ​c​o​n​s​t​ ​i​n​_​p​o​r​t​_​t​ ​k​P​o​r​t​N​u​m​b​e​r​ ​=​ ​2​3​4​2​;​

s​t​a​t​i​c​ ​i​n​t​ ​S​o​c​k​e​t​C​o​n​n​e​c​t​e​d​T​o​H​o​s​t​N​a​m​e​d​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​h​o​s​t​n​a​m​e​)​;​
s​t​a​t​i​c​ ​b​o​o​l​ ​G​e​t​A​d​d​r​e​s​s​A​t​I​n​d​e​x​ ​(​s​t​r​u​c​t​ ​h​o​s​t​e​n​t​ ​*​h​o​s​t​,​ ​i​n​t​ ​a​d​d​r​e​s​s​I​n​d​e​x​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​s​t​o​r​a​g​e​ ​*​o​u​t​S​e​r​v​e​r​A​d​d​r​e​s​s​)​;​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​e​x​i​t​_​c​o​d​e​ ​=​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​h​o​s​t​ ​n​a​m​e​.​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​!​=​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​U​s​a​g​e​:​ ​%​s​ ​h​o​s​t​n​a​m​e​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​c​l​e​a​n​u​p​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​h​o​s​t​n​a​m​e​ ​=​ ​a​r​g​v​[​1​]​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​a​ ​c​o​n​n​e​c​t​e​d​ ​s​o​c​k​e​t​.​
 ​ ​ ​ ​i​n​t​ ​s​o​c​k​f​d​ ​=​ ​S​o​c​k​e​t​C​o​n​n​e​c​t​e​d​T​o​H​o​s​t​N​a​m​e​d​(​h​o​s​t​n​a​m​e​)​;​
 ​ ​ ​ ​i​f​ ​(​s​o​c​k​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​*​*​*​ ​U​n​a​b​l​e​ ​t​o​ ​c​o​n​n​e​c​t​ ​t​o​ ​%​s​ ​p​o​r​t​ ​%​d​.​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​o​s​t​n​a​m​e​,​ ​(​i​n​t​)​k​P​o​r​t​N​u​m​b​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​[​c​o​n​n​e​c​t​e​d​ ​t​o​ ​%​s​ ​p​o​r​t​ ​%​d​]​\​n​"​,​ ​h​o​s​t​n​a​m​e​,​ ​(​i​n​t​)​k​P​o​r​t​N​u​m​b​e​r​)​;​

 ​ ​ ​ ​/​/​ ​E​c​h​o​ ​l​i​n​e​s​ ​f​r​o​m​ ​s​t​d​i​n​ ​t​o​ ​s​o​c​k​f​d​.​

 ​ ​ ​ ​w​h​i​l​e​ ​(​t​r​u​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​4​0​9​6​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​b​y​t​e​s​ ​=​ ​f​g​e​t​s​ ​(​b​u​f​f​e​r​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​,​ ​s​t​d​i​n​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​c​h​e​c​k​ ​E​O​F​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​b​y​t​e​s​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​f​e​r​r​o​r​(​s​t​d​i​n​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​(​"​r​e​a​d​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​f​e​o​f​(​s​t​d​i​n​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​(​s​t​d​e​r​r​,​ ​"​E​O​F​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​s​s​i​z​e​_​t​ ​w​r​i​t​e​_​c​o​u​n​t​ ​=​ ​w​r​i​t​e​(​s​o​c​k​f​d​,​ ​b​u​f​f​e​r​,​ ​s​t​r​l​e​n​(​b​u​f​f​e​r​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​w​r​i​t​e​_​c​o​u​n​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​(​"​w​r​i​t​e​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​p​u​t​s​(​"​[​c​o​n​n​e​c​t​i​o​n​ ​c​l​o​s​e​d​]​"​)​;​
 ​ ​ ​ ​e​x​i​t​_​c​o​d​e​ ​=​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

c​l​e​a​n​u​p​:​
 ​ ​ ​ ​c​l​o​s​e​(​s​o​c​k​f​d​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​e​x​i​t​_​c​o​d​e​;​
}​ ​ ​/​/​ ​m​a​i​n​

/​/​ ​R​e​t​u​r​n​s​ ​-​1​ ​o​n​ ​f​a​i​l​u​r​e​,​ ​>​=​ ​0​ ​o​n​ ​s​u​c​c​e​s​s​.​
s​t​a​t​i​c​ ​i​n​t​ ​S​o​c​k​e​t​C​o​n​n​e​c​t​e​d​T​o​H​o​s​t​N​a​m​e​d​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​h​o​s​t​n​a​m​e​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​s​o​c​k​f​d​ ​=​ ​-​1​;​

 ​ ​ ​ ​/​/​ ​F​o​r​ ​e​a​c​h​ ​f​a​m​i​l​y​ ​c​a​l​l​ ​g​e​t​h​o​s​t​b​y​n​a​m​e​2​(​)​
 ​ ​ ​ ​s​a​_​f​a​m​i​l​y​_​t​ ​f​a​m​i​l​y​[​]​ ​=​ ​{​ ​A​F​_​I​N​E​T​6​,​ ​A​F​_​I​N​E​T​ ​}​;​
 ​ ​ ​ ​i​n​t​ ​f​a​m​i​l​y​_​c​o​u​n​t​ ​=​ ​s​i​z​e​o​f​(​f​a​m​i​l​y​)​ ​/​ ​s​i​z​e​o​f​(​*​f​a​m​i​l​y​)​;​

 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​s​o​c​k​f​d​ ​=​=​ ​-​1​ ​&​&​ ​i​ ​<​ ​f​a​m​i​l​y​_​c​o​u​n​t​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​(​"​L​o​o​k​i​n​g​ ​a​t​ ​%​s​ ​f​a​m​i​l​y​:​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​a​m​i​l​y​[​i​]​ ​=​=​ ​A​F​_​I​N​E​T​6​ ​?​ ​"​A​F​_​I​N​E​T​6​"​ ​:​ ​"​A​F​_​I​N​E​T​"​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​h​o​s​t​ ​a​d​d​r​e​s​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​h​o​s​t​e​n​t​ ​*​h​o​s​t​ ​=​ ​N​U​L​L​;​
 ​ ​ ​ ​ ​ ​ ​ ​h​o​s​t​ ​=​ ​g​e​t​h​o​s​t​b​y​n​a​m​e​2​(​h​o​s​t​n​a​m​e​,​ ​f​a​m​i​l​y​[​i​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​h​o​s​t​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​r​r​o​r​ ​(​"​g​e​t​h​o​s​t​b​y​n​a​m​e​2​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​i​n​u​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​T​r​y​ ​t​o​ ​c​o​n​n​e​c​t​ ​w​i​t​h​ ​e​a​c​h​ ​a​d​d​r​e​s​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​s​t​o​r​a​g​e​ ​s​e​r​v​e​r​_​a​d​d​r​;​

 ​ ​ ​ ​ ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​a​d​d​r​e​s​s​I​n​d​e​x​ ​=​ ​0​;​ ​s​o​c​k​f​d​ ​=​=​ ​-​1​;​ ​a​d​d​r​e​s​s​I​n​d​e​x​+​+​)​ ​{​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​G​r​a​b​ ​t​h​e​ ​n​e​x​t​ ​a​d​d​r​e​s​s​.​ ​ ​B​a​i​l​ ​o​u​t​ ​i​f​ ​w​e​'​v​e​ ​r​u​n​ ​o​u​t​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​!​G​e​t​A​d​d​r​e​s​s​A​t​I​n​d​e​x​(​h​o​s​t​,​ ​a​d​d​r​e​s​s​I​n​d​e​x​,​ ​&​s​e​r​v​e​r​_​a​d​d​r​)​)​ ​b​r​e​a​k​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​I​N​E​T​6​_​A​D​D​R​S​T​R​L​E​N​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​(​"​ ​ ​ ​ ​T​r​y​i​n​g​ ​%​s​.​.​.​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​e​t​_​n​t​o​p​(​h​o​s​t​-​>​h​_​a​d​d​r​t​y​p​e​,​ ​h​o​s​t​-​>​h​_​a​d​d​r​_​l​i​s​t​[​a​d​d​r​e​s​s​I​n​d​e​x​]​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​u​f​f​e​r​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​)​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​G​e​t​ ​a​ ​s​o​c​k​e​t​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​o​c​k​f​d​ ​=​ ​s​o​c​k​e​t​ ​(​s​e​r​v​e​r​_​a​d​d​r​.​s​s​_​f​a​m​i​l​y​,​ ​S​O​C​K​_​S​T​R​E​A​M​,​ ​0​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​s​o​c​k​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​ ​ ​ ​ ​ ​ ​ ​ ​s​o​c​k​e​t​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​i​n​u​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​R​e​a​c​h​ ​o​u​t​ ​a​n​d​ ​t​o​u​c​h​ ​s​o​m​e​o​n​e​.​ ​ ​C​l​i​e​n​t​s​ ​c​a​l​l​ ​c​o​n​n​e​c​t​(​)​ ​i​n​s​t​e​a​d​ ​o​f​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​b​i​n​d​(​)​ ​+​ ​l​i​s​t​e​n​(​)​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​e​r​r​ ​=​ ​c​o​n​n​e​c​t​ ​(​s​o​c​k​f​d​,​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​ ​*​)​&​s​e​r​v​e​r​_​a​d​d​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​e​r​v​e​r​_​a​d​d​r​.​s​s​_​l​e​n​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​e​r​r​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​n​e​c​t​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​l​o​s​e​ ​(​s​o​c​k​f​d​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​o​c​k​f​d​ ​=​ ​-​1​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​e​ ​s​u​c​c​e​s​s​f​u​l​l​y​ ​c​o​n​n​e​c​t​e​d​,​ ​s​o​ ​s​o​c​k​f​d​ ​i​s​ ​n​o​t​ ​-​1​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​B​o​t​h​ ​l​o​o​p​s​ ​w​i​l​l​ ​e​x​i​t​ ​a​t​ ​t​h​i​s​ ​p​o​i​n​t​.​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​o​c​k​f​d​;​
}​ ​/​/​ ​S​o​c​k​e​t​C​o​n​n​e​c​t​e​d​T​o​H​o​s​t​N​a​m​e​d​

/​/​ ​I​n​d​e​x​ ​i​n​t​o​ ​t​h​e​ ​h​o​s​t​e​n​t​ ​a​n​d​ ​g​e​t​ ​t​h​e​ ​a​d​d​r​e​s​s​I​n​d​e​x​'​t​h​ ​a​d​d​r​e​s​s​.​
/​/​ ​R​e​t​u​r​n​s​ ​t​r​u​e​ ​i​f​ ​s​u​c​c​e​s​s​f​u​l​,​ ​f​a​l​s​e​ ​i​f​ ​w​e​'​v​e​ ​r​u​n​ ​o​u​t​ ​o​f​ ​a​d​d​r​e​s​s​e​s​.​
s​t​a​t​i​c​ ​b​o​o​l​ ​G​e​t​A​d​d​r​e​s​s​A​t​I​n​d​e​x​ ​(​s​t​r​u​c​t​ ​h​o​s​t​e​n​t​ ​*​h​o​s​t​,​ ​i​n​t​ ​a​d​d​r​e​s​s​I​n​d​e​x​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​s​t​o​r​a​g​e​ ​*​o​u​t​S​e​r​v​e​r​A​d​d​r​e​s​s​)​ ​{​
 ​ ​ ​ ​/​/​ ​B​a​d​ ​a​r​g​u​m​e​n​t​s​?​
 ​ ​ ​ ​i​f​ ​(​o​u​t​S​e​r​v​e​r​A​d​d​r​e​s​s​ ​=​=​ ​N​U​L​L​ ​|​|​ ​h​o​s​t​ ​=​=​ ​N​U​L​L​)​ ​r​e​t​u​r​n​ ​f​a​l​s​e​;​

 ​ ​ ​ ​/​/​ ​O​u​t​ ​o​f​ ​a​d​d​r​e​s​s​e​s​?​
 ​ ​ ​ ​i​f​ ​(​h​o​s​t​-​>​h​_​a​d​d​r​_​l​i​s​t​[​a​d​d​r​e​s​s​I​n​d​e​x​]​ ​=​=​ ​N​U​L​L​)​ ​r​e​t​u​r​n​ ​f​a​l​s​e​;​

 ​ ​ ​ ​o​u​t​S​e​r​v​e​r​A​d​d​r​e​s​s​-​>​s​s​_​f​a​m​i​l​y​ ​=​ ​h​o​s​t​-​>​h​_​a​d​d​r​t​y​p​e​;​

 ​ ​ ​ ​i​f​ ​(​o​u​t​S​e​r​v​e​r​A​d​d​r​e​s​s​-​>​s​s​_​f​a​m​i​l​y​ ​=​=​ ​A​F​_​I​N​E​T​6​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​6​ ​*​a​d​d​r​ ​=​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​6​ ​*​)​o​u​t​S​e​r​v​e​r​A​d​d​r​e​s​s​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​-​>​s​i​n​6​_​l​e​n​ ​=​ ​s​i​z​e​o​f​(​*​a​d​d​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​-​>​s​i​n​6​_​p​o​r​t​ ​=​ ​h​t​o​n​s​(​k​P​o​r​t​N​u​m​b​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​-​>​s​i​n​6​_​f​l​o​w​i​n​f​o​ ​=​ ​0​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​-​>​s​i​n​6​_​a​d​d​r​ ​=​ ​*​(​s​t​r​u​c​t​ ​i​n​6​_​a​d​d​r​ ​*​)​h​o​s​t​-​>​h​_​a​d​d​r​_​l​i​s​t​[​a​d​d​r​e​s​s​I​n​d​e​x​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​-​>​s​i​n​6​_​s​c​o​p​e​_​i​d​ ​=​ ​0​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​*​a​d​d​r​ ​=​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​*​)​o​u​t​S​e​r​v​e​r​A​d​d​r​e​s​s​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​-​>​s​i​n​_​l​e​n​ ​=​ ​s​i​z​e​o​f​(​*​a​d​d​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​-​>​s​i​n​_​p​o​r​t​ ​=​ ​h​t​o​n​s​(​k​P​o​r​t​N​u​m​b​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​-​>​s​i​n​_​a​d​d​r​ ​=​ ​*​(​s​t​r​u​c​t​ ​i​n​_​a​d​d​r​ ​*​)​h​o​s​t​-​>​h​_​a​d​d​r​_​l​i​s​t​[​a​d​d​r​e​s​s​I​n​d​e​x​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​m​e​m​s​e​t​(​&​a​d​d​r​-​>​s​i​n​_​z​e​r​o​,​ ​0​,​ ​s​i​z​e​o​f​(​a​d​d​r​-​>​s​i​n​_​z​e​r​o​)​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​t​r​u​e​;​
}​ ​/​/​ ​G​e​t​A​d​d​r​e​s​s​A​t​I​n​d​e​x​

Compile this, start the server running in one terminal window, and then run the client in another. This sample run has the server running on a different machine, called “buggun,” on the local network,

%​ ​.​/​s​i​m​p​l​e​c​l​i​e​n​t​ ​b​i​g​g​u​n​.​l​o​c​a​l​.​
L​o​o​k​i​n​g​ ​a​t​ ​A​F​_​I​N​E​T​6​ ​f​a​m​i​l​y​:​
 ​ ​ ​ ​T​r​y​i​n​g​ ​f​e​8​0​:​6​:​:​2​2​5​:​f​f​:​f​e​f​4​:​6​7​3​b​.​.​.​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​n​n​e​c​t​:​ ​C​o​n​n​e​c​t​i​o​n​ ​r​e​f​u​s​e​d​
L​o​o​k​i​n​g​ ​a​t​ ​A​F​_​I​N​E​T​ ​f​a​m​i​l​y​:​
 ​ ​ ​ ​T​r​y​i​n​g​ ​1​9​2​.​1​6​8​.​2​5​4​.​8​.​.​.​
[​c​o​n​n​e​c​t​e​d​ ​t​o​ ​b​i​g​g​u​n​.​l​o​c​a​l​.​ ​p​o​r​t​ ​2​3​4​2​]​
H​e​l​l​o​ ​t​h​e​r​e​!​
I​ ​h​o​p​e​ ​t​h​e​ ​w​e​a​t​h​e​r​ ​i​s​ ​b​e​t​t​e​r​ ​w​h​e​r​e​ ​y​o​u​ ​a​r​e​.​
j​o​2​y​ ​a​n​d​ ​m​o​n​k​e​y​b​o​t​,​ ​s​i​t​t​i​n​g​ ​i​n​ ​a​ ​t​r​e​e​.​
(​T​y​p​e​ ​C​o​n​t​r​o​l​-​D​ ​t​o​ ​s​e​n​d​ ​E​O​F​ ​t​o​ ​t​h​e​ ​c​l​i​e​n​t​.​)​
E​O​F​
[​c​o​n​n​e​c​t​i​o​n​ ​c​l​o​s​e​d​]​

When you send EOF to the client, the fgets function returns NULL, and the connection is closed.

On the server side, you will see something like:

l​i​s​t​e​n​i​n​g​ ​o​n​ ​p​o​r​t​ ​2​3​4​2​
[​1​9​2​.​1​6​8​.​2​5​4​.​2​ ​p​o​r​t​ ​5​5​5​8​0​ ​c​o​n​n​e​c​t​e​d​]​
H​e​l​l​o​ ​t​h​e​r​e​!​
I​ ​h​o​p​e​ ​t​h​e​ ​w​e​a​t​h​e​r​ ​i​s​ ​b​e​t​t​e​r​ ​w​h​e​r​e​ ​y​o​u​ ​a​r​e​.​
j​o​2​y​ ​a​n​d​ ​m​o​n​k​e​y​b​o​t​,​ ​s​i​t​t​i​n​g​ ​i​n​ ​a​ ​t​r​e​e​.​
[​c​o​n​n​e​c​t​i​o​n​ ​c​l​o​s​e​d​]​

You can do more than just type text. You can send the contents of a file too:

$​ ​.​/​s​i​m​p​l​e​c​l​i​e​n​t​ ​l​o​c​a​l​h​o​s​t​ ​<​ ​/​u​s​r​/​s​h​a​r​e​/​d​i​c​t​/​w​o​r​d​s​

This sends about two megs of words across the connection to the server.

More Advanced Issues

Multiplexing connections

A one-connection-at-a-time server like simpleserver is OK for some applications, but to truly leverage the power of networking, it is better to have a single server support many connections simultaneously. To do this, you need a way to multiplex multiple input and output streams and only block when there is no work to be done.

The select() call is the Berkeley sockets way of doing this multiplexing. You will see in Chapter 16: kqueue and FSEvents an alternate way of multiplexing connections, which is superior to select() in many ways, but it is not available on many platforms. Grand Central Dispatch also gives you a way to multiplex sockets.

For select(), you give it a set of file descriptors. If there is any data to be read, or if there is the ability to write on any of the descriptors, select() will tell you so. Otherwise, it will block (with an optional timeout) until network activity is possible.

int select (int nfds, fd_set *readfds, fd_set *writefds,
            fd_set *exceptfds, struct timeval *timeout);

nfds | One more than the largest file descriptor number.
readfds | A set of file descriptors to see if you can read data from without blocking.
writefds | A set of file descriptors to see if you can write data to without blocking.
exceptfds | A set of file descriptors to see if there are any error conditions.
timeout | How long to wait for activity before breaking out of the select call. Pass NULL for no timeout (wait forever).

The fd_sets are modified by select(). You set the file descriptors you are interested in before calling select() and then examine the fd_sets to see which ones are interesting afterward.

What are fd_sets? They are an opaque bitvector that can be manipulated with this API:

 ​ ​ ​ ​ ​f​d​_​s​e​t​ ​f​d​s​e​t​;​

FD_ZERO (&fdset); | Clear out the fd_set.
FD_SET (fd, &fdset); | Add a file descriptor to the set.
FD_CLR (fd, &fdset); | Remove a file descriptor from the set.
FD_ISSET (fd, &fdset); | Test to see if a file descriptor is in the set.

Usually an fd_set holds 1024 items. If you try to put more than that into an fd_set, you could end up writing over memory after the fd_set’s storage. You can alter this by putting a #define FD_SETSIZE before including <sys/types.h>. The getdtablesize() function will return the size of the file descriptor table for a process, but because it is a function call, it is not useful to set to FD_SETSIZE.

struct timeval is defined like this:

s​t​r​u​c​t​ ​t​i​m​e​v​a​l​ ​{​
 ​ ​ ​ ​t​i​m​e​_​t​ ​ ​ ​ ​ ​ ​t​v​_​s​e​c​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​s​e​c​o​n​d​s​ ​*​/​
 ​ ​ ​ ​s​u​s​e​c​o​n​d​s​_​t​ ​t​v​_​u​s​e​c​;​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​a​n​d​ ​m​i​c​r​o​s​e​c​o​n​d​s​ ​*​/​
}​;​

You can specify the timeout in seconds and microseconds. You are not guaranteed microsecond granularity, though. Specify zero for both values to have select() return immediately. You can use this to have select() poll rather than block.

The usual way to use select() is like this:

/​/​ ​G​e​t​ ​t​h​e​ ​m​e​m​o​r​y​ ​a​n​d​ ​c​l​e​a​r​ ​i​t​ ​o​u​t​.​
f​d​_​s​e​t​ ​r​e​a​d​f​d​s​;​
F​D​_​Z​E​R​O​ ​(​&​r​e​a​d​f​d​s​)​;​

/​/​ ​A​d​d​ ​t​h​e​ ​l​i​s​t​e​n​ ​s​o​c​k​e​t​.​
F​D​_​S​E​T​(​m​y​L​i​s​t​e​n​S​o​c​k​e​t​,​ ​&​r​e​a​d​f​d​s​)​;​
i​n​t​ ​m​a​x​F​d​ ​=​ ​m​y​L​i​s​t​e​n​S​o​c​k​e​t​;​

/​/​ ​W​a​l​k​ ​t​h​e​ ​d​a​t​a​ ​s​t​r​u​c​t​u​r​e​ ​t​h​a​t​ ​h​a​s​ ​y​o​u​r​ ​o​p​e​n​ ​s​e​s​s​i​o​n​s​.​
f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​w​h​a​t​e​v​e​r​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​F​D​_​S​E​T​ ​(​s​e​s​s​i​o​n​-​>​f​d​,​ ​&​r​e​a​d​f​d​s​)​;​
 ​ ​ ​ ​m​a​x​F​d​ ​=​ ​M​A​X​ ​(​m​a​x​F​d​,​ ​s​e​s​s​i​o​n​-​>​f​d​)​;​
}​

i​n​t​ ​r​e​s​u​l​t​ ​=​ ​s​e​l​e​c​t​ ​(​m​a​x​F​d​ ​+​ ​1​,​ ​&​r​e​a​d​f​d​s​,​ ​N​U​L​L​,​ ​N​U​L​L​,​ ​N​U​L​L​)​;​

i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​.​.​.​ ​h​a​n​d​l​e​ ​e​r​r​o​r​
}​

/​/​ ​s​e​e​ ​i​f​ ​o​u​r​ ​a​c​c​e​p​t​ ​s​o​c​k​e​t​ ​i​s​ ​t​h​e​r​e​
i​f​ ​(​F​D​_​I​S​S​E​T​(​m​y​L​i​s​t​e​n​S​o​c​k​e​t​,​ ​&​r​e​a​d​f​d​s​)​)​ ​{​
 ​ ​ ​ ​/​/​ ​N​e​w​ ​c​o​n​n​e​c​t​i​o​n​.​ ​ ​C​a​l​l​ ​a​c​c​e​p​t​(​)​,​ ​m​a​k​e​ ​a​ ​n​e​w​ ​s​e​s​s​i​o​n​
}​

/​/​ ​W​a​l​k​ ​t​h​e​ ​d​a​t​a​ ​s​t​r​u​c​t​u​r​e​ ​a​g​a​i​n​ ​l​o​o​k​i​n​g​ ​f​o​r​ ​a​c​t​i​v​i​t​y​
f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​w​h​a​t​e​v​e​r​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​F​D​_​I​S​S​E​T​(​s​e​s​s​i​o​n​-​>​f​d​,​ ​&​r​e​a​d​f​d​s​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​r​e​a​d​(​s​e​s​s​i​o​n​-​>​f​d​,​ ​.​.​.​)​;​
 ​ ​ ​ ​}​
}​

Figure 14.1 shows select() consuming three file descriptor sets, each filled in with the active connections, and then removing any file descriptors that don’t have anything to read or write or that don’t have problems.

Figure 14.1  select()

It is important to remember that the first argument to select() is one more than the largest file descriptor. This is because it is a count of how many values in the FD_SET the kernel needs to test with FD_ISSET(). fd_sets can be copied and kept around. A common technique is to have a master fd_set that has all of the session file descriptors in it. Each time you call select(), you pass in a copy instead of the master itself. This saves you from walking your data structures every time before a call to select() at the cost of keeping that master set in sync with those data structures. This means clearing fds when sessions close and setting them when sessions open.

When a file descriptor is in the readfds set, it will appear in the set returned by select() when it has data available. It will also appear when the connection closes (read() will return zero in that case). As seen above, you can also put the listening socket in there to be informed when new connections are ready to be accepted.

One thing you might notice is that select() does not tell you how much data is available. If you just do a result = read(session->fd, buffer, bufferSize), the call to read() will block until bufferSize bytes have been read or an error occurs. That is not what you want to happen in a busy server program!

To avoid blocking, put the file descriptor into non-blocking mode. This causes read() to read however many bytes have arrived on the connection and then return. The return value from read() tells you how many bytes were read. To put a file descriptor into non-blocking mode, set the O_NONBLOCK flag using fcntl():

r​e​s​u​l​t​ ​=​ ​f​c​n​t​l​ ​(​f​d​,​ ​F​_​S​E​T​F​L​,​ ​O​_​N​O​N​B​L​O​C​K​)​;​

As usual, -1 is returned on error and errno set.

There is an alternative multiplexing API you might encounter called poll(). It is equivalent to select(), and each can be implemented in terms of the other.

Message boundaries

Some networking protocols, like HTTP, are pretty easy to deal with. You can just read everything that comes on the socket and then write out everything you need to. Other networking protocols are harder to deal with due to some of the real world issues regarding network communication and how the kernels on both sides of the connection buffer data.

Consider a little chat program where the client sends the server a message that looks like this:

length | : | one byte
message | : | length bytes of text

Here are some scenarios:

One message is returned from a read().

This is pretty easy. Just look at the first byte for the length, get the subsequent bytes of the message, and send it out to the chat clients.

Multiple complete messages are returned from a read().

Since read() does not know where the messages begin and end, you have to walk the read buffer: get a length byte, process subsequent bytes, get another length byte, process those bytes, until you run out.

A message straddles the bytes returned by more than one call to read().

This is where things get complicated. Between calls to read(), you need to remember that you only got part of a message. The first part waits around in a buffer until the other parts have arrived.

Generally, you will keep a buffer large enough to hold two or more complete messages and read into that buffer. When processing messages, you need to know whether you are continuing a partially received message or starting a new one.

Figure 14.2  Worst-case scenario for messages

Example 14.5 is the Chatter server side. The more interesting parts are the select() loop and readMessage(), which handles the various pathological cases of incoming messages.

Example 14.5. chatterserver.m

/​/​ ​c​h​a​t​t​e​r​s​e​r​v​e​r​.​m​ ​-​-​ ​c​h​a​t​ ​s​e​r​v​e​r​ ​u​s​i​n​g​ ​s​t​a​n​d​a​r​d​ ​s​o​c​k​e​t​s​ ​A​P​I​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​s​t​d​=​c​9​9​ ​-​o​ ​c​h​a​t​t​e​r​s​e​r​v​e​r​ ​c​h​a​t​t​e​r​s​e​r​v​e​r​.​m​

#​i​m​p​o​r​t​ ​<​s​t​d​b​o​o​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​t​r​u​e​/​f​a​l​s​e​
#​i​m​p​o​r​t​ ​<​s​t​d​i​n​t​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​U​I​N​T​8​_​M​A​X​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​s​t​r​e​r​r​o​r​(​)​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​f​c​n​t​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​c​n​t​l​(​)​
#​i​m​p​o​r​t​ ​<​m​a​c​h​/​v​m​_​p​a​r​a​m​.​h​>​ ​ ​/​/​ ​P​A​G​E​_​S​I​Z​E​
#​i​m​p​o​r​t​ ​<​s​i​g​n​a​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​s​i​g​a​c​t​i​o​n​(​)​
#​i​m​p​o​r​t​ ​<​s​y​s​/​u​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​i​o​v​e​c​
#​i​m​p​o​r​t​ ​<​s​y​s​l​o​g​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​s​y​s​l​o​g​(​)​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​c​l​o​s​e​(​)​

#​i​m​p​o​r​t​ ​<​a​r​p​a​/​i​n​e​t​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​i​n​e​t​_​n​t​o​p​(​)​
#​i​m​p​o​r​t​ ​<​n​e​t​i​n​e​t​/​i​n​.​h​>​ ​ ​ ​ ​ ​/​/​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​
#​i​m​p​o​r​t​ ​<​n​e​t​i​n​e​t​6​/​i​n​6​.​h​>​ ​ ​ ​/​/​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​6​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​o​c​k​e​t​.​h​>​ ​ ​ ​ ​ ​/​/​ ​s​o​c​k​e​t​(​)​,​ ​A​F​_​I​N​E​T​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​r​a​n​d​o​m​ ​t​y​p​e​s​

/​/​ ​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
/​/​ ​M​e​s​s​a​g​e​ ​p​r​o​t​o​c​o​l​

/​/​ ​A​ ​m​e​s​s​a​g​e​ ​i​s​ ​l​e​n​g​t​h​ ​+​ ​d​a​t​a​.​ ​T​h​e​ ​l​e​n​g​t​h​ ​i​s​ ​a​ ​s​i​n​g​l​e​ ​b​y​t​e​.​
/​/​ ​T​h​e​ ​f​i​r​s​t​ ​m​e​s​s​a​g​e​ ​s​e​n​t​ ​b​y​ ​a​ ​u​s​e​r​ ​h​a​s​ ​a​ ​m​a​x​ ​l​e​n​g​t​h​ ​o​f​ ​8​ ​a​n​d​ ​s​e​t​s​ ​t​h​e​ ​u​s​e​r​'​s​ ​n​a​m​e​.​

#​d​e​f​i​n​e​ ​M​A​X​(​x​,​ ​y​)​ ​(​(​(​x​)​ ​>​ ​(​y​)​)​ ​?​ ​(​x​)​ ​:​ ​(​y​)​)​
#​d​e​f​i​n​e​ ​M​A​X​_​M​E​S​S​A​G​E​_​S​I​Z​E​ ​ ​(​U​I​N​T​8​_​M​A​X​)​
#​d​e​f​i​n​e​ ​R​E​A​D​_​B​U​F​F​E​R​_​S​I​Z​E​ ​ ​(​P​A​G​E​_​S​I​Z​E​)​

/​/​ ​P​a​r​a​n​o​i​a​ ​c​h​e​c​k​ ​t​h​a​t​ ​t​h​e​ ​r​e​a​d​ ​b​u​f​f​e​r​ ​i​s​ ​l​a​r​g​e​ ​e​n​o​u​g​h​ ​t​o​ ​h​o​l​d​ ​a​ ​f​u​l​l​ ​m​e​s​s​a​g​e​.​
t​y​p​e​d​e​f​ ​u​i​n​t​8​_​t​ ​R​E​A​D​_​B​U​F​F​E​R​_​S​I​Z​E​_​n​o​t​_​l​e​s​s​_​t​h​a​n​_​M​A​X​_​M​E​S​S​A​G​E​_​S​I​Z​E​
 ​ ​ ​ ​[​!​(​R​E​A​D​_​B​U​F​F​E​R​_​S​I​Z​E​ ​<​ ​M​A​X​_​M​E​S​S​A​G​E​_​S​I​Z​E​)​ ​?​ ​0​ ​:​ ​-​1​]​;​

/​/​ ​T​h​e​r​e​ ​i​s​ ​o​n​e​ ​o​f​ ​t​h​e​s​e​ ​f​o​r​ ​e​a​c​h​ ​c​o​n​n​e​c​t​e​d​ ​u​s​e​r​
t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​C​h​a​t​t​e​r​U​s​e​r​_​ ​{​
 ​ ​ ​ ​i​n​t​ ​ ​ ​ ​ ​ ​f​d​;​ ​ ​ ​ ​ ​ ​ ​/​/​ ​z​e​r​o​ ​f​d​ ​=​=​ ​n​o​ ​u​s​e​r​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​ ​n​a​m​e​[​9​]​;​ ​ ​/​/​ ​8​ ​c​h​a​r​a​c​t​e​r​ ​n​a​m​e​ ​p​l​u​s​ ​t​r​a​i​l​i​n​g​ ​z​e​r​o​ ​b​y​t​e​
 ​ ​ ​ ​b​o​o​l​ ​ ​ ​ ​ ​g​o​t​N​a​m​e​;​ ​ ​/​/​ ​h​a​v​e​ ​w​e​ ​g​o​t​t​e​n​ ​t​h​e​ ​u​s​e​r​n​a​m​e​ ​p​a​c​k​e​t​?​

 ​ ​ ​ ​/​*​ ​i​n​c​o​m​i​n​g​ ​d​a​t​a​ ​w​o​r​k​s​p​a​c​e​ ​*​/​
 ​ ​ ​ ​s​s​i​z​e​_​t​ ​ ​b​y​t​e​s​R​e​a​d​;​
 ​ ​ ​ ​c​h​a​r​ ​ ​ ​ ​ ​b​u​f​f​e​r​[​R​E​A​D​_​B​U​F​F​E​R​_​S​I​Z​E​]​;​
}​ ​C​h​a​t​t​e​r​U​s​e​r​;​

#​d​e​f​i​n​e​ ​M​A​X​_​U​S​E​R​S​ ​5​0​
s​t​a​t​i​c​ ​C​h​a​t​t​e​r​U​s​e​r​ ​s​_​U​s​e​r​s​[​M​A​X​_​U​S​E​R​S​]​;​

s​t​a​t​i​c​ ​c​o​n​s​t​ ​i​n​_​p​o​r​t​_​t​ ​k​P​o​r​t​N​u​m​b​e​r​ ​=​ ​2​3​4​2​;​
s​t​a​t​i​c​ ​c​o​n​s​t​ ​i​n​t​ ​k​A​c​c​e​p​t​Q​u​e​u​e​S​i​z​e​H​i​n​t​ ​=​ ​8​;​

/​/​ ​R​e​t​u​r​n​s​ ​f​d​ ​o​n​ ​s​u​c​c​e​s​s​,​ ​-​1​ ​o​n​ ​e​r​r​o​r​.​ ​ ​B​a​s​e​d​ ​o​n​ ​m​a​i​n​(​)​ ​i​n​ ​s​i​m​p​l​e​s​e​r​v​e​r​.​m​
s​t​a​t​i​c​ ​i​n​t​ ​S​t​a​r​t​L​i​s​t​e​n​i​n​g​ ​(​c​o​n​s​t​ ​b​o​o​l​ ​u​s​e​I​P​v​6​)​ ​{​
 ​ ​ ​ ​/​/​ ​g​e​t​ ​a​ ​s​o​c​k​e​t​
 ​ ​ ​ ​i​n​t​ ​f​d​;​
 ​ ​ ​ ​i​f​ ​(​u​s​e​I​P​v​6​)​ ​f​d​ ​=​ ​s​o​c​k​e​t​ ​(​A​F​_​I​N​E​T​6​,​ ​S​O​C​K​_​S​T​R​E​A​M​,​ ​0​)​;​
 ​ ​ ​ ​e​l​s​e​ ​f​d​ ​=​ ​s​o​c​k​e​t​ ​(​A​F​_​I​N​E​T​,​ ​S​O​C​K​_​S​T​R​E​A​M​,​ ​0​)​;​

 ​ ​ ​ ​i​f​ ​(​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​*​*​*​ ​s​o​c​k​e​t​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​R​e​u​s​e​ ​t​h​e​ ​a​d​d​r​e​s​s​ ​s​o​ ​s​t​a​l​e​ ​s​o​c​k​e​t​s​ ​w​o​n​'​t​ ​k​i​l​l​ ​u​s​.​
 ​ ​ ​ ​i​n​t​ ​y​e​s​ ​=​ ​1​;​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​s​e​t​s​o​c​k​o​p​t​ ​(​f​d​,​ ​S​O​L​_​S​O​C​K​E​T​,​ ​S​O​_​R​E​U​S​E​A​D​D​R​,​ ​&​y​e​s​,​ ​s​i​z​e​o​f​(​y​e​s​)​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​*​*​*​ ​s​e​t​s​o​c​k​o​p​t​(​S​O​_​R​E​U​S​E​A​D​D​R​)​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​B​i​n​d​ ​t​o​ ​a​n​ ​a​d​d​r​e​s​s​ ​a​n​d​ ​p​o​r​t​

 ​ ​ ​ ​/​/​ ​G​l​o​m​ ​b​o​t​h​ ​k​i​n​d​s​ ​o​f​ ​a​d​d​r​e​s​s​e​s​ ​i​n​t​o​ ​a​ ​u​n​i​o​n​ ​t​o​ ​a​v​o​i​d​ ​c​a​s​t​i​n​g​.​
 ​ ​ ​ ​u​n​i​o​n​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​ ​s​a​;​ ​ ​ ​ ​ ​ ​ ​/​/​ ​a​v​o​i​d​s​ ​c​a​s​t​i​n​g​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​i​n​;​ ​ ​ ​ ​/​/​ ​I​P​v​4​ ​s​u​p​p​o​r​t​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​6​ ​i​n​6​;​ ​ ​/​/​ ​I​P​v​6​ ​s​u​p​p​o​r​t​
 ​ ​ ​ ​}​ ​a​d​d​r​e​s​s​;​

 ​ ​ ​ ​i​f​ ​(​u​s​e​I​P​v​6​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​6​.​s​i​n​6​_​l​e​n​ ​=​ ​s​i​z​e​o​f​ ​(​a​d​d​r​e​s​s​.​i​n​6​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​6​.​s​i​n​6​_​f​a​m​i​l​y​ ​=​ ​A​F​_​I​N​E​T​6​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​6​.​s​i​n​6​_​p​o​r​t​ ​=​ ​h​t​o​n​s​ ​(​k​P​o​r​t​N​u​m​b​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​6​.​s​i​n​6​_​f​l​o​w​i​n​f​o​ ​=​ ​0​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​6​.​s​i​n​6​_​a​d​d​r​ ​=​ ​i​n​6​a​d​d​r​_​a​n​y​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​6​.​s​i​n​6​_​s​c​o​p​e​_​i​d​ ​=​ ​0​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​.​s​i​n​_​l​e​n​ ​=​ ​s​i​z​e​o​f​ ​(​a​d​d​r​e​s​s​.​i​n​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​.​s​i​n​_​f​a​m​i​l​y​ ​=​ ​A​F​_​I​N​E​T​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​.​s​i​n​_​p​o​r​t​ ​=​ ​h​t​o​n​s​ ​(​k​P​o​r​t​N​u​m​b​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​.​i​n​.​s​i​n​_​a​d​d​r​.​s​_​a​d​d​r​ ​=​ ​h​t​o​n​l​ ​(​I​N​A​D​D​R​_​A​N​Y​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​m​e​m​s​e​t​ ​(​a​d​d​r​e​s​s​.​i​n​.​s​i​n​_​z​e​r​o​,​ ​0​,​ ​s​i​z​e​o​f​ ​(​a​d​d​r​e​s​s​.​i​n​.​s​i​n​_​z​e​r​o​)​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​b​i​n​d​ ​(​f​d​,​ ​&​a​d​d​r​e​s​s​.​s​a​,​ ​a​d​d​r​e​s​s​.​s​a​.​s​a​_​l​e​n​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​*​*​*​ ​b​i​n​d​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​l​i​s​t​e​n​ ​(​f​d​,​ ​k​A​c​c​e​p​t​Q​u​e​u​e​S​i​z​e​H​i​n​t​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​*​*​*​ ​l​i​s​t​e​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​p​r​i​n​t​f​(​"​l​i​s​t​e​n​i​n​g​ ​o​n​ ​p​o​r​t​ ​%​d​\​n​"​,​ ​(​i​n​t​)​k​P​o​r​t​N​u​m​b​e​r​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​f​d​;​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​i​f​ ​(​f​d​ ​!​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​l​o​s​e​ ​(​f​d​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​d​ ​=​ ​-​1​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​f​d​;​
}​ ​ ​/​/​ ​S​t​a​r​t​L​i​s​t​e​n​i​n​g​

/​/​ ​C​a​l​l​e​d​ ​w​h​e​n​ ​s​e​l​e​c​t​(​)​ ​i​n​d​i​c​a​t​e​s​ ​t​h​e​ ​l​i​s​t​e​n​i​n​g​ ​s​o​c​k​e​t​ ​i​s​ ​r​e​a​d​y​ ​t​o​ ​b​e​ ​r​e​a​d​,​
/​/​ ​w​h​i​c​h​ ​m​e​a​n​s​ ​t​h​e​r​e​ ​i​s​ ​a​ ​c​o​n​n​e​c​t​i​o​n​ ​w​a​i​t​i​n​g​ ​t​o​ ​b​e​ ​a​c​c​e​p​t​e​d​.​
s​t​a​t​i​c​ ​v​o​i​d​ ​A​c​c​e​p​t​C​o​n​n​e​c​t​i​o​n​ ​(​i​n​t​ ​l​i​s​t​e​n​_​f​d​)​ ​{​
 ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​s​t​o​r​a​g​e​ ​a​d​d​r​;​
 ​ ​ ​ ​s​o​c​k​l​e​n​_​t​ ​a​d​d​r​_​l​e​n​ ​=​ ​s​i​z​e​o​f​(​a​d​d​r​)​;​

 ​ ​ ​ ​i​n​t​ ​c​l​i​e​n​t​F​d​ ​=​ ​a​c​c​e​p​t​ ​(​l​i​s​t​e​n​_​f​d​,​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​ ​*​)​&​a​d​d​r​,​ ​&​a​d​d​r​_​l​e​n​)​;​

 ​ ​ ​ ​i​f​ ​(​c​l​i​e​n​t​F​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​*​*​*​ ​a​c​c​e​p​t​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​S​e​t​ ​t​o​ ​n​o​n​-​b​l​o​c​k​i​n​g​
 ​ ​ ​ ​i​n​t​ ​e​r​r​ ​=​ ​f​c​n​t​l​ ​(​c​l​i​e​n​t​F​d​,​ ​F​_​S​E​T​F​L​,​ ​O​_​N​O​N​B​L​O​C​K​)​;​
 ​ ​ ​ ​i​f​ ​(​e​r​r​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​*​*​*​ ​f​c​n​t​l​(​c​l​i​e​n​t​F​d​ ​O​_​N​O​N​B​L​O​C​K​)​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​F​i​n​d​ ​t​h​e​ ​n​e​x​t​ ​f​r​e​e​ ​s​p​o​t​ ​i​n​ ​t​h​e​ ​u​s​e​r​s​ ​a​r​r​a​y​
 ​ ​ ​ ​C​h​a​t​t​e​r​U​s​e​r​ ​*​n​e​w​U​s​e​r​ ​=​ ​N​U​L​L​;​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​M​A​X​_​U​S​E​R​S​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​s​_​U​s​e​r​s​[​i​]​.​f​d​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​e​w​U​s​e​r​ ​=​ ​&​s​_​U​s​e​r​s​[​i​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​i​f​ ​(​n​e​w​U​s​e​r​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​g​r​i​p​e​[​]​ ​=​ ​"​T​o​o​ ​m​a​n​y​ ​u​s​e​r​s​ ​-​ ​t​r​y​ ​a​g​a​i​n​ ​l​a​t​e​r​.​\​n​"​;​
 ​ ​ ​ ​ ​ ​ ​ ​w​r​i​t​e​ ​(​c​l​i​e​n​t​F​d​,​ ​g​r​i​p​e​,​ ​s​i​z​e​o​f​(​g​r​i​p​e​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​o​k​,​ ​c​l​e​a​r​ ​o​u​t​ ​t​h​e​ ​s​t​r​u​c​t​u​r​e​,​ ​a​n​d​ ​g​e​t​ ​i​t​ ​s​e​t​ ​u​p​
 ​ ​ ​ ​m​e​m​s​e​t​ ​(​n​e​w​U​s​e​r​,​ ​0​,​ ​s​i​z​e​o​f​(​C​h​a​t​t​e​r​U​s​e​r​)​)​;​

 ​ ​ ​ ​n​e​w​U​s​e​r​-​>​f​d​ ​=​ ​c​l​i​e​n​t​F​d​;​
 ​ ​ ​ ​c​l​i​e​n​t​F​d​ ​=​ ​-​1​;​ ​/​/​ ​D​o​n​'​t​ ​l​e​t​ ​f​u​n​c​t​i​o​n​ ​c​l​e​a​n​u​p​ ​c​l​o​s​e​ ​t​h​e​ ​f​d​.​

 ​ ​ ​ ​/​/​ ​l​o​g​ ​w​h​e​r​e​ ​t​h​e​ ​c​o​n​n​e​c​t​i​o​n​ ​i​s​ ​f​r​o​m​
 ​ ​ ​ ​v​o​i​d​ ​*​n​e​t​_​a​d​d​r​ ​=​ ​N​U​L​L​;​

 ​ ​ ​ ​i​n​_​p​o​r​t​_​t​ ​p​o​r​t​ ​=​ ​0​;​
 ​ ​ ​ ​i​f​ ​(​a​d​d​r​.​s​s​_​f​a​m​i​l​y​ ​=​=​ ​A​F​_​I​N​E​T​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​6​ ​*​s​i​n​6​ ​=​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​6​ ​*​)​&​a​d​d​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​n​e​t​_​a​d​d​r​ ​=​ ​&​s​i​n​6​-​>​s​i​n​6​_​a​d​d​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​o​r​t​ ​=​ ​s​i​n​6​-​>​s​i​n​6​_​p​o​r​t​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​*​s​i​n​ ​=​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​*​)​&​a​d​d​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​n​e​t​_​a​d​d​r​ ​=​ ​&​s​i​n​-​>​s​i​n​_​a​d​d​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​o​r​t​ ​=​ ​s​i​n​-​>​s​i​n​_​p​o​r​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​M​a​k​e​ ​i​t​ ​s​o​m​e​w​h​a​t​ ​h​u​m​a​n​ ​r​e​a​d​a​b​l​e​.​
 ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​I​N​E​T​6​_​A​D​D​R​S​T​R​L​E​N​]​;​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​n​a​m​e​ ​=​ ​i​n​e​t​_​n​t​o​p​ ​(​a​d​d​r​.​s​s​_​f​a​m​i​l​y​,​ ​n​e​t​_​a​d​d​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​u​f​f​e​r​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​)​;​

 ​ ​ ​ ​s​y​s​l​o​g​ ​(​L​O​G​_​N​O​T​I​C​E​,​ ​"​A​c​c​e​p​t​e​d​ ​c​o​n​n​e​c​t​i​o​n​ ​f​r​o​m​ ​%​s​ ​p​o​r​t​ ​%​d​ ​a​s​ ​f​d​ ​%​d​.​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​a​m​e​,​ ​p​o​r​t​,​ ​c​l​i​e​n​t​F​d​)​;​
b​a​i​l​o​u​t​:​
 ​ ​ ​ ​i​f​ ​(​c​l​i​e​n​t​F​d​ ​!​=​ ​-​1​)​ ​c​l​o​s​e​ ​(​c​l​i​e​n​t​F​d​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​;​
}​ ​ ​/​/​ ​A​c​c​e​p​t​C​o​n​n​e​c​t​i​o​n​

/​/​ ​s​e​n​d​ ​a​ ​m​e​s​s​a​g​e​ ​t​o​ ​a​l​l​ ​t​h​e​ ​s​i​g​n​e​d​-​i​n​ ​u​s​e​r​s​
s​t​a​t​i​c​ ​v​o​i​d​ ​B​r​o​a​d​c​a​s​t​M​e​s​s​a​g​e​F​r​o​m​U​s​e​r​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​m​e​s​s​a​g​e​,​ ​c​o​n​s​t​ ​C​h​a​t​t​e​r​U​s​e​r​ ​*​u​s​e​r​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​!​u​s​e​r​-​>​g​o​t​N​a​m​e​)​ ​r​e​t​u​r​n​;​

 ​ ​ ​ ​s​t​a​t​i​c​ ​c​o​n​s​t​ ​c​h​a​r​ ​s​e​p​a​r​a​t​o​r​[​]​ ​=​ ​"​:​ ​"​;​

 ​ ​ ​ ​/​/​ ​A​l​l​ ​m​e​s​s​a​g​e​s​ ​a​r​e​ ​e​x​p​e​c​t​e​d​ ​t​o​ ​h​a​v​e​ ​a​ ​t​e​r​m​i​n​a​t​i​n​g​ ​n​e​w​l​i​n​e​.​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​B​r​o​a​d​c​a​s​t​ ​m​e​s​s​a​g​e​:​ ​%​s​%​s​%​s​"​,​ ​u​s​e​r​-​>​n​a​m​e​,​ ​s​e​p​a​r​a​t​o​r​,​ ​m​e​s​s​a​g​e​)​;​

 ​ ​ ​ ​/​/​ ​u​s​e​ ​s​c​a​t​t​e​r​e​d​ ​w​r​i​t​e​s​ ​j​u​s​t​ ​f​o​r​ ​f​u​n​.​ ​B​e​c​a​u​s​e​ ​W​e​ ​C​a​n​.​
 ​ ​ ​ ​c​o​n​s​t​ ​s​t​r​u​c​t​ ​i​o​v​e​c​ ​i​o​v​e​c​t​o​r​[​]​ ​=​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​{​ ​(​c​h​a​r​ ​*​)​u​s​e​r​-​>​n​a​m​e​,​ ​s​t​r​l​e​n​(​u​s​e​r​-​>​n​a​m​e​)​ ​ ​ ​ ​}​,​
 ​ ​ ​ ​ ​ ​ ​ ​{​ ​(​c​h​a​r​ ​*​)​s​e​p​a​r​a​t​o​r​,​ ​ ​s​i​z​e​o​f​(​s​e​p​a​r​a​t​o​r​)​ ​-​ ​1​ ​}​,​ ​/​/​ ​o​m​i​t​ ​t​e​r​m​i​n​a​t​o​r​
 ​ ​ ​ ​ ​ ​ ​ ​{​ ​(​c​h​a​r​ ​*​)​m​e​s​s​a​g​e​,​ ​ ​ ​ ​s​t​r​l​e​n​(​m​e​s​s​a​g​e​)​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​;​
 ​ ​ ​ ​c​o​n​s​t​ ​i​n​t​ ​i​o​v​e​c​t​o​r​_​l​e​n​ ​=​ ​s​i​z​e​o​f​(​i​o​v​e​c​t​o​r​)​ ​/​ ​s​i​z​e​o​f​(​*​i​o​v​e​c​t​o​r​)​;​

 ​ ​ ​ ​/​/​ ​S​c​a​n​ ​t​h​r​o​u​g​h​ ​t​h​e​ ​u​s​e​r​s​ ​a​n​d​ ​s​e​n​d​ ​t​h​e​ ​m​e​s​a​g​e​.​
 ​ ​ ​ ​c​o​n​s​t​ ​C​h​a​t​t​e​r​U​s​e​r​ ​*​s​t​o​p​ ​=​ ​&​s​_​U​s​e​r​s​[​M​A​X​_​U​S​E​R​S​]​;​
 ​ ​ ​ ​f​o​r​ ​(​C​h​a​t​t​e​r​U​s​e​r​ ​*​u​ ​=​ ​s​_​U​s​e​r​s​;​ ​u​ ​<​ ​s​t​o​p​;​ ​u​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​u​-​>​f​d​ ​>​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​s​i​z​e​_​t​ ​n​w​r​i​t​e​ ​=​ ​w​r​i​t​e​v​ ​(​u​-​>​f​d​,​ ​i​o​v​e​c​t​o​r​,​ ​i​o​v​e​c​t​o​r​_​l​e​n​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​n​w​r​i​t​e​ ​=​=​ ​-​1​)​ ​p​e​r​r​o​r​ ​(​"​w​r​i​t​e​v​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​l​s​e​ ​f​p​r​i​n​t​f​(​s​t​d​e​r​r​,​ ​"​\​t​S​e​n​t​ ​\​"​%​s​\​"​ ​%​z​d​ ​b​y​t​e​s​\​n​"​,​ ​u​-​>​n​a​m​e​,​ ​n​w​r​i​t​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​
}​ ​ ​/​/​ ​B​r​o​a​d​c​a​s​t​M​e​s​s​a​g​e​F​r​o​m​U​s​e​r​

/​/​ ​u​s​e​r​ ​d​i​s​c​o​n​n​e​c​t​e​d​.​ ​ ​D​o​ ​a​n​y​ ​m​o​p​-​u​p​
s​t​a​t​i​c​ ​v​o​i​d​ ​D​i​s​c​o​n​n​e​c​t​U​s​e​r​(​C​h​a​t​t​e​r​U​s​e​r​ ​*​u​s​e​r​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​u​s​e​r​-​>​f​d​ ​>​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​l​o​s​e​ ​(​u​s​e​r​-​>​f​d​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​-​>​f​d​ ​=​ ​0​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​y​s​l​o​g​ ​(​L​O​G​_​N​O​T​I​C​E​,​ ​"​D​i​s​c​o​n​n​e​c​t​e​d​ ​u​s​e​r​ ​\​"​%​s​\​"​ ​o​n​ ​f​d​ ​%​d​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​-​>​g​o​t​N​a​m​e​?​ ​u​s​e​r​-​>​n​a​m​e​ ​:​ ​"​(​u​n​k​n​o​w​n​)​"​,​ ​u​s​e​r​-​>​f​d​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​b​r​o​a​d​c​a​s​t​ ​'​u​s​e​r​ ​d​i​s​c​o​n​n​e​c​t​e​d​'​ ​m​e​s​s​a​g​e​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​u​s​e​r​-​>​g​o​t​N​a​m​e​)​ ​B​r​o​a​d​c​a​s​t​M​e​s​s​a​g​e​F​r​o​m​U​s​e​r​(​"​h​a​s​ ​l​e​f​t​ ​t​h​e​ ​c​h​a​n​n​e​l​.​\​n​"​,​ ​u​s​e​r​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​u​s​e​r​-​>​g​o​t​N​a​m​e​ ​=​ ​f​a​l​s​e​;​
 ​ ​ ​ ​u​s​e​r​-​>​b​y​t​e​s​R​e​a​d​ ​=​ ​0​;​
 ​ ​ ​ ​u​s​e​r​-​>​b​u​f​f​e​r​[​0​]​ ​=​ ​0​;​

}​ ​/​/​ ​D​i​s​c​o​n​n​e​c​t​U​s​e​r​

/​/​ ​t​h​e​ ​f​i​r​s​t​ ​p​a​c​k​e​t​ ​i​s​ ​t​h​e​ ​u​s​e​r​'​s​ ​n​a​m​e​.​ ​ ​G​e​t​ ​i​t​.​
s​t​a​t​i​c​ ​v​o​i​d​ ​R​e​a​d​U​s​e​r​n​a​m​e​(​C​h​a​t​t​e​r​U​s​e​r​ ​*​u​s​e​r​)​ ​{​
 ​ ​ ​ ​/​/​ ​s​e​e​ ​i​f​ ​w​e​ ​h​a​v​e​ ​r​e​a​d​ ​a​n​y​t​h​i​n​g​ ​y​e​t​
 ​ ​ ​ ​i​f​ ​(​u​s​e​r​-​>​b​y​t​e​s​R​e​a​d​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​R​e​a​d​ ​t​h​e​ ​l​e​n​g​t​h​ ​b​y​t​e​.​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​s​i​z​e​_​t​ ​t​o​r​e​a​d​ ​=​ ​s​i​z​e​o​f​ ​(​u​s​e​r​-​>​b​u​f​f​e​r​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​s​i​z​e​_​t​ ​n​r​e​a​d​ ​=​ ​r​e​a​d​ ​(​u​s​e​r​-​>​f​d​,​ ​u​s​e​r​-​>​b​u​f​f​e​r​,​ ​t​o​r​e​a​d​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​n​r​e​a​d​ ​=​=​ ​t​o​r​e​a​d​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​w​e​ ​g​o​t​ ​o​u​r​ ​l​e​n​g​t​h​ ​b​y​t​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​-​>​b​y​t​e​s​R​e​a​d​ ​+​=​ ​n​r​e​a​d​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​s​i​z​e​_​t​ ​n​a​m​e​l​e​n​ ​=​ ​(​u​i​n​t​8​_​t​)​u​s​e​r​-​>​b​u​f​f​e​r​[​0​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​n​a​m​e​l​e​n​ ​>​=​ ​s​i​z​e​o​f​(​u​s​e​r​-​>​n​a​m​e​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​a​t​i​c​ ​c​o​n​s​t​ ​c​h​a​r​ ​b​a​d​N​a​m​e​M​e​s​s​a​g​e​[​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​=​ ​"​E​r​r​o​r​:​ ​U​s​e​r​n​a​m​e​ ​m​u​s​t​ ​b​e​ ​8​ ​c​h​a​r​a​c​t​e​r​s​ ​o​r​ ​f​e​w​e​r​.​\​n​"​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​r​i​t​e​ ​(​u​s​e​r​-​>​f​d​,​ ​b​a​d​N​a​m​e​M​e​s​s​a​g​e​,​ ​s​i​z​e​o​f​(​b​a​d​N​a​m​e​M​e​s​s​a​g​e​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​D​i​s​c​o​n​n​e​c​t​U​s​e​r​ ​(​u​s​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​n​r​e​a​d​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​e​n​d​ ​o​f​ ​f​i​l​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​D​i​s​c​o​n​n​e​c​t​U​s​e​r​ ​(​u​s​e​r​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​n​r​e​a​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​(​"​r​e​a​d​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​D​i​s​c​o​n​n​e​c​t​U​s​e​r​ ​(​u​s​e​r​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​S​h​o​u​l​d​ ​n​o​t​ ​h​a​v​e​ ​r​e​a​c​h​e​d​ ​l​i​n​e​ ​%​d​.​ ​ ​n​r​e​a​d​:​ ​%​z​u​ ​t​o​r​e​a​d​:​ ​%​z​u​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​_​_​L​I​N​E​_​_​,​ ​n​r​e​a​d​,​ ​t​o​r​e​a​d​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​o​k​,​ ​t​r​y​ ​t​o​ ​r​e​a​d​ ​j​u​s​t​ ​t​h​e​ ​r​e​s​t​ ​o​f​ ​t​h​e​ ​u​s​e​r​n​a​m​e​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​u​i​n​t​8​_​t​ ​n​a​m​e​l​e​n​ ​=​ ​(​u​i​n​t​8​_​t​)​u​s​e​r​-​>​b​u​f​f​e​r​[​0​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​s​i​z​e​_​t​ ​p​a​c​k​e​t​l​e​n​ ​=​ ​s​i​z​e​o​f​(​n​a​m​e​l​e​n​)​ ​+​ ​n​a​m​e​l​e​n​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​s​i​z​e​_​t​ ​n​l​e​f​t​ ​=​ ​p​a​c​k​e​t​l​e​n​ ​-​ ​u​s​e​r​-​>​b​y​t​e​s​R​e​a​d​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​s​i​z​e​_​t​ ​n​r​e​a​d​ ​=​ ​r​e​a​d​ ​(​u​s​e​r​-​>​f​d​,​ ​u​s​e​r​-​>​b​u​f​f​e​r​ ​+​ ​u​s​e​r​-​>​b​y​t​e​s​R​e​a​d​,​ ​n​l​e​f​t​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​s​w​i​t​c​h​ ​(​n​r​e​a​d​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​f​a​u​l​t​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​-​>​b​y​t​e​s​R​e​a​d​ ​+​=​ ​n​r​e​a​d​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​0​:​ ​ ​/​/​ ​p​e​e​r​ ​c​l​o​s​e​d​ ​t​h​e​ ​c​o​n​n​e​c​t​i​o​n​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​D​i​s​c​o​n​n​e​c​t​U​s​e​r​ ​(​u​s​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​-​1​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​R​e​a​d​N​a​m​e​:​ ​r​e​a​d​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​D​i​s​c​o​n​n​e​c​t​U​s​e​r​ ​(​u​s​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​D​o​ ​w​e​ ​h​a​v​e​ ​t​h​e​ ​n​a​m​e​?​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​u​s​e​r​-​>​b​y​t​e​s​R​e​a​d​ ​>​ ​n​a​m​e​l​e​n​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​-​>​g​o​t​N​a​m​e​ ​=​ ​t​r​u​e​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​o​p​y​ ​u​s​e​r​n​a​m​e​ ​i​n​t​o​ ​t​h​e​ ​U​s​e​r​ ​s​t​r​u​c​t​u​r​e​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​m​e​m​c​p​y​ ​(​u​s​e​r​-​>​n​a​m​e​,​ ​&​u​s​e​r​-​>​b​u​f​f​e​r​[​1​]​,​ ​n​a​m​e​l​e​n​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​-​>​n​a​m​e​[​n​a​m​e​l​e​n​]​ ​=​ ​'​\​0​'​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​R​e​c​e​i​v​e​d​ ​u​s​e​r​n​a​m​e​:​ ​%​s​\​n​"​,​ ​u​s​e​r​-​>​n​a​m​e​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​n​o​ ​c​u​r​r​e​n​t​ ​m​e​s​s​a​g​e​,​ ​s​o​ ​c​l​e​a​r​ ​i​t​ ​o​u​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​-​>​b​u​f​f​e​r​[​0​]​ ​=​ ​0​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​-​>​b​y​t​e​s​R​e​a​d​ ​-​=​ ​p​a​c​k​e​t​l​e​n​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​y​s​l​o​g​ ​(​L​O​G​_​N​O​T​I​C​E​,​ ​"​U​s​e​r​n​a​m​e​ ​f​o​r​ ​f​d​ ​%​d​ ​i​s​ ​%​s​.​"​,​ ​u​s​e​r​-​>​f​d​,​ ​u​s​e​r​-​>​n​a​m​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​B​r​o​a​d​c​a​s​t​M​e​s​s​a​g​e​F​r​o​m​U​s​e​r​ ​(​"​h​a​s​ ​j​o​i​n​e​d​ ​t​h​e​ ​c​h​a​n​n​e​l​.​\​n​"​,​ ​u​s​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​
}​ ​ ​/​/​ ​R​e​a​d​U​s​e​r​n​a​m​e​

/​/​ ​G​e​t​ ​m​e​s​s​a​g​e​ ​d​a​t​a​ ​f​r​o​m​ ​t​h​e​ ​g​i​v​e​n​ ​u​s​e​r​
s​t​a​t​i​c​ ​v​o​i​d​ ​R​e​a​d​M​e​s​s​a​g​e​ ​(​C​h​a​t​t​e​r​U​s​e​r​ ​*​u​s​e​r​)​ ​{​
 ​ ​ ​ ​/​/​ ​r​e​a​d​ ​a​s​ ​m​u​c​h​ ​a​s​ ​w​e​ ​c​a​n​ ​i​n​t​o​ ​t​h​e​ ​b​u​f​f​e​r​
 ​ ​ ​ ​c​o​n​s​t​ ​s​i​z​e​_​t​ ​t​o​r​e​a​d​ ​=​ ​s​i​z​e​o​f​(​u​s​e​r​-​>​b​u​f​f​e​r​)​ ​-​ ​u​s​e​r​-​>​b​y​t​e​s​R​e​a​d​;​
 ​ ​ ​ ​s​s​i​z​e​_​t​ ​n​r​e​a​d​ ​=​ ​r​e​a​d​ ​(​u​s​e​r​-​>​f​d​,​ ​u​s​e​r​-​>​b​u​f​f​e​r​ ​+​ ​u​s​e​r​-​>​b​y​t​e​s​R​e​a​d​,​ ​t​o​r​e​a​d​)​;​

 ​ ​ ​ ​s​w​i​t​c​h​ ​(​n​r​e​a​d​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​d​e​f​a​u​l​t​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​-​>​b​y​t​e​s​R​e​a​d​ ​+​=​ ​n​r​e​a​d​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​0​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​D​i​s​c​o​n​n​e​c​t​U​s​e​r​ ​(​u​s​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​-​1​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​R​e​a​d​M​e​s​s​a​g​e​:​ ​r​e​a​d​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​S​e​n​d​ ​a​n​y​ ​c​o​m​p​l​e​t​e​ ​m​e​s​s​a​g​e​s​.​ ​T​h​e​ ​f​i​r​s​t​ ​b​y​t​e​ ​i​n​ ​t​h​e​ ​b​u​f​f​e​r​ ​i​s​
 ​ ​ ​ ​/​/​ ​a​l​w​a​y​s​ ​t​h​e​ ​l​e​n​g​t​h​ ​b​y​t​e​.​
 ​ ​ ​ ​c​h​a​r​ ​m​s​g​b​u​f​[​M​A​X​_​M​E​S​S​A​G​E​_​S​I​Z​E​ ​+​ ​1​]​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​u​s​e​r​-​>​b​y​t​e​s​R​e​a​d​ ​>​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​u​i​n​t​8​_​t​ ​m​s​g​l​e​n​ ​=​ ​(​u​i​n​t​8​_​t​)​u​s​e​r​-​>​b​u​f​f​e​r​[​0​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​O​n​l​y​ ​a​ ​p​a​r​t​i​a​l​ ​m​e​s​s​a​g​e​ ​l​e​f​t​?​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​u​s​e​r​-​>​b​y​t​e​s​R​e​a​d​ ​<​=​ ​m​s​g​l​e​n​)​ ​b​r​e​a​k​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​c​o​p​y​ ​m​e​s​s​a​g​e​ ​t​o​ ​b​u​f​f​e​r​ ​a​n​d​ ​n​u​l​l​-​t​e​r​m​i​n​a​t​e​
 ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​*​m​s​g​ ​=​ ​&​u​s​e​r​-​>​b​u​f​f​e​r​[​1​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​m​e​m​c​p​y​ ​(​m​s​g​b​u​f​,​ ​m​s​g​,​ ​m​s​g​l​e​n​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​m​s​g​b​u​f​[​m​s​g​l​e​n​]​ ​=​ ​'​\​0​'​;​
 ​ ​ ​ ​ ​ ​ ​ ​B​r​o​a​d​c​a​s​t​M​e​s​s​a​g​e​F​r​o​m​U​s​e​r​ ​(​m​s​g​b​u​f​,​ ​u​s​e​r​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​S​l​i​d​e​ ​t​h​e​ ​r​e​s​t​ ​o​f​ ​t​h​e​ ​d​a​t​a​ ​o​v​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​s​i​z​e​_​t​ ​p​a​c​k​e​t​l​e​n​ ​=​ ​s​i​z​e​o​f​(​m​s​g​l​e​n​)​ ​+​ ​m​s​g​l​e​n​;​
 ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​-​>​b​y​t​e​s​R​e​a​d​ ​-​=​ ​p​a​c​k​e​t​l​e​n​;​
 ​ ​ ​ ​ ​ ​ ​ ​m​e​m​m​o​v​e​ ​(​u​s​e​r​-​>​b​u​f​f​e​r​,​ ​m​s​g​ ​+​ ​m​s​g​l​e​n​,​ ​u​s​e​r​-​>​b​y​t​e​s​R​e​a​d​)​;​
 ​ ​ ​ ​}​

}​ ​ ​/​/​ ​R​e​a​d​M​e​s​s​a​g​e​

/​/​ ​w​e​ ​g​o​t​ ​r​e​a​d​ ​a​c​t​i​v​i​t​y​ ​f​o​r​ ​a​ ​u​s​e​r​
s​t​a​t​i​c​ ​v​o​i​d​ ​H​a​n​d​l​e​R​e​a​d​ ​(​C​h​a​t​t​e​r​U​s​e​r​ ​*​u​s​e​r​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​!​u​s​e​r​-​>​g​o​t​N​a​m​e​)​ ​R​e​a​d​U​s​e​r​n​a​m​e​(​u​s​e​r​)​;​
 ​ ​ ​ ​e​l​s​e​ ​R​e​a​d​M​e​s​s​a​g​e​(​u​s​e​r​)​;​
}​ ​ ​/​/​ ​H​a​n​d​l​e​R​e​a​d​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​e​x​i​t​_​s​t​a​t​u​s​ ​=​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​c​o​n​s​t​ ​b​o​o​l​ ​u​s​e​I​P​v​6​ ​=​ ​(​a​r​g​c​ ​>​ ​1​)​;​
 ​ ​ ​ ​i​n​t​ ​l​i​s​t​e​n​F​d​ ​=​ ​S​t​a​r​t​L​i​s​t​e​n​i​n​g​ ​(​u​s​e​I​P​v​6​)​;​

 ​ ​ ​ ​i​f​ ​(​l​i​s​t​e​n​F​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​*​*​*​ ​C​o​u​l​d​ ​n​o​t​ ​o​p​e​n​ ​l​i​s​t​e​n​i​n​g​ ​s​o​c​k​e​t​.​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​B​l​o​c​k​ ​S​I​G​P​I​P​E​ ​s​o​ ​a​ ​d​r​o​p​p​e​d​ ​c​o​n​n​e​c​t​i​o​n​ ​w​o​n​'​t​ ​s​i​g​n​a​l​ ​u​s​.​
 ​ ​ ​ ​s​t​r​u​c​t​ ​s​i​g​a​c​t​i​o​n​ ​a​c​t​;​
 ​ ​ ​ ​a​c​t​.​s​a​_​h​a​n​d​l​e​r​ ​=​ ​S​I​G​_​I​G​N​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​s​i​g​a​c​t​i​o​n​ ​o​a​c​t​;​
 ​ ​ ​ ​i​n​t​ ​e​r​r​ ​=​ ​s​i​g​a​c​t​i​o​n​ ​(​S​I​G​P​I​P​E​,​ ​&​a​c​t​,​ ​&​o​a​c​t​)​;​
 ​ ​ ​ ​i​f​ ​(​e​r​r​ ​=​=​ ​-​1​)​ ​p​e​r​r​o​r​ ​(​"​s​i​g​a​c​t​i​o​n​(​S​I​G​P​I​P​E​,​ ​S​I​G​_​I​G​N​)​"​)​;​

 ​ ​ ​ ​/​/​ ​w​a​i​t​ ​f​o​r​ ​a​c​t​i​v​i​t​y​
 ​ ​ ​ ​w​h​i​l​e​ ​(​t​r​u​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​d​_​s​e​t​ ​r​e​a​d​f​d​s​;​
 ​ ​ ​ ​ ​ ​ ​ ​F​D​_​Z​E​R​O​ ​(​&​r​e​a​d​f​d​s​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​A​d​d​ ​t​h​e​ ​l​i​s​t​e​n​ ​s​o​c​k​e​t​
 ​ ​ ​ ​ ​ ​ ​ ​F​D​_​S​E​T​(​l​i​s​t​e​n​F​d​,​ ​&​r​e​a​d​f​d​s​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​m​a​x​_​f​d​ ​=​ ​M​A​X​ ​(​m​a​x​_​f​d​,​ ​l​i​s​t​e​n​F​d​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​A​d​d​ ​t​h​e​ ​u​s​e​r​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​M​A​X​_​U​S​E​R​S​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​i​n​t​ ​u​s​e​r​_​f​d​ ​=​ ​s​_​U​s​e​r​s​[​i​]​.​f​d​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​u​s​e​r​_​f​d​ ​<​=​ ​0​)​ ​c​o​n​t​i​n​u​e​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​F​D​_​S​E​T​ ​(​u​s​e​r​_​f​d​,​ ​&​r​e​a​d​f​d​s​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​m​a​x​_​f​d​ ​=​ ​M​A​X​ ​(​m​a​x​_​f​d​,​ ​u​s​e​r​_​f​d​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​a​i​t​ ​u​n​t​i​l​ ​s​o​m​e​t​h​i​n​g​ ​i​n​t​e​r​e​s​t​i​n​g​ ​h​a​p​p​e​n​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​n​r​e​a​d​y​ ​=​ ​s​e​l​e​c​t​ ​(​m​a​x​_​f​d​ ​+​ ​1​,​ ​&​r​e​a​d​f​d​s​,​ ​N​U​L​L​,​ ​N​U​L​L​,​ ​N​U​L​L​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​n​r​e​a​d​y​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​s​e​l​e​c​t​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​i​n​u​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​S​e​e​ ​i​f​ ​a​ ​n​e​w​ ​u​s​e​r​ ​i​s​ ​k​n​o​c​k​i​n​g​ ​o​n​ ​o​u​r​ ​d​o​o​r​.​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​F​D​_​I​S​S​E​T​(​l​i​s​t​e​n​F​d​,​ ​&​r​e​a​d​f​d​s​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​A​c​c​e​p​t​C​o​n​n​e​c​t​i​o​n​ ​(​l​i​s​t​e​n​F​d​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​H​a​n​d​l​e​ ​a​n​y​ ​n​e​w​ ​i​n​c​o​m​i​n​g​ ​d​a​t​a​ ​f​r​o​m​ ​t​h​e​ ​u​s​e​r​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​l​o​s​e​s​ ​a​p​p​e​a​r​ ​h​e​r​e​ ​t​o​o​.​
 ​ ​ ​ ​ ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​M​A​X​_​U​S​E​R​S​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​h​a​t​t​e​r​U​s​e​r​ ​*​c​o​n​s​t​ ​u​s​e​r​ ​=​ ​&​s​_​U​s​e​r​s​[​i​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​u​s​e​r​-​>​f​d​ ​>​=​ ​0​ ​&​&​ ​F​D​_​I​S​S​E​T​(​u​s​e​r​-​>​f​d​,​ ​&​r​e​a​d​f​d​s​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​H​a​n​d​l​e​R​e​a​d​ ​(​u​s​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​
 ​ ​ ​ ​e​x​i​t​_​s​t​a​t​u​s​ ​=​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​r​e​t​u​r​n​ ​e​x​i​t​_​s​t​a​t​u​s​;​
}​ ​ ​/​/​ ​m​a​i​n​

The client program (Example 14.6) also uses select() because it needs to be able to read from both the terminal (messages from the user) and the server (messages sent by other users). The protocol from server to client is much simpler: the server just sends the string’s characters without any size information.

This compile-time assert is the same general idea as shown in Chapter 5: Exceptions, Error Handling, and Signals:

t​y​p​e​d​e​f​ ​u​i​n​t​8​_​t​ ​R​E​A​D​_​B​U​F​F​E​R​_​S​I​Z​E​_​n​o​t​_​l​e​s​s​_​t​h​a​n​_​M​A​X​_​M​E​S​S​A​G​E​_​S​I​Z​E​
 ​ ​ ​ ​[​!​(​R​E​A​D​_​B​U​F​F​E​R​_​S​I​Z​E​ ​<​ ​M​A​X​_​M​E​S​S​A​G​E​_​S​I​Z​E​)​ ​?​ ​0​ ​:​ ​-​1​]​;​

The message parsing code assumes that it can process an entire message in the read buffer. If the read buffer is reduced accidentally during program maintenance, chatterserver might read or write off the end of a buffer, introducing bugs and security issues. If READ_BUFFER_SIZE is less than MAX_MESSAGE_SIZE, a static array of length negative-one is created, yielding a compiler error:

e​r​r​o​r​:​ ​s​i​z​e​ ​o​f​ ​a​r​r​a​y​ ​‘​R​E​A​D​_​B​U​F​F​E​R​_​S​I​Z​E​_​n​o​t​_​l​e​s​s​_​t​h​a​n​_​M​A​X​_​M​E​S​S​A​G​E​_​S​I​Z​E​’​ ​i​s​ ​n​e​g​a​t​i​v​e​

Example 14.6 is the user-facing portion of the chatter system:

Example 14.6. chatterclient.m

/​/​ ​c​h​a​t​t​e​r​c​l​i​e​n​t​.​m​ ​-​-​ ​c​l​i​e​n​t​ ​s​i​d​e​ ​o​f​ ​t​h​e​ ​c​h​a​t​t​e​r​ ​w​o​r​l​d​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​s​t​d​=​c​9​9​ ​-​o​ ​c​h​a​t​t​e​r​c​l​i​e​n​t​ ​c​h​a​t​t​e​r​c​l​i​e​n​t​.​m​

#​i​m​p​o​r​t​ ​<​s​t​d​b​o​o​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​t​r​u​e​/​f​a​l​s​e​
#​i​m​p​o​r​t​ ​<​s​t​d​i​n​t​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​U​I​N​T​8​_​M​A​X​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​s​t​r​e​r​r​o​r​(​)​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​f​c​n​t​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​c​n​t​l​(​)​
#​i​m​p​o​r​t​ ​<​m​a​c​h​/​v​m​_​p​a​r​a​m​.​h​>​ ​ ​/​/​ ​P​A​G​E​_​S​I​Z​E​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​c​l​o​s​e​(​)​

#​i​m​p​o​r​t​ ​<​a​r​p​a​/​i​n​e​t​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​i​n​e​t​_​n​t​o​p​(​)​
#​i​m​p​o​r​t​ ​<​n​e​t​d​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​g​e​t​h​o​s​t​b​y​n​a​m​e​2​(​)​
#​i​m​p​o​r​t​ ​<​n​e​t​i​n​e​t​/​i​n​.​h​>​ ​ ​ ​ ​ ​/​/​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​
#​i​m​p​o​r​t​ ​<​n​e​t​i​n​e​t​6​/​i​n​6​.​h​>​ ​ ​ ​/​/​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​6​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​o​c​k​e​t​.​h​>​ ​ ​ ​ ​ ​/​/​ ​s​o​c​k​e​t​(​)​,​ ​A​F​_​I​N​E​T​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​r​a​n​d​o​m​ ​t​y​p​e​s​

/​/​ ​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
/​/​ ​M​e​s​s​a​g​e​ ​p​r​o​t​o​c​o​l​

/​/​ ​A​ ​m​e​s​s​a​g​e​ ​i​s​ ​l​e​n​g​t​h​ ​+​ ​d​a​t​a​.​ ​T​h​e​ ​l​e​n​g​t​h​ ​i​s​ ​a​ ​s​i​n​g​l​e​ ​b​y​t​e​.​
/​/​ ​T​h​e​ ​f​i​r​s​t​ ​m​e​s​s​a​g​e​ ​s​e​n​t​ ​b​y​ ​a​ ​u​s​e​r​ ​h​a​s​ ​a​ ​m​a​x​ ​l​e​n​g​t​h​ ​o​f​ ​8​
/​/​ ​a​n​d​ ​s​e​t​s​ ​t​h​e​ ​u​s​e​r​'​s​ ​n​a​m​e​.​

#​d​e​f​i​n​e​ ​M​A​X​(​x​,​ ​y​)​ ​(​(​(​x​)​ ​>​ ​(​y​)​)​?​ ​(​x​)​ ​:​ ​(​y​)​)​
#​d​e​f​i​n​e​ ​M​A​X​_​M​E​S​S​A​G​E​_​S​I​Z​E​ ​ ​(​U​I​N​T​8​_​M​A​X​)​
#​d​e​f​i​n​e​ ​R​E​A​D​_​B​U​F​F​E​R​_​S​I​Z​E​ ​ ​(​P​A​G​E​_​S​I​Z​E​)​

s​t​a​t​i​c​ ​c​o​n​s​t​ ​i​n​_​p​o​r​t​_​t​ ​k​P​o​r​t​N​u​m​b​e​r​ ​=​ ​2​3​4​2​;​

/​/​ ​S​e​e​ ​s​i​m​p​l​e​c​l​i​e​n​t​.​m​ ​f​o​r​ ​t​h​e​ ​d​e​f​i​n​i​t​i​o​n​ ​o​f​ ​t​h​e​s​e​ ​t​w​o​ ​f​u​n​c​t​i​o​n​s​.​
s​t​a​t​i​c​ ​i​n​t​ ​S​o​c​k​e​t​C​o​n​n​e​c​t​e​d​T​o​H​o​s​t​N​a​m​e​d​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​h​o​s​t​n​a​m​e​)​;​
s​t​a​t​i​c​ ​b​o​o​l​ ​G​e​t​A​d​d​r​e​s​s​A​t​I​n​d​e​x​ ​(​s​t​r​u​c​t​ ​h​o​s​t​e​n​t​ ​*​h​o​s​t​,​ ​i​n​t​ ​a​d​d​r​e​s​s​I​n​d​e​x​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​s​t​o​r​a​g​e​ ​*​o​u​t​S​e​r​v​e​r​A​d​d​r​e​s​s​)​;​

/​/​ ​R​e​t​u​r​n​s​ ​-​1​ ​o​n​ ​f​a​i​l​u​r​e​,​ ​>​=​ ​0​ ​o​n​ ​s​u​c​c​e​s​s​.​
s​t​a​t​i​c​ ​i​n​t​ ​W​r​i​t​e​M​e​s​s​a​g​e​ ​(​i​n​t​ ​f​d​,​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​b​u​f​f​e​r​,​ ​s​i​z​e​_​t​ ​l​e​n​g​t​h​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​l​e​n​g​t​h​ ​>​ ​M​A​X​_​M​E​S​S​A​G​E​_​S​I​Z​E​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​*​*​*​ ​T​r​u​n​c​a​t​i​n​g​ ​m​e​s​s​a​g​e​ ​t​o​ ​%​d​ ​b​y​t​e​s​.​\​n​"​,​ ​M​A​X​_​M​E​S​S​A​G​E​_​S​I​Z​E​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​l​e​n​g​t​h​ ​=​ ​M​A​X​_​M​E​S​S​A​G​E​_​S​I​Z​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​S​s​e​n​d​ ​t​h​e​ ​l​e​n​g​t​h​ ​b​y​t​e​ ​f​i​r​s​t​
 ​ ​ ​ ​u​i​n​t​8​_​t​ ​b​y​t​e​s​L​e​f​t​ ​=​ ​(​u​i​n​t​8​_​t​)​l​e​n​g​t​h​;​
 ​ ​ ​ ​s​s​i​z​e​_​t​ ​n​w​r​i​t​t​e​n​ ​=​ ​w​r​i​t​e​ ​(​f​d​,​ ​&​b​y​t​e​s​L​e​f​t​,​ ​s​i​z​e​o​f​(​b​y​t​e​s​L​e​f​t​)​)​;​

 ​ ​ ​ ​i​f​ ​(​n​w​r​i​t​t​e​n​ ​<​=​ ​0​)​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​

 ​ ​ ​ ​/​/​ ​T​h​e​n​,​ ​s​e​n​d​ ​t​h​e​ ​s​t​r​i​n​g​ ​b​y​t​e​s​.​
 ​ ​ ​ ​w​h​i​l​e​ ​(​b​y​t​e​s​L​e​f​t​ ​>​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​n​w​r​i​t​t​e​n​ ​=​ ​w​r​i​t​e​(​f​d​,​ ​b​u​f​f​e​r​,​ ​b​y​t​e​s​L​e​f​t​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​n​w​r​i​t​t​e​n​ ​<​=​ ​0​)​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​

 ​ ​ ​ ​ ​ ​ ​ ​b​y​t​e​s​L​e​f​t​ ​-​=​ ​n​w​r​i​t​t​e​n​;​
 ​ ​ ​ ​ ​ ​ ​ ​b​u​f​f​e​r​ ​+​=​ ​n​w​r​i​t​t​e​n​;​
 ​ ​ ​ ​}​
b​a​i​l​o​u​t​:​
 ​ ​ ​ ​i​f​ ​(​n​w​r​i​t​t​e​n​ ​=​=​ ​-​1​)​ ​p​e​r​r​o​r​(​"​w​r​i​t​e​"​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​n​w​r​i​t​t​e​n​;​
}​ ​/​/​ ​W​r​i​t​e​M​e​s​s​a​g​e​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​e​x​i​t​_​s​t​a​t​u​s​ ​=​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​

 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​!​=​ ​3​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​(​s​t​d​e​r​r​,​ ​"​U​s​a​g​e​:​ ​c​h​a​t​t​e​r​c​l​i​e​n​t​ ​h​o​s​t​n​a​m​e​ ​u​s​e​r​n​a​m​e​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​l​i​m​i​t​ ​u​s​e​r​n​a​m​e​ ​t​o​ ​8​ ​c​h​a​r​a​c​t​e​r​s​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​n​a​m​e​ ​=​ ​a​r​g​v​[​2​]​;​
 ​ ​ ​ ​s​i​z​e​_​t​ ​n​a​m​e​l​e​n​ ​=​ ​s​t​r​l​e​n​(​n​a​m​e​)​;​
 ​ ​ ​ ​i​f​ ​(​n​a​m​e​l​e​n​ ​>​ ​8​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​*​*​*​ ​U​s​e​r​n​a​m​e​ ​m​u​s​t​ ​b​e​ ​8​ ​c​h​a​r​a​c​t​e​r​s​ ​o​r​ ​f​e​w​e​r​.​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​s​e​t​ ​s​t​d​i​n​ ​t​o​ ​n​o​n​-​b​l​o​c​k​i​n​g​
 ​ ​ ​ ​i​n​t​ ​e​r​r​ ​=​ ​f​c​n​t​l​(​S​T​D​I​N​_​F​I​L​E​N​O​,​ ​F​_​S​E​T​F​L​,​ ​O​_​N​O​N​B​L​O​C​K​)​;​
 ​ ​ ​ ​i​f​ ​(​e​r​r​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​f​c​n​t​l​(​s​t​d​i​n​ ​O​_​N​O​N​B​L​O​C​K​)​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​a​ ​c​o​n​n​e​c​t​e​d​ ​s​o​c​k​e​t​.​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​h​o​s​t​n​a​m​e​ ​=​ ​a​r​g​v​[​1​]​;​
 ​ ​ ​ ​i​n​t​ ​s​e​r​v​e​r​F​d​ ​=​ ​S​o​c​k​e​t​C​o​n​n​e​c​t​e​d​T​o​H​o​s​t​N​a​m​e​d​ ​(​h​o​s​t​n​a​m​e​)​;​
 ​ ​ ​ ​i​f​ ​(​s​e​r​v​e​r​F​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​*​*​*​ ​U​n​a​b​l​e​ ​t​o​ ​c​o​n​n​e​c​t​ ​t​o​ ​%​s​ ​p​o​r​t​ ​%​d​.​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​o​s​t​n​a​m​e​,​ ​(​i​n​t​)​k​P​o​r​t​N​u​m​b​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​[​c​o​n​n​e​c​t​e​d​ ​t​o​ ​%​s​ ​p​o​r​t​ ​%​d​]​\​n​"​,​ ​h​o​s​t​n​a​m​e​,​ ​(​i​n​t​)​k​P​o​r​t​N​u​m​b​e​r​)​;​
 ​ ​ ​ ​/​/​ ​n​o​ ​n​e​e​d​ ​t​o​ ​b​i​n​d​(​)​ ​o​r​ ​l​i​s​t​e​n​(​)​

 ​ ​ ​ ​i​n​t​ ​n​w​r​i​t​t​e​n​ ​=​ ​W​r​i​t​e​M​e​s​s​a​g​e​ ​(​s​e​r​v​e​r​F​d​,​ ​n​a​m​e​,​ ​n​a​m​e​l​e​n​)​;​
 ​ ​ ​ ​i​f​ ​(​n​w​r​i​t​t​e​n​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​(​"​*​*​*​ ​U​n​a​b​l​e​ ​t​o​ ​w​r​i​t​e​ ​u​s​e​r​n​a​m​e​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​N​o​w​ ​s​e​t​ ​t​o​ ​n​o​n​-​b​l​o​c​k​ ​s​o​ ​w​e​ ​c​a​n​ ​i​n​t​e​r​l​e​a​v​e​ ​s​t​d​i​n​ ​a​n​d​ ​m​e​s​s​a​g​e​s​ ​f​r​o​m​ ​t​h​e​ ​s​e​r​v​e​r​.​
 ​ ​ ​ ​e​r​r​ ​=​ ​f​c​n​t​l​ ​(​s​e​r​v​e​r​F​d​,​ ​F​_​S​E​T​F​L​,​ ​O​_​N​O​N​B​L​O​C​K​)​;​
 ​ ​ ​ ​i​f​ ​(​e​r​r​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​f​c​n​t​l​(​s​e​r​v​e​r​F​d​ ​O​_​N​O​N​B​L​O​C​K​)​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​c​h​a​r​ ​i​n​c​o​m​i​n​g​B​u​f​f​e​r​[​R​E​A​D​_​B​U​F​F​E​R​_​S​I​Z​E​]​;​
 ​ ​ ​ ​c​h​a​r​ ​m​e​s​s​a​g​e​B​u​f​f​e​r​[​M​A​X​_​M​E​S​S​A​G​E​_​S​I​Z​E​]​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​t​r​u​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​d​_​s​e​t​ ​r​e​a​d​f​d​s​;​
 ​ ​ ​ ​ ​ ​ ​ ​F​D​_​Z​E​R​O​ ​(​&​r​e​a​d​f​d​s​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​F​D​_​S​E​T​ ​(​S​T​D​I​N​_​F​I​L​E​N​O​,​ ​&​r​e​a​d​f​d​s​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​F​D​_​S​E​T​ ​(​s​e​r​v​e​r​F​d​,​ ​&​r​e​a​d​f​d​s​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​m​a​x​_​f​d​ ​=​ ​M​A​X​ ​(​S​T​D​I​N​_​F​I​L​E​N​O​,​ ​s​e​r​v​e​r​F​d​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​n​r​e​a​d​y​ ​=​ ​s​e​l​e​c​t​ ​(​m​a​x​_​f​d​ ​+​ ​1​,​ ​&​r​e​a​d​f​d​s​,​ ​N​U​L​L​,​ ​N​U​L​L​,​ ​N​U​L​L​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​n​r​e​a​d​y​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​(​"​s​e​l​e​c​t​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​i​n​u​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​h​e​c​k​ ​s​t​a​n​d​a​r​d​-​i​n​.​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​F​D​_​I​S​S​E​T​(​S​T​D​I​N​_​F​I​L​E​N​O​,​ ​&​r​e​a​d​f​d​s​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​n​r​e​a​d​ ​=​ ​r​e​a​d​ ​(​S​T​D​I​N​_​F​I​L​E​N​O​,​ ​m​e​s​s​a​g​e​B​u​f​f​e​r​,​ ​s​i​z​e​o​f​(​m​e​s​s​a​g​e​B​u​f​f​e​r​)​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​n​r​e​a​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​(​"​r​e​a​d​(​s​t​d​i​n​)​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​n​r​e​a​d​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​c​l​o​s​e​d​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​w​r​i​t​t​e​n​ ​=​ ​W​r​i​t​e​M​e​s​s​a​g​e​(​s​e​r​v​e​r​F​d​,​ ​m​e​s​s​a​g​e​B​u​f​f​e​r​,​ ​n​r​e​a​d​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​n​w​r​i​t​t​e​n​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​r​i​t​e​M​e​s​s​a​g​e​ ​l​o​g​g​e​d​ ​t​h​e​ ​e​r​r​o​r​ ​f​o​r​ ​u​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​D​o​e​s​ ​t​h​e​ ​s​e​r​v​e​r​ ​h​a​v​e​ ​s​t​u​f​f​ ​f​o​r​ ​u​s​?​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​F​D​_​I​S​S​E​T​(​s​e​r​v​e​r​F​d​,​ ​&​r​e​a​d​f​d​s​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​R​e​a​d​ ​a​t​ ​m​o​s​t​ ​1​ ​l​e​s​s​ ​t​h​a​n​ ​t​h​e​ ​b​u​f​f​e​r​ ​s​i​z​e​ ​s​o​ ​w​e​ ​c​a​n​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​a​l​w​a​y​s​ ​n​u​l​l​-​t​e​r​m​i​n​a​t​e​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​n​r​e​a​d​ ​=​ ​r​e​a​d​ ​(​s​e​r​v​e​r​F​d​,​ ​i​n​c​o​m​i​n​g​B​u​f​f​e​r​,​ ​s​i​z​e​o​f​(​i​n​c​o​m​i​n​g​B​u​f​f​e​r​)​ ​-​ ​1​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​n​r​e​a​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​(​"​r​e​a​d​(​s​e​r​v​e​r​F​d​)​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​n​r​e​a​d​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​[​S​e​r​v​e​r​ ​c​l​o​s​e​d​ ​c​o​n​n​e​c​t​i​o​n​.​]​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​c​o​m​i​n​g​B​u​f​f​e​r​[​n​r​e​a​d​]​ ​=​ ​'​\​0​'​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​s​"​,​ ​i​n​c​o​m​i​n​g​B​u​f​f​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​e​x​i​t​_​s​t​a​t​u​s​ ​=​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​i​f​ ​(​s​e​r​v​e​r​F​d​ ​>​ ​-​1​)​ ​c​l​o​s​e​(​s​e​r​v​e​r​F​d​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​(​e​x​i​t​_​s​t​a​t​u​s​)​;​

}​ ​/​/​ ​m​a​i​n​

The main thing of interest here is how the string is written by WriteMessage(). Just calling write() is not sufficient. If the buffer associated with the socket becomes full, write() will return a value smaller than what you told it to write. To ensure all bytes are written, you might need to call write() repeatedly. This requires tracking where to pick up writing and how many bytes are left to write.

For the More Curious: Datagrams

What we’ve been talking about in this chapter are “stream sockets.” They are a connection-oriented, reliable form of network communication. Berkeley sockets also support “datagram sockets.” These are a message-oriented, unreliable form of network communication. They are unreliable in the sense that datagrams may be lost in transit and that the receiver might not get the datagrams in the same order in which they were sent. The data in the payload is checksummed, so it will be correct. Stream sockets use the TCP protocol, and datagram sockets use the UDP protocol.

Figure 14.3  TCP versus UDP

To create a datagram socket you use SOCK_DGRAM rather than SOCK_STREAM for the type parameter of socket(). For servers, you would still bind() your address to the socket, but you do not call listen(). Clients do not have to bind(); they can just make a socket and then send messages over it.

To send and receive datagrams, you use the sendto and recvfrom functions:

ssize_t sendto (int socket, const void *msg, size_t len, int flags,
                const struct sockaddr *to, socklen_t to_len);

Here are its arguments:

socket | The socket to write to
msg, len | The data and its length
flags | Just pass zero
to, tolen | The destination socket address and its size

The return value is the number of bytes sent or -1/errno on error.

ssize_t recvfrom (int socket, void *restrict buf, size_t len, int flags,
                       struct sockaddr *restrict from, socklen_t *restrict from_len);

Here are its arguments:

socket | The socket to read from
buf, len | Where to deposit the incoming data
flags | Just pass zero (you can pass in MSG_PEEK to peek at incoming data)
from, fromlen | The address the message was sent from

The return value is the number of bytes read or -1/errno on an error.

Exercises

Change the chatter programs to use datagrams. The messages are small enough to fit into a single datagram. Given the quality of discussion in most chat rooms, a lost packet here or there is probably not too much of an issue. Since the messages fit into packet boundaries and since recvfrom() will not coalesce packets like socket streams, this should simplify a lot of the code.

15
CFRunLoop

Unix networking uses file descriptors for reading and writing. Using file descriptors in server programs is pretty easy with the select() and kevent() functions. Using sockets in end-user GUI apps presents a problem, though. You cannot call select() on the main thread to wait for network activity. This will cause the UI to hang until there is something to be done on the network. Blocking the UI thread would cause your program to become unresponsive, and your process would be killed on iOS devices.

There are some options available to you. You can put your blocking function into a new thread. That thread can then block to its heart’s content until there is activity on the network, and then it can inform the UI thread that there is new data available for processing.

Another option is to take advantage of the run loop. A run loop is an event loop in a thread that looks for events, such as mouse clicks, key presses, and timer firings (Figure 15.1). The run loop dispatches these events to interested parties and then goes to sleep waiting for more events. NSRunLoop from the Foundation framework has a high-level API for watching for activity on mach ports, timers, and distributed object connections. NSRunLoop is built on CFRunLoop. We will be dealing with CFRunLoop here.

Figure 15.1  The run loop

CFRunLoop is provided by the Core Foundation framework, which supplies a number of useful features similar to those found in the Foundation framework. Much of Foundation is implemented on top of Core Foundation, including the various collection classes.

Core Foundation feels like a purely C-based version of Foundation. You use functions to manipulate CFTypeRefs, the Core Foundation equivalent of Objective-C’s id type. Core Foundation memory management is virtually identical to Foundation memory management; the primary difference is that Core Foundation has no equivalent to the autorelease pool. Where Cocoa would use the target-action system to specify a callback, Core Foundation uses a C function together with a context pointer.

CFRunLoop is where you can put the socket file descriptors to react to network traffic and not gum up the GUI works. There is one run loop per thread, whether it is created with NSThread or the pthread, so in a multithreaded application, you can have multiple run loops, each handling multiple sockets.

Many interfaces that deal with runloops include a “mode” parameter, such as NSStream’s -scheduleInRunLoop:forMode: or NSRunLoop’s -runMode:beforeDate:. A runloop mode is just a string, which is analogous to a dictionary lookup key. The runloop keeps collections of runloop sources keyed by the mode.

When a runloop is in a particular mode, the runloop ignores any sources that are not in that mode’s collection.

For instance, you might want to defer network activity while the user is dragging a slider.

CFSocket

CFSocket integrates sockets into the CFRunLoop architecture. CFSocketRef is a pointer to an opaque type and is created with CFSocketCreateWithNative():

CFSocketRef CFSocketCreateWithNative (CFAllocatorRef allocator,
                                      CFSocketNativeHandle sock,
                                      CFOptionFlags callBackTypes,
                                      CFSocketCallBack callout,
                                      const CFSocketContext *context);

There are lots of CF data types here. For CFAllocatorRef, you can pass kCFAllocatorDefault or NULL to get the default memory allocator. This will give you back a chunk of dynamically allocated memory that you should release by calling CFRelease(). You must release memory obtained from a Core Foundation function whose name begins with Create or Copy. There are no autorelease pools in Core Foundation.

In a garbage collected environment, the collector doles out the memory, and CFRetain() and CFRelease() manipulate the collector’s reference count for the object, so the object is not freed as soon as its reference count reaches zero. It is instead freed when the collector decides to reclaim its memory. You can thus CFRelease() a Core Foundation object immediately after creation to get behavior similar to the autorelease pools ubiquitous in Foundation. When this is the intended behavior, it is better to use the CFMakeCollectable() function. CFMakeCollectable() has no effect in non-garbage-collected code; in garbage-collected code, in addition to CFRelease()ing the object, the function also halts the program if the object was not allocated from the garbage collection zone. It also looks strange reading code to see a Core Foundation object being created and immediately released.

CFSocketNativeHandle is your socket file descriptor, which is just an integer.

CFOptionFlags is one of the CFSocketCallBackType flags:

kCFSocketNoCallBack

No callback function is supplied.

kCFSocketReadCallBack

Call the callback function when data can be read. The callback can get the data by calling read().

kCFSockeWriteCallBack

Call the callback function when there is space in the kernel buffers so that the socket is writable again. This is handy if you are writing large amounts of data to the socket.

kCFSocketAcceptCallBack

Call the callback function when there is a new connection on a listening socket.

kCFSocketDataCallBack

The data will be automatically read and packed into a CFData (similar to NSData). The callback will be called and be passed this CFData.

kCFSocketConnectCallBack

The above constants are mutually exclusive. You can bitwise-OR kCFSocketConnectCallBack if you want your socket to connect to a remote machine in the background.

CFSocketContext is a pointer to a struct that looks like this:

t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​{​
 ​ ​ ​ ​C​F​I​n​d​e​x​ ​ ​ ​ ​ ​v​e​r​s​i​o​n​;​
 ​ ​ ​ ​v​o​i​d​ ​*​ ​ ​ ​ ​ ​ ​i​n​f​o​;​
 ​ ​ ​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​(​*​r​e​t​a​i​n​ ​)​(​c​o​n​s​t​ ​v​o​i​d​ ​*​i​n​f​o​)​;​
 ​ ​ ​ ​v​o​i​d​ ​ ​ ​ ​ ​ ​ ​ ​(​*​r​e​l​e​a​s​e​)​(​c​o​n​s​t​ ​v​o​i​d​ ​*​i​n​f​o​)​;​
 ​ ​ ​ ​C​F​S​t​r​i​n​g​R​e​f​ ​(​*​c​o​p​y​D​e​s​c​r​i​p​t​i​o​n​)​(​c​o​n​s​t​ ​v​o​i​d​ ​*​i​n​f​o​)​;​
}​ ​C​F​S​o​c​k​e​t​C​o​n​t​e​x​t​;​

Set everything to zero or NULL for all of these, unless you want to perform some custom memory management. info is a pointer to whatever data you want, such as an Objective-C object pointer if you are using Cocoa. You can see some of the Foundation-flavored behavior with functions to perform retain and release on the context. Passing NULL for the function pointers indicates that the memory management for the item pointed to by info will be handled elsewhere and that info will be valid at least as long as the life of the CFSocket.

The CFSocketCallBack is a function of the form

 ​ ​ ​ ​v​o​i​d​ ​(​*​C​F​S​o​c​k​e​t​C​a​l​l​B​a​c​k​)​(​C​F​S​o​c​k​e​t​R​e​f​ ​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​S​o​c​k​e​t​C​a​l​l​B​a​c​k​T​y​p​e​ ​t​y​p​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​D​a​t​a​R​e​f​ ​a​d​d​r​e​s​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​d​a​t​a​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​v​o​i​d​ ​*​i​n​f​o​)​;​

CFDataRef is what you get if you registered a kCFSocketDataCallBack. data can be ignored if you don’t need it, but it is used for the kCFSocketAcceptCallBack and kCFSocketConnectCallBack cases. info is the info pointer of the context structure given to CFSocketCreateWithNative().

One memory management note: If your thread is not running AppKit’s event loop, which automatically creates and destroys autorelease pools, your callback must create and destroy its own pool to avoid leaking all objects autoreleased by or on behalf of it, such as through an NSLog() call.

Here is some code that will register a socket, using self for the info pointer (assuming that the code is run from within an Objective-C method):

c​o​n​s​t​ ​C​F​S​o​c​k​e​t​C​o​n​t​e​x​t​ ​c​o​n​t​e​x​t​ ​=​ ​{​ ​0​,​ ​s​e​l​f​,​ ​N​U​L​L​,​ ​N​U​L​L​,​ ​N​U​L​L​ ​}​;​

C​F​S​o​c​k​e​t​R​e​f​ ​s​o​c​k​e​t​ ​=​ ​C​F​S​o​c​k​e​t​C​r​e​a​t​e​W​i​t​h​N​a​t​i​v​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​e​r​v​e​r​_​s​o​c​k​f​d​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​C​F​S​o​c​k​e​t​R​e​a​d​C​a​l​l​B​a​c​k​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​o​c​k​e​t​C​a​l​l​B​a​c​k​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​&​c​o​n​t​e​x​t​)​;​

If the returned socket is NULL, something went wrong.

Once you have the CFSocketRef from this call, it is time to create a CFRunLoopSource with the socket. A CFRunLoopSource wraps the CFSocketRef. A wrapped CFSocketRef can be added to the run loop.

CFRunLoopSourceRef CFSocketCreateRunLoopSource (CFAllocatorRef allocator,
                                                CFSocketRef runLoopSocket,
                                                CFIndex order);

As you saw earlier, pass kCFAllocatorDefault to use the default allocator. The runLoopSocket parameter is the CFSocketRef you got from CFSocketCreateWithNative() earlier. The order parameter can control the order in which multiple callbacks are invoked. If you do not care about the order (which is usually the case), pass zero.

Here it is in action:

C​F​R​u​n​L​o​o​p​S​o​u​r​c​e​R​e​f​ ​r​l​s​ ​=​
 ​ ​ ​ ​C​F​S​o​c​k​e​t​C​r​e​a​t​e​R​u​n​L​o​o​p​S​o​u​r​c​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​ ​r​u​n​L​o​o​p​S​o​c​k​e​t​,​ ​0​)​;​

If the return value is NULL, something went wrong.

Finally, add the CFRunLoopSourceRef to a runloop using CFRunLoopAddSource():

v​o​i​d​ ​C​F​R​u​n​L​o​o​p​A​d​d​S​o​u​r​c​e​ ​(​C​F​R​u​n​L​o​o​p​R​e​f​ ​r​l​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​R​u​n​L​o​o​p​S​o​u​r​c​e​R​e​f​ ​s​o​u​r​c​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​S​t​r​i​n​g​R​e​f​ ​m​o​d​e​)​;​

To get the current run loop, use CFRunLoopGetCurrent(). The source parameter is the CFRunLoopSource created above. You can pass the constant kCFRunLoopDefaultMode as the mode. On a UI thread, use kCFRunLoopCommonModes so that the socket doesn’t get starved during some of the other modes of the UI’s runloop, such as when running a modal dialog.

Add the socket to the run loop with

C​F​R​u​n​L​o​o​p​A​d​d​S​o​u​r​c​e​ ​(​C​F​R​u​n​L​o​o​p​G​e​t​C​u​r​r​e​n​t​(​)​,​ ​r​l​s​,​ ​k​C​F​R​u​n​L​o​o​p​C​o​m​m​o​n​M​o​d​e​s​)​;​

The run loop takes ownership of the source and so retains it. You have relinquished ownership and so must CFRelease() the run loop source to keep the reference count correct.

CFHost

Virtually all network-orientated applications require host name lookup, such as with gethostbyname2() described in Chapter 14: Network Programming With Sockets. Unfortunately, the standard Unix domain name lookup functions are blocking, which causes the same problems with GUI unresponsiveness and application termination as discussed earlier.

The Core Services framework provides CFHost which does runloop-based host name lookup. You can use CFHost to look up addresses (mapping hostnames to an IP address) or names (mapping an IP address to a set of hostnames). You look up addresses by creating a CFHost and giving it the hostname you are interested in. Then set the “client,” which is a callback function and a context block like the socket context described earlier. Then schedule the CFHost with a runloop and start resolution:

 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​h​o​s​t​n​a​m​e​ ​=​ ​[​h​o​s​t​n​a​m​e​F​i​e​l​d​ ​s​t​r​i​n​g​V​a​l​u​e​]​;​
 ​ ​ ​ ​C​F​H​o​s​t​R​e​f​ ​h​o​s​t​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​C​F​H​o​s​t​C​r​e​a​t​e​W​i​t​h​N​a​m​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​ ​(​C​F​S​t​r​i​n​g​R​e​f​)​h​o​s​t​n​a​m​e​)​;​
 ​ ​ ​ ​C​F​H​o​s​t​C​l​i​e​n​t​C​o​n​t​e​x​t​ ​c​o​n​t​e​x​t​ ​=​ ​{​ ​.​i​n​f​o​ ​=​ ​s​e​l​f​ ​}​;​
 ​ ​ ​ ​B​o​o​l​e​a​n​ ​o​k​ ​=​ ​C​F​H​o​s​t​S​e​t​C​l​i​e​n​t​ ​(​h​o​s​t​,​ ​n​a​m​e​L​o​o​k​u​p​C​a​l​l​b​a​c​k​,​ ​&​c​o​n​t​e​x​t​)​;​
 ​ ​ ​ ​/​/​ ​e​r​r​o​r​ ​c​h​e​c​k​
 ​ ​ ​ ​C​F​H​o​s​t​S​c​h​e​d​u​l​e​W​i​t​h​R​u​n​L​o​o​p​ ​(​h​o​s​t​,​ ​C​F​R​u​n​L​o​o​p​G​e​t​C​u​r​r​e​n​t​(​)​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​C​F​R​u​n​L​o​o​p​D​e​f​a​u​l​t​M​o​d​e​)​;​
 ​ ​ ​ ​C​F​S​t​r​e​a​m​E​r​r​o​r​ ​e​r​r​o​r​;​
 ​ ​ ​ ​o​k​ ​=​ ​C​F​H​o​s​t​S​t​a​r​t​I​n​f​o​R​e​s​o​l​u​t​i​o​n​ ​(​h​o​s​t​,​ ​k​C​F​H​o​s​t​A​d​d​r​e​s​s​e​s​,​ ​&​e​r​r​o​r​)​;​
 ​ ​ ​ ​/​/​ ​e​r​r​o​r​ ​c​h​e​c​k​

CFHostStartInfoResolution takes a CFHostInfoType:

kCFHostAddresses

Returns the known addresses for the give hostname. You would create the host ref with CFHostCreateWithName(). The callback is passed a CFArray of CFDatas, each wrapping a struct sockaddr.

kCFHostNames

Returns the known hostnames for the given address. You would create the host ref with CFHostCreateWithAddress(), which takes a CFData wrapping a struct sockaddr. The callback is passed a CFArray of CFStrings.

kCFHostReachability

The callback is passed a CFData wrapping SCNetworkConnectionFlags.

Your callback gets called once the hostname resolution has run.

s​t​a​t​i​c​ ​v​o​i​d​ ​n​a​m​e​L​o​o​k​u​p​C​a​l​l​b​a​c​k​ ​(​C​F​H​o​s​t​R​e​f​ ​h​o​s​t​,​ ​C​F​H​o​s​t​I​n​f​o​T​y​p​e​ ​t​y​p​e​I​n​f​o​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​C​F​S​t​r​e​a​m​E​r​r​o​r​ ​*​e​r​r​o​r​,​ ​v​o​i​d​ ​*​i​n​f​o​)​ ​{​
 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​a​d​d​r​e​s​s​e​s​ ​=​ ​(​N​S​A​r​r​a​y​ ​*​)​ ​C​F​H​o​s​t​G​e​t​A​d​d​r​e​s​s​i​n​g​ ​(​h​o​s​t​,​ ​N​U​L​L​)​;​

 ​ ​ ​ ​f​o​r​ ​(​N​S​D​a​t​a​ ​*​d​a​t​a​ ​i​n​ ​a​d​d​r​e​s​s​e​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​ ​*​r​e​m​o​t​e​A​d​d​r​e​s​s​ ​=​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​ ​*​)​ ​d​a​t​a​.​b​y​t​e​s​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​a​d​d​r​e​s​s​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​m​o​t​e​A​d​d​r​e​s​s​-​>​s​a​_​f​a​m​i​l​y​ ​=​=​ ​A​F​_​I​N​E​T​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​I​N​E​T​_​A​D​D​R​S​T​R​L​E​N​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​*​i​p​v​4​ ​=​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​*​)​r​e​m​o​t​e​A​d​d​r​e​s​s​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​ ​=​ ​i​n​e​t​_​n​t​o​p​ ​(​A​F​_​I​N​E​T​,​ ​&​i​p​v​4​-​>​s​i​n​_​a​d​d​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​u​f​f​e​r​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​d​o​ ​s​o​m​e​t​h​i​n​g​ ​w​i​t​h​ ​a​d​d​r​e​s​s​

 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​r​e​m​o​t​e​A​d​d​r​e​s​s​-​>​s​a​_​f​a​m​i​l​y​ ​=​=​ ​A​F​_​I​N​E​T​6​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​I​N​E​T​6​_​A​D​D​R​S​T​R​L​E​N​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​6​ ​*​i​p​v​6​ ​=​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​6​ ​*​)​r​e​m​o​t​e​A​d​d​r​e​s​s​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​d​d​r​e​s​s​ ​=​ ​i​n​e​t​_​n​t​o​p​ ​(​A​F​_​I​N​E​T​6​,​ ​&​i​p​v​6​-​>​s​i​n​6​_​a​d​d​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​u​f​f​e​r​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​d​o​ ​s​o​m​e​t​h​i​n​g​ ​w​i​t​h​ ​a​d​d​r​e​s​s​

 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​c​o​m​p​l​a​i​n​.​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​C​l​e​a​n​ ​u​p​ ​a​f​t​e​r​ ​t​h​e​ ​C​F​H​o​s​t​.​
 ​ ​ ​ ​C​F​H​o​s​t​U​n​s​c​h​e​d​u​l​e​F​r​o​m​R​u​n​L​o​o​p​ ​(​h​o​s​t​,​ ​C​F​R​u​n​L​o​o​p​G​e​t​C​u​r​r​e​n​t​(​)​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​C​F​R​u​n​L​o​o​p​C​o​m​m​o​n​M​o​d​e​s​)​;​
 ​ ​ ​ ​/​/​ ​t​u​r​n​ ​o​f​f​ ​t​h​e​ ​c​a​l​l​b​a​c​k​.​
 ​ ​ ​ ​C​F​H​o​s​t​S​e​t​C​l​i​e​n​t​ ​(​h​o​s​t​,​ ​N​U​L​L​,​ ​N​U​L​L​)​;​
 ​ ​ ​ ​C​F​R​e​l​e​a​s​e​ ​(​h​o​s​t​)​;​

}​ ​/​/​ ​n​a​m​e​L​o​o​k​u​p​C​a​l​l​b​a​c​k​

GUI Chatter Client

chatterclient from the networking chapter works fine if you like living on the command line. Most Mac users (understandably) want to use GUI programs. CFChatterClient is a GUI version of the chatterclient. It uses CFRunLoop and CFSocket to communicate across the network without blocking the application runloop.

In Xcode, create a new Cocoa Application project called CFChatterClient. Edit MainMenu.xib and lay out the UI as shown in Figure 15.2.

Figure 15.2  CFChatterClient user interface

Back in Xcode, edit CFChatterClientAppDelegate. Edit the header to look like Example 15.1 and the body like Example 15.2.

Example 15.1. CFChatterClientAppDelegate.h

#​i​m​p​o​r​t​ ​<​C​o​c​o​a​/​C​o​c​o​a​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​C​F​C​h​a​t​t​e​r​C​l​i​e​n​t​A​p​p​D​e​l​e​g​a​t​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​<​N​S​A​p​p​l​i​c​a​t​i​o​n​D​e​l​e​g​a​t​e​>​ ​{​
 ​ ​ ​ ​N​S​W​i​n​d​o​w​ ​*​w​i​n​d​o​w​;​

 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​_​u​s​e​r​n​a​m​e​F​i​e​l​d​;​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​_​h​o​s​t​F​i​e​l​d​;​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​N​S​B​u​t​t​o​n​ ​ ​ ​ ​*​_​j​o​i​n​L​e​a​v​e​B​u​t​t​o​n​;​

 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​V​i​e​w​ ​ ​*​_​t​r​a​n​s​c​r​i​p​t​;​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​_​m​e​s​s​a​g​e​F​i​e​l​d​;​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​N​S​B​u​t​t​o​n​ ​ ​ ​ ​*​_​s​e​n​d​B​u​t​t​o​n​;​

 ​ ​ ​ ​C​F​S​o​c​k​e​t​N​a​t​i​v​e​H​a​n​d​l​e​ ​ ​_​s​o​c​k​f​d​;​
 ​ ​ ​ ​C​F​S​o​c​k​e​t​R​e​f​ ​_​s​o​c​k​e​t​R​e​f​;​
}​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​W​i​n​d​o​w​ ​*​w​i​n​d​o​w​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​a​d​o​n​l​y​,​ ​g​e​t​t​e​r​=​i​s​C​o​n​n​e​c​t​e​d​)​ ​B​O​O​L​ ​c​o​n​n​e​c​t​e​d​;​

-​ ​(​I​B​A​c​t​i​o​n​)​ ​s​e​n​d​M​e​s​s​a​g​e​:​ ​(​i​d​)​ ​s​e​n​d​e​r​;​
-​ ​(​I​B​A​c​t​i​o​n​)​ ​j​o​i​n​:​ ​(​i​d​)​ ​s​e​n​d​e​r​;​
-​ ​(​I​B​A​c​t​i​o​n​)​ ​l​e​a​v​e​:​ ​(​i​d​)​ ​s​e​n​d​e​r​;​

@​e​n​d​ ​/​/​ ​C​F​C​h​a​t​t​e​r​C​l​i​e​n​t​A​p​p​D​e​l​e​g​a​t​e​

Example 15.2. CFChatterClientAppDelegate.m

#​i​m​p​o​r​t​ ​"​C​F​C​h​a​t​t​e​r​C​l​i​e​n​t​A​p​p​D​e​l​e​g​a​t​e​.​h​"​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​f​c​n​t​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​c​n​t​l​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​b​o​o​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​t​r​u​e​/​f​a​l​s​e​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​s​t​r​e​r​r​o​r​(​)​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​c​l​o​s​e​(​)​

#​i​m​p​o​r​t​ ​<​a​r​p​a​/​i​n​e​t​.​h​>​ ​ ​ ​ ​ ​/​/​ ​i​n​e​t​_​n​t​o​p​(​)​
#​i​m​p​o​r​t​ ​<​n​e​t​d​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​g​e​t​h​o​s​t​b​y​n​a​m​e​2​(​)​
#​i​m​p​o​r​t​ ​<​n​e​t​i​n​e​t​/​i​n​.​h​>​ ​ ​ ​ ​/​/​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​
#​i​m​p​o​r​t​ ​<​n​e​t​i​n​e​t​6​/​i​n​6​.​h​>​ ​ ​/​/​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​6​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​o​c​k​e​t​.​h​>​ ​ ​ ​ ​/​/​ ​s​o​c​k​e​t​(​)​,​ ​A​F​_​I​N​E​T​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​ ​ ​ ​/​/​ ​r​a​n​d​o​m​ ​t​y​p​e​s​

#​d​e​f​i​n​e​ ​M​A​X​_​M​E​S​S​A​G​E​_​S​I​Z​E​ ​ ​(​U​I​N​T​8​_​M​A​X​)​
#​d​e​f​i​n​e​ ​R​E​A​D​_​B​U​F​F​E​R​_​S​I​Z​E​ ​ ​(​P​A​G​E​_​S​I​Z​E​)​

s​t​a​t​i​c​ ​c​o​n​s​t​ ​i​n​_​p​o​r​t​_​t​ ​k​P​o​r​t​N​u​m​b​e​r​ ​=​ ​2​3​4​2​;​
s​t​a​t​i​c​ ​c​o​n​s​t​ ​i​n​t​ ​k​I​n​v​a​l​i​d​S​o​c​k​e​t​ ​=​ ​-​1​;​

/​/​ ​S​e​e​ ​c​h​a​t​t​e​r​c​l​i​e​n​t​.​m​ ​f​o​r​ ​t​h​e​ ​d​e​f​i​n​i​t​i​o​n​ ​o​f​ ​t​h​i​s​ ​f​u​n​c​t​i​o​n​.​
s​t​a​t​i​c​ ​i​n​t​ ​W​r​i​t​e​M​e​s​s​a​g​e​ ​(​i​n​t​ ​f​d​,​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​b​u​f​f​e​r​,​ ​s​i​z​e​_​t​ ​l​e​n​g​t​h​)​;​

/​/​ ​S​e​e​ ​s​i​m​p​l​e​c​l​i​e​n​t​.​m​ ​f​o​r​ ​t​h​e​ ​d​e​f​i​n​i​t​i​o​n​ ​o​f​ ​t​h​e​s​e​ ​t​w​o​ ​f​u​n​c​t​i​o​n​s​.​
s​t​a​t​i​c​ ​i​n​t​ ​S​o​c​k​e​t​C​o​n​n​e​c​t​e​d​T​o​H​o​s​t​N​a​m​e​d​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​h​o​s​t​n​a​m​e​)​;​
s​t​a​t​i​c​ ​b​o​o​l​ ​G​e​t​A​d​d​r​e​s​s​A​t​I​n​d​e​x​ ​(​s​t​r​u​c​t​ ​h​o​s​t​e​n​t​ ​*​h​o​s​t​,​ ​i​n​t​ ​a​d​d​r​e​s​s​I​n​d​e​x​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​s​t​o​r​a​g​e​ ​*​o​u​t​S​e​r​v​e​r​A​d​d​r​e​s​s​)​;​

/​/​ ​F​o​r​w​a​r​d​ ​r​e​f​e​r​e​n​c​e​s​
@​i​n​t​e​r​f​a​c​e​ ​C​F​C​h​a​t​t​e​r​C​l​i​e​n​t​A​p​p​D​e​l​e​g​a​t​e​ ​(​)​

/​/​ ​U​I​
-​ ​(​v​o​i​d​)​ ​u​p​d​a​t​e​U​I​;​
-​ ​(​v​o​i​d​)​ ​a​p​p​e​n​d​M​e​s​s​a​g​e​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​m​s​g​;​
-​ ​(​v​o​i​d​)​ ​r​u​n​E​r​r​o​r​M​e​s​s​a​g​e​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​m​e​s​s​a​g​e​ ​ ​w​i​t​h​E​r​r​n​o​:​ ​(​i​n​t​)​ ​e​r​r​;​

/​/​ ​C​o​n​n​e​c​t​i​o​n​
-​ ​(​v​o​i​d​)​ ​c​o​n​n​e​c​t​T​o​H​o​s​t​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​h​o​s​t​n​a​m​e​ ​ ​a​s​U​s​e​r​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​u​s​e​r​n​a​m​e​;​
-​ ​(​v​o​i​d​)​ ​c​l​o​s​e​C​o​n​n​e​c​t​i​o​n​;​

/​/​ ​S​o​c​k​e​t​
-​ ​(​v​o​i​d​)​ ​s​t​a​r​t​M​o​n​i​t​o​r​i​n​g​S​o​c​k​e​t​;​
-​ ​(​v​o​i​d​)​ ​s​t​o​p​M​o​n​i​t​o​r​i​n​g​S​o​c​k​e​t​;​

/​/​ ​R​u​n​l​o​o​p​ ​s​t​u​f​f​a​g​e​
s​t​a​t​i​c​ ​v​o​i​d​ ​R​e​c​e​i​v​e​M​e​s​s​a​g​e​ ​(​C​F​S​o​c​k​e​t​R​e​f​,​ ​C​F​S​o​c​k​e​t​C​a​l​l​B​a​c​k​T​y​p​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​D​a​t​a​R​e​f​,​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​,​ ​v​o​i​d​ ​*​)​;​
-​ ​(​v​o​i​d​)​ ​h​a​n​d​l​e​M​e​s​s​a​g​e​D​a​t​a​:​ ​(​N​S​D​a​t​a​ ​*​)​ ​d​a​t​a​;​

@​e​n​d​ ​/​/​ ​e​x​t​e​n​s​i​o​n​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​C​F​C​h​a​t​t​e​r​C​l​i​e​n​t​A​p​p​D​e​l​e​g​a​t​e​
@​s​y​n​t​h​e​s​i​z​e​ ​w​i​n​d​o​w​;​

-​ ​(​i​d​)​ ​i​n​i​t​ ​{​
 ​ ​ ​ ​i​f​ ​(​(​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​_​s​o​c​k​f​d​ ​=​ ​k​I​n​v​a​l​i​d​S​o​c​k​e​t​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​ ​/​/​ ​i​n​i​t​

-​ ​(​v​o​i​d​)​ ​a​w​a​k​e​F​r​o​m​N​i​b​ ​{​
 ​ ​ ​ ​/​/​ ​P​r​e​p​o​p​u​l​a​t​e​ ​t​h​e​ ​u​s​e​r​n​a​m​e​ ​f​i​e​l​d​ ​a​s​ ​a​ ​c​o​n​v​e​n​i​e​n​c​e​.​x​
 ​ ​ ​ ​[​_​u​s​e​r​n​a​m​e​F​i​e​l​d​ ​s​e​t​S​t​r​i​n​g​V​a​l​u​e​:​ ​N​S​U​s​e​r​N​a​m​e​(​)​]​;​
 ​ ​ ​ ​[​s​e​l​f​ ​u​p​d​a​t​e​U​I​]​;​
}​ ​/​/​ ​a​w​a​k​e​F​r​o​m​N​i​b​

-​ ​(​v​o​i​d​)​ ​d​e​a​l​l​o​c​ ​{​
 ​ ​ ​ ​[​s​e​l​f​ ​c​l​o​s​e​C​o​n​n​e​c​t​i​o​n​]​;​
 ​ ​ ​ ​[​s​u​p​e​r​ ​d​e​a​l​l​o​c​]​;​
}​ ​/​/​ ​d​e​a​l​l​o​c​

/​/​ ​Q​u​i​t​ ​t​h​e​ ​a​p​p​ ​w​h​e​n​ ​t​h​e​ ​l​a​s​t​ ​w​i​n​d​o​w​ ​c​l​o​s​e​s​.​
-​ ​(​B​O​O​L​)​ ​a​p​p​l​i​c​a​t​i​o​n​S​h​o​u​l​d​T​e​r​m​i​n​a​t​e​A​f​t​e​r​L​a​s​t​W​i​n​d​o​w​C​l​o​s​e​d​:​ ​(​N​S​A​p​p​l​i​c​a​t​i​o​n​ ​*​)​ ​a​p​p​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​Y​E​S​;​
}​ ​/​/​ ​a​p​p​l​i​c​a​t​i​o​n​S​h​o​u​l​d​T​e​r​m​i​n​a​t​e​A​f​t​e​r​L​a​s​t​W​i​n​d​o​w​C​l​o​s​e​d​

-​ ​(​B​O​O​L​)​ ​i​s​C​o​n​n​e​c​t​e​d​ ​{​
 ​ ​ ​ ​B​O​O​L​ ​c​o​n​n​e​c​t​e​d​ ​=​ ​(​_​s​o​c​k​e​t​R​e​f​ ​!​=​ ​N​U​L​L​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​c​o​n​n​e​c​t​e​d​;​
}​ ​/​/​ ​i​s​C​o​n​n​e​c​t​e​d​

-​ ​(​I​B​A​c​t​i​o​n​)​ ​s​e​n​d​M​e​s​s​a​g​e​:​ ​(​i​d​)​ ​s​e​n​d​e​r​ ​{​
 ​ ​ ​ ​i​f​ ​(​_​s​o​c​k​f​d​ ​=​=​ ​k​I​n​v​a​l​i​d​S​o​c​k​e​t​)​ ​ ​r​e​t​u​r​n​;​

 ​ ​ ​ ​/​/​ ​A​d​d​ ​a​ ​n​e​w​l​i​n​e​ ​t​o​ ​m​a​t​c​h​ ​c​o​m​m​a​n​d​-​l​i​n​e​ ​c​l​i​e​n​t​'​s​ ​b​e​h​a​v​i​o​r​.​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​m​e​s​s​a​g​e​S​t​r​i​n​g​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​_​m​e​s​s​a​g​e​F​i​e​l​d​ ​s​t​r​i​n​g​V​a​l​u​e​]​ ​s​t​r​i​n​g​B​y​A​p​p​e​n​d​i​n​g​S​t​r​i​n​g​:​ ​@​"​\​n​"​]​;​
 ​ ​ ​ ​i​f​ ​(​m​e​s​s​a​g​e​S​t​r​i​n​g​.​l​e​n​g​t​h​ ​=​=​ ​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​J​u​s​t​ ​t​h​e​ ​n​e​w​l​i​n​e​ ​-​ ​d​o​n​'​t​ ​s​e​n​d​ ​i​t​.​ ​ ​H​e​l​p​ ​p​r​e​v​e​n​t​ ​c​h​a​n​n​e​l​ ​f​l​o​o​d​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​m​e​s​s​a​g​e​ ​=​ ​[​m​e​s​s​a​g​e​S​t​r​i​n​g​ ​U​T​F​8​S​t​r​i​n​g​]​;​
 ​ ​ ​ ​c​o​n​s​t​ ​s​i​z​e​_​t​ ​l​e​n​g​t​h​ ​=​ ​s​t​r​l​e​n​ ​(​m​e​s​s​a​g​e​)​;​
 ​ ​ ​ ​i​n​t​ ​n​w​r​i​t​t​e​n​ ​=​ ​W​r​i​t​e​M​e​s​s​a​g​e​ ​(​_​s​o​c​k​f​d​,​ ​m​e​s​s​a​g​e​,​ ​l​e​n​g​t​h​)​;​

 ​ ​ ​ ​/​/​ ​S​u​c​c​e​s​s​f​u​l​ ​s​e​n​d​,​ ​c​l​e​a​r​ ​o​u​t​ ​t​h​e​ ​m​e​s​s​a​g​e​ ​f​i​e​l​d​.​
 ​ ​ ​ ​i​f​ ​(​n​w​r​i​t​t​e​n​ ​=​=​ ​l​e​n​g​t​h​)​ ​[​_​m​e​s​s​a​g​e​F​i​e​l​d​ ​s​e​t​S​t​r​i​n​g​V​a​l​u​e​:​ ​@​"​"​]​;​

}​ ​/​/​ ​s​e​n​d​M​e​s​s​a​g​e​

/​/​ ​S​e​n​t​ ​b​y​ ​J​o​i​n​/​L​e​a​v​e​ ​b​u​t​t​o​n​ ​w​h​e​n​ ​n​o​t​ ​c​o​n​n​e​c​t​e​d​.​
-​ ​(​I​B​A​c​t​i​o​n​)​ ​j​o​i​n​:​ ​(​i​d​)​ ​s​e​n​d​e​r​ ​{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​h​o​s​t​n​a​m​e​ ​=​ ​[​_​h​o​s​t​F​i​e​l​d​ ​s​t​r​i​n​g​V​a​l​u​e​]​;​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​u​s​e​r​n​a​m​e​ ​=​ ​[​_​u​s​e​r​n​a​m​e​F​i​e​l​d​ ​s​t​r​i​n​g​V​a​l​u​e​]​;​
 ​ ​ ​ ​[​s​e​l​f​ ​c​o​n​n​e​c​t​T​o​H​o​s​t​:​ ​h​o​s​t​n​a​m​e​ ​ ​a​s​U​s​e​r​:​ ​u​s​e​r​n​a​m​e​]​;​

 ​ ​ ​ ​[​s​e​l​f​ ​u​p​d​a​t​e​U​I​]​;​

 ​ ​ ​ ​i​f​ ​(​[​s​e​l​f​ ​i​s​C​o​n​n​e​c​t​e​d​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​c​o​n​n​e​c​t​M​e​s​s​a​g​e​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​ ​@​"​(​ ​*​ ​*​ ​*​ ​c​o​n​n​e​c​t​e​d​ ​t​o​ ​%​@​ ​a​s​ ​%​@​ ​*​ ​*​ ​*​ ​)​ ​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​o​s​t​n​a​m​e​,​ ​u​s​e​r​n​a​m​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​a​p​p​e​n​d​M​e​s​s​a​g​e​:​ ​c​o​n​n​e​c​t​M​e​s​s​a​g​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​_​m​e​s​s​a​g​e​F​i​e​l​d​ ​w​i​n​d​o​w​]​ ​m​a​k​e​F​i​r​s​t​R​e​s​p​o​n​d​e​r​:​ ​_​m​e​s​s​a​g​e​F​i​e​l​d​]​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​s​u​b​s​c​r​i​b​e​

/​/​ ​S​e​n​t​ ​b​y​ ​J​o​i​n​/​L​e​a​v​e​ ​b​u​t​t​o​n​ ​w​h​e​n​ ​c​o​n​n​e​c​t​e​d​.​
-​ ​(​I​B​A​c​t​i​o​n​)​ ​l​e​a​v​e​:​ ​(​i​d​)​ ​s​e​n​d​e​r​ ​{​
 ​ ​ ​ ​[​s​e​l​f​ ​c​l​o​s​e​C​o​n​n​e​c​t​i​o​n​]​;​

 ​ ​ ​ ​[​s​e​l​f​ ​u​p​d​a​t​e​U​I​]​;​

 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​d​i​s​c​o​n​n​e​c​t​M​e​s​s​a​g​e​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​ ​@​"​(​ ​*​ ​*​ ​*​ ​d​i​s​c​o​n​n​e​c​t​e​d​ ​f​r​o​m​ ​%​@​ ​*​ ​*​ ​*​)​ ​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​_​h​o​s​t​F​i​e​l​d​ ​s​t​r​i​n​g​V​a​l​u​e​]​]​;​
 ​ ​ ​ ​[​s​e​l​f​ ​a​p​p​e​n​d​M​e​s​s​a​g​e​:​ ​d​i​s​c​o​n​n​e​c​t​M​e​s​s​a​g​e​]​;​
 ​ ​ ​ ​[​[​_​h​o​s​t​F​i​e​l​d​ ​w​i​n​d​o​w​]​ ​m​a​k​e​F​i​r​s​t​R​e​s​p​o​n​d​e​r​:​ ​_​h​o​s​t​F​i​e​l​d​]​;​
}​ ​/​/​ ​u​n​s​u​b​s​c​r​i​b​e​

-​ ​(​v​o​i​d​)​ ​u​p​d​a​t​e​U​I​ ​{​
 ​ ​ ​ ​c​o​n​s​t​ ​B​O​O​L​ ​c​o​n​n​e​c​t​e​d​ ​=​ ​[​s​e​l​f​ ​i​s​C​o​n​n​e​c​t​e​d​]​;​

 ​ ​ ​ ​/​/​ ​D​i​s​a​b​l​e​ ​u​s​e​r​n​a​m​e​ ​a​n​d​ ​h​o​s​t​n​a​m​e​ ​w​h​i​l​e​ ​c​o​n​n​e​c​t​e​d​.​
 ​ ​ ​ ​[​_​u​s​e​r​n​a​m​e​F​i​e​l​d​ ​s​e​t​E​n​a​b​l​e​d​:​ ​!​c​o​n​n​e​c​t​e​d​]​;​
 ​ ​ ​ ​[​_​h​o​s​t​F​i​e​l​d​ ​s​e​t​E​n​a​b​l​e​d​:​ ​!​c​o​n​n​e​c​t​e​d​]​;​

 ​ ​ ​ ​/​/​ ​J​o​i​n​ ​b​e​c​o​m​e​s​ ​L​e​a​v​e​ ​w​h​e​n​ ​c​o​n​n​e​c​t​e​d​.​
 ​ ​ ​ ​[​_​j​o​i​n​L​e​a​v​e​B​u​t​t​o​n​ ​s​e​t​T​i​t​l​e​:​ ​c​o​n​n​e​c​t​e​d​ ​?​ ​@​"​L​e​a​v​e​"​ ​:​ ​@​"​J​o​i​n​"​]​;​
 ​ ​ ​ ​[​_​j​o​i​n​L​e​a​v​e​B​u​t​t​o​n​ ​s​e​t​A​c​t​i​o​n​:​ ​c​o​n​n​e​c​t​e​d​ ​?​ ​@​s​e​l​e​c​t​o​r​(​l​e​a​v​e​:​)​ ​:​ ​@​s​e​l​e​c​t​o​r​(​j​o​i​n​:​)​]​;​

 ​ ​ ​ ​/​/​ ​C​a​n​ ​o​n​l​y​ ​t​y​p​e​ ​o​r​ ​s​e​n​d​ ​m​e​s​s​a​g​e​s​ ​w​h​i​l​e​ ​c​o​n​n​e​c​t​e​d​.​
 ​ ​ ​ ​[​_​m​e​s​s​a​g​e​F​i​e​l​d​ ​s​e​t​E​n​a​b​l​e​d​:​ ​c​o​n​n​e​c​t​e​d​]​;​
 ​ ​ ​ ​[​_​s​e​n​d​B​u​t​t​o​n​ ​s​e​t​E​n​a​b​l​e​d​:​ ​c​o​n​n​e​c​t​e​d​]​;​
}​ ​/​/​ ​u​p​d​a​t​e​U​I​

-​ ​(​v​o​i​d​)​ ​a​p​p​e​n​d​M​e​s​s​a​g​e​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​m​s​g​ ​{​
 ​ ​ ​ ​/​/​ ​A​p​p​e​n​d​ ​t​h​e​ ​m​e​s​s​a​g​e​.​
 ​ ​ ​ ​N​S​R​a​n​g​e​ ​e​n​d​O​f​T​e​x​t​ ​=​ ​N​S​M​a​k​e​R​a​n​g​e​ ​(​[​_​t​r​a​n​s​c​r​i​p​t​ ​s​t​r​i​n​g​]​.​l​e​n​g​t​h​,​ ​0​)​;​
 ​ ​ ​ ​[​_​t​r​a​n​s​c​r​i​p​t​ ​r​e​p​l​a​c​e​C​h​a​r​a​c​t​e​r​s​I​n​R​a​n​g​e​:​ ​e​n​d​O​f​T​e​x​t​ ​ ​w​i​t​h​S​t​r​i​n​g​:​ ​m​s​g​]​;​

 ​ ​ ​ ​/​/​ ​M​a​k​e​ ​t​h​e​ ​e​n​d​ ​o​f​ ​t​h​e​ ​m​e​s​s​a​g​e​ ​v​i​s​i​b​l​e​.​
 ​ ​ ​ ​e​n​d​O​f​T​e​x​t​ ​=​ ​N​S​M​a​k​e​R​a​n​g​e​ ​(​[​_​t​r​a​n​s​c​r​i​p​t​ ​s​t​r​i​n​g​]​.​l​e​n​g​t​h​,​ ​0​)​;​
 ​ ​ ​ ​[​_​t​r​a​n​s​c​r​i​p​t​ ​s​c​r​o​l​l​R​a​n​g​e​T​o​V​i​s​i​b​l​e​:​ ​e​n​d​O​f​T​e​x​t​]​;​
}​ ​/​/​ ​a​p​p​e​n​d​M​e​s​s​a​g​e​

-​ ​(​v​o​i​d​)​ ​r​u​n​E​r​r​o​r​M​e​s​s​a​g​e​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​m​e​s​s​a​g​e​ ​ ​w​i​t​h​E​r​r​n​o​:​ ​(​i​n​t​)​ ​e​r​r​o​r​ ​{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​e​r​r​n​o​S​t​r​i​n​g​ ​=​ ​@​"​"​;​
 ​ ​ ​ ​i​f​ ​(​e​r​r​o​r​ ​!​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​S​t​r​i​n​g​ ​=​ ​(​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​U​T​F​8​S​t​r​i​n​g​:​ ​s​t​r​e​r​r​o​r​(​e​r​r​o​r​)​]​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​N​S​R​u​n​A​l​e​r​t​P​a​n​e​l​ ​(​m​e​s​s​a​g​e​,​ ​e​r​r​n​o​S​t​r​i​n​g​,​ ​@​"​O​K​"​,​ ​n​i​l​,​ ​n​i​l​)​;​
}​ ​/​/​ ​r​u​n​E​r​r​o​r​M​e​s​s​a​g​e​

-​ ​(​v​o​i​d​)​ ​c​o​n​n​e​c​t​T​o​H​o​s​t​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​h​o​s​t​n​a​m​e​ ​ ​a​s​U​s​e​r​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​u​s​e​r​n​a​m​e​ ​{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​e​r​r​o​r​M​e​s​s​a​g​e​ ​=​ ​n​i​l​;​
 ​ ​ ​ ​i​n​t​ ​s​y​s​E​r​r​o​r​ ​=​ ​n​o​E​r​r​;​

 ​ ​ ​ ​i​f​ ​(​_​s​o​c​k​f​d​ ​!​=​ ​k​I​n​v​a​l​i​d​S​o​c​k​e​t​)​ ​[​s​e​l​f​ ​c​l​o​s​e​C​o​n​n​e​c​t​i​o​n​]​;​

 ​ ​ ​ ​/​/​ ​s​a​n​i​t​y​ ​c​h​e​c​k​ ​o​u​r​ ​n​i​c​k​ ​n​a​m​e​ ​b​e​f​o​r​e​ ​t​r​y​i​n​g​ ​t​o​ ​c​o​n​n​e​c​t​
 ​ ​ ​ ​i​f​ ​(​h​o​s​t​n​a​m​e​.​l​e​n​g​t​h​ ​<​ ​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​M​e​s​s​a​g​e​ ​=​ ​@​"​H​o​s​t​n​a​m​e​ ​m​u​s​t​ ​n​o​t​ ​b​e​ ​e​m​p​t​y​.​"​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​i​f​ ​(​u​s​e​r​n​a​m​e​.​l​e​n​g​t​h​ ​=​=​ ​0​ ​|​|​ ​u​s​e​r​n​a​m​e​.​l​e​n​g​t​h​ ​>​ ​8​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​M​e​s​s​a​g​e​ ​=​ ​@​"​U​s​e​r​n​a​m​e​ ​m​u​s​t​ ​b​e​ ​b​e​t​w​e​e​n​ ​1​ ​a​n​d​ ​8​ ​c​h​a​r​a​c​t​e​r​s​ ​l​o​n​g​.​"​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​h​o​s​t​n​a​m​e​C​S​t​r​ ​=​ ​[​h​o​s​t​n​a​m​e​ ​U​T​F​8​S​t​r​i​n​g​]​;​
 ​ ​ ​ ​_​s​o​c​k​f​d​ ​=​ ​S​o​c​k​e​t​C​o​n​n​e​c​t​e​d​T​o​H​o​s​t​N​a​m​e​d​ ​(​h​o​s​t​n​a​m​e​C​S​t​r​)​;​

 ​ ​ ​ ​/​/​ ​U​T​F​-​8​ ​l​e​n​g​t​h​ ​c​o​u​l​d​ ​b​e​ ​g​r​e​a​t​e​r​ ​t​h​a​n​ ​t​h​e​ ​n​u​m​b​e​r​ ​o​f​ ​c​h​a​r​a​c​t​e​r​s​.​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​n​a​m​e​ ​=​ ​[​u​s​e​r​n​a​m​e​ ​U​T​F​8​S​t​r​i​n​g​]​;​
 ​ ​ ​ ​N​S​U​I​n​t​e​g​e​r​ ​n​a​m​e​l​e​n​ ​=​ ​s​t​r​l​e​n​ ​(​n​a​m​e​)​;​

 ​ ​ ​ ​i​n​t​ ​n​w​r​i​t​t​e​n​ ​=​ ​W​r​i​t​e​M​e​s​s​a​g​e​ ​(​_​s​o​c​k​f​d​,​ ​n​a​m​e​,​ ​n​a​m​e​l​e​n​)​;​

 ​ ​ ​ ​i​f​ ​(​n​w​r​i​t​t​e​n​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​M​e​s​s​a​g​e​ ​=​ ​@​"​F​a​i​l​e​d​ ​t​o​ ​s​e​n​d​ ​u​s​e​r​n​a​m​e​.​"​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​y​s​E​r​r​o​r​ ​=​ ​e​r​r​n​o​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​M​a​k​e​ ​t​h​e​ ​s​o​c​k​e​t​ ​n​o​n​-​b​l​o​c​k​i​n​g​.​
 ​ ​ ​ ​i​n​t​ ​e​r​r​ ​=​ ​f​c​n​t​l​ ​(​_​s​o​c​k​f​d​,​ ​F​_​S​E​T​F​L​,​ ​O​_​N​O​N​B​L​O​C​K​)​;​
 ​ ​ ​ ​i​f​ ​(​e​r​r​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​M​e​s​s​a​g​e​ ​=​ ​@​"​C​o​u​l​d​ ​n​o​t​ ​p​u​t​ ​s​o​c​k​e​t​ ​i​n​t​o​ ​n​o​n​b​l​o​c​k​i​n​g​ ​m​o​d​e​.​"​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​y​s​E​r​r​o​r​ ​=​ ​e​r​r​n​o​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​[​s​e​l​f​ ​s​t​a​r​t​M​o​n​i​t​o​r​i​n​g​S​o​c​k​e​t​]​;​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​i​f​ ​(​e​r​r​o​r​M​e​s​s​a​g​e​ ​!​=​ ​n​i​l​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​r​u​n​E​r​r​o​r​M​e​s​s​a​g​e​:​ ​e​r​r​o​r​M​e​s​s​a​g​e​ ​ ​w​i​t​h​E​r​r​n​o​:​ ​s​y​s​E​r​r​o​r​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​c​l​o​s​e​C​o​n​n​e​c​t​i​o​n​]​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​;​
}​ ​/​/​ ​c​o​n​n​e​c​t​T​o​H​o​s​t​:​ ​a​s​U​s​e​r​:​

-​ ​(​v​o​i​d​)​ ​c​l​o​s​e​C​o​n​n​e​c​t​i​o​n​ ​{​
 ​ ​ ​ ​[​s​e​l​f​ ​s​t​o​p​M​o​n​i​t​o​r​i​n​g​S​o​c​k​e​t​]​;​
 ​ ​ ​ ​c​l​o​s​e​ ​(​_​s​o​c​k​f​d​)​;​
 ​ ​ ​ ​_​s​o​c​k​f​d​ ​=​ ​k​I​n​v​a​l​i​d​S​o​c​k​e​t​;​
}​ ​/​/​ ​c​l​o​s​e​C​o​n​n​e​c​t​i​o​n​

-​ ​(​v​o​i​d​)​ ​s​t​a​r​t​M​o​n​i​t​o​r​i​n​g​S​o​c​k​e​t​ ​{​
 ​ ​ ​ ​C​F​S​o​c​k​e​t​C​o​n​t​e​x​t​ ​c​o​n​t​e​x​t​ ​=​ ​{​ ​0​,​ ​s​e​l​f​,​ ​N​U​L​L​,​ ​N​U​L​L​,​ ​N​U​L​L​ ​}​;​
 ​ ​ ​ ​_​s​o​c​k​e​t​R​e​f​ ​=​ ​C​F​S​o​c​k​e​t​C​r​e​a​t​e​W​i​t​h​N​a​t​i​v​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​_​s​o​c​k​f​d​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​C​F​S​o​c​k​e​t​D​a​t​a​C​a​l​l​B​a​c​k​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​R​e​c​e​i​v​e​M​e​s​s​a​g​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​&​c​o​n​t​e​x​t​)​;​
 ​ ​ ​ ​i​f​ ​(​_​s​o​c​k​e​t​R​e​f​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​r​u​n​E​r​r​o​r​M​e​s​s​a​g​e​:​ ​@​"​U​n​a​b​l​e​ ​t​o​ ​c​r​e​a​t​e​ ​C​F​S​o​c​k​e​t​R​e​f​.​"​ ​ ​w​i​t​h​E​r​r​n​o​:​ ​n​o​E​r​r​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​C​F​R​u​n​L​o​o​p​S​o​u​r​c​e​R​e​f​ ​r​l​s​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​C​F​S​o​c​k​e​t​C​r​e​a​t​e​R​u​n​L​o​o​p​S​o​u​r​c​e​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​ ​_​s​o​c​k​e​t​R​e​f​,​ ​0​)​;​
 ​ ​ ​ ​i​f​ ​(​r​l​s​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​r​u​n​E​r​r​o​r​M​e​s​s​a​g​e​:​ ​@​"​U​n​a​b​l​e​ ​t​o​ ​c​r​e​a​t​e​ ​s​o​c​k​e​t​ ​r​u​n​ ​l​o​o​p​ ​s​o​u​r​c​e​.​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​t​h​E​r​r​n​o​:​ ​n​o​E​r​r​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​C​F​R​u​n​L​o​o​p​A​d​d​S​o​u​r​c​e​ ​(​C​F​R​u​n​L​o​o​p​G​e​t​C​u​r​r​e​n​t​(​)​,​ ​r​l​s​,​ ​k​C​F​R​u​n​L​o​o​p​D​e​f​a​u​l​t​M​o​d​e​)​;​
 ​ ​ ​ ​C​F​R​e​l​e​a​s​e​ ​(​r​l​s​)​;​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​r​e​t​u​r​n​;​

}​ ​/​/​ ​s​t​a​r​t​M​o​n​i​t​o​r​i​n​g​S​o​c​k​e​t​

-​ ​(​v​o​i​d​)​ ​s​t​o​p​M​o​n​i​t​o​r​i​n​g​S​o​c​k​e​t​ ​{​
 ​ ​ ​ ​i​f​ ​(​s​o​c​k​e​t​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​C​F​S​o​c​k​e​t​I​n​v​a​l​i​d​a​t​e​ ​(​_​s​o​c​k​e​t​R​e​f​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​C​F​R​e​l​e​a​s​e​ ​(​_​s​o​c​k​e​t​R​e​f​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​s​o​c​k​e​t​R​e​f​ ​=​ ​N​U​L​L​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​s​t​o​p​M​o​n​i​t​o​r​i​n​g​S​o​c​k​e​t​

s​t​a​t​i​c​ ​v​o​i​d​ ​R​e​c​e​i​v​e​M​e​s​s​a​g​e​ ​(​C​F​S​o​c​k​e​t​R​e​f​ ​s​o​c​k​e​t​,​ ​C​F​S​o​c​k​e​t​C​a​l​l​B​a​c​k​T​y​p​e​ ​t​y​p​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​D​a​t​a​R​e​f​ ​a​d​d​r​e​s​s​,​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​d​a​t​a​,​ ​v​o​i​d​ ​*​i​n​f​o​)​ ​{​
 ​ ​ ​ ​C​F​C​h​a​t​t​e​r​C​l​i​e​n​t​A​p​p​D​e​l​e​g​a​t​e​ ​*​s​e​l​f​ ​=​ ​i​n​f​o​;​
 ​ ​ ​ ​[​s​e​l​f​ ​h​a​n​d​l​e​M​e​s​s​a​g​e​D​a​t​a​:​ ​(​N​S​D​a​t​a​ ​*​)​ ​d​a​t​a​]​;​
}​ ​/​/​ ​R​e​c​e​i​v​e​M​e​s​s​a​g​e​

-​ ​(​v​o​i​d​)​ ​h​a​n​d​l​e​M​e​s​s​a​g​e​D​a​t​a​:​ ​(​N​S​D​a​t​a​ ​*​)​ ​d​a​t​a​ ​{​
 ​ ​ ​ ​/​/​ ​C​l​o​s​e​d​ ​c​o​n​n​e​c​t​i​o​n​?​
 ​ ​ ​ ​i​f​ ​(​d​a​t​a​.​l​e​n​g​t​h​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​c​l​o​s​e​C​o​n​n​e​c​t​i​o​n​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​r​u​n​E​r​r​o​r​M​e​s​s​a​g​e​:​ ​@​"​T​h​e​ ​s​e​r​v​e​r​ ​c​l​o​s​e​d​ ​t​h​e​ ​c​o​n​n​e​c​t​i​o​n​.​"​ ​w​i​t​h​E​r​r​n​o​:​ ​n​o​E​r​r​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​N​u​l​l​-​t​e​r​m​i​n​a​t​e​ ​t​h​e​ ​d​a​t​a​.​
 ​ ​ ​ ​N​S​M​u​t​a​b​l​e​D​a​t​a​ ​*​m​e​s​s​a​g​e​D​a​t​a​ ​=​ ​[​N​S​M​u​t​a​b​l​e​D​a​t​a​ ​d​a​t​a​W​i​t​h​D​a​t​a​:​ ​d​a​t​a​]​;​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​N​U​L​ ​=​ ​'​\​0​'​;​
 ​ ​ ​ ​[​m​e​s​s​a​g​e​D​a​t​a​ ​a​p​p​e​n​d​B​y​t​e​s​:​ ​&​N​U​L​ ​l​e​n​g​t​h​:​ ​1​]​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​a​ ​s​t​r​i​n​g​ ​t​o​ ​d​i​s​p​l​a​y​.​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​m​e​s​s​a​g​e​ ​=​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​U​T​F​8​S​t​r​i​n​g​:​ ​m​e​s​s​a​g​e​D​a​t​a​.​b​y​t​e​s​]​;​
 ​ ​ ​ ​i​f​ ​(​m​e​s​s​a​g​e​ ​=​=​ ​n​i​l​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​r​u​n​E​r​r​o​r​M​e​s​s​a​g​e​:​ ​@​"​E​r​r​o​r​ ​r​e​a​d​i​n​g​ ​f​r​o​m​ ​s​e​r​v​e​r​.​"​ ​ ​w​i​t​h​E​r​r​n​o​:​ ​n​o​E​r​r​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​[​s​e​l​f​ ​a​p​p​e​n​d​M​e​s​s​a​g​e​:​ ​m​e​s​s​a​g​e​]​;​
}​ ​/​/​ ​h​a​n​d​l​e​M​e​s​s​a​g​e​D​a​t​a​:​

@​e​n​d​ ​/​/​ ​C​F​C​h​a​t​t​e​r​C​l​i​e​n​t​A​p​p​D​e​l​e​g​a​t​e​

Edit the xib file and connect each of the app delegate’s outlets to the appropriate view. Then, set each outlet of the app delegate to point to the appropriate view.

Next, rig up the connection management UI. Set the Join button to trigger the -join: action. Set the username and hostname text fields to trigger the button’s -performClick: action.

Finally, set up the chat portion of the UI. The message text field should trigger the Send button’s -performClick: action. The Send button should trigger the -sendMessage: action. Save the xib file and return to Xcode.

Start the chatterserver from the networking chapter, then build and run CFChatterClient. Notice how the joinLeaveButton’s label and action are changed from "Join" to "Leave" by -updateUI based on whether the client is connected or not.

Runloop Chatter Server

GUI applications aren’t the only ones that can benefit from being runloop-driven. Here you will make some changes to chatterserver so that it uses a runloop at its core rather than select(). You’ll perform some surgery to existing functions and add some new ones. There won’t be any changes to the fundamental data structures or architecture of chatterserver. While the behavior of the server won’t change, the changes you do make will set the stage for adding Bonjour support in a later chapter.

Make a copy of chatterserver.m and name it chatterserver-runloop.m. The first thing to change is the signature and the first several lines of AcceptConnection. The runloop will call accept() for us, so all we need to do is get the remote address using getpeername(). The function will return a pointer to the ChatterUser that will be allocated for the new user, so you will need to move the ChatterUser variable to the top of the function. Finally, return the new user at the end.

s​t​a​t​i​c​ ​C​h​a​t​t​e​r​U​s​e​r​ ​*​A​c​c​e​p​t​C​o​n​n​e​c​t​i​o​n​ ​(​i​n​t​ ​a​c​c​e​p​t​e​d​F​d​)​ ​{​
 ​ ​ ​ ​C​h​a​t​t​e​r​U​s​e​r​ ​*​n​e​w​U​s​e​r​ ​=​ ​N​U​L​L​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​s​t​o​r​a​g​e​ ​a​d​d​r​;​
 ​ ​ ​ ​s​o​c​k​l​e​n​_​t​ ​a​d​d​r​_​l​e​n​ ​=​ ​s​i​z​e​o​f​(​a​d​d​r​)​;​

 ​ ​ ​ ​i​n​t​ ​c​l​i​e​n​t​F​d​ ​=​ ​a​c​c​e​p​t​e​d​F​d​;​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​g​e​t​p​e​e​r​n​a​m​e​ ​(​c​l​i​e​n​t​F​d​,​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​ ​*​)​&​a​d​d​r​,​ ​&​d​d​r​_​l​e​n​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​*​*​*​ ​g​e​t​p​e​e​r​n​a​m​e​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​i​f​ ​(​c​l​i​e​n​t​F​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​*​*​*​ ​a​c​c​e​p​t​"​)​;​
 ​ ​ ​ ​.​.​.​
 ​ ​ ​ ​s​y​s​l​o​g​ ​(​L​O​G​_​N​O​T​I​C​E​,​ ​"​A​c​c​e​p​t​e​d​ ​c​o​n​n​e​c​t​i​o​n​ ​f​r​o​m​ ​%​s​ ​p​o​r​t​ ​%​d​ ​a​s​ ​f​d​ ​%​d​.​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​a​m​e​,​ ​p​o​r​t​,​ ​c​l​i​e​n​t​F​d​)​;​
b​a​i​l​o​u​t​:​
 ​ ​ ​ ​i​f​ ​(​c​l​i​e​n​t​F​d​ ​!​=​ ​-​1​)​ ​c​l​o​s​e​ ​(​c​l​i​e​n​t​F​d​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​n​e​w​U​s​e​r​;​
}​ ​ ​/​/​ ​A​c​c​e​p​t​C​o​n​n​e​c​t​i​o​n​

Next, add a function to register a file descriptor with the runloop, along with two callbacks. listenCallback gets a new socket from accept() and passes it to listenCallback. userCallback is called when there’s new data to be read. The runloop is told when registering the user callback to not read the socket on our behalf.

When userCallback is called, it gets the pointer to our User, and passes it on to HandleRead which does the heavy lifting of processing messages from the users.

s​t​a​t​i​c​ ​v​o​i​d​ ​R​e​g​i​s​t​e​r​R​u​n​L​o​o​p​S​o​c​k​e​t​ ​(​i​n​t​ ​l​i​s​t​e​n​F​d​,​ ​C​F​S​o​c​k​e​t​C​a​l​l​B​a​c​k​ ​c​a​l​l​b​a​c​k​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​B​O​O​L​ ​l​i​s​t​e​n​,​ ​C​h​a​t​t​e​r​U​s​e​r​ ​*​u​s​e​r​)​ ​{​
 ​ ​ ​ ​C​F​S​o​c​k​e​t​C​o​n​t​e​x​t​ ​c​o​n​t​e​x​t​ ​=​ ​{​ ​.​i​n​f​o​ ​=​ ​u​s​e​r​ ​}​;​

 ​ ​ ​ ​C​F​S​o​c​k​e​t​R​e​f​ ​s​o​c​k​e​t​R​e​f​;​
 ​ ​ ​ ​i​f​ ​(​l​i​s​t​e​n​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​o​c​k​e​t​R​e​f​ ​=​ ​C​F​S​o​c​k​e​t​C​r​e​a​t​e​W​i​t​h​N​a​t​i​v​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​l​i​s​t​e​n​F​d​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​C​F​S​o​c​k​e​t​A​c​c​e​p​t​C​a​l​l​B​a​c​k​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​a​l​l​b​a​c​k​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​&​c​o​n​t​e​x​t​)​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​o​c​k​e​t​R​e​f​ ​=​ ​C​F​S​o​c​k​e​t​C​r​e​a​t​e​W​i​t​h​N​a​t​i​v​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​l​i​s​t​e​n​F​d​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​C​F​S​o​c​k​e​t​R​e​a​d​C​a​l​l​B​a​c​k​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​a​l​l​b​a​c​k​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​&​c​o​n​t​e​x​t​)​;​

 ​ ​ ​ ​}​

 ​ ​ ​ ​C​F​R​u​n​L​o​o​p​S​o​u​r​c​e​R​e​f​ ​r​l​s​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​C​F​S​o​c​k​e​t​C​r​e​a​t​e​R​u​n​L​o​o​p​S​o​u​r​c​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​ ​s​o​c​k​e​t​R​e​f​,​ ​0​)​;​

 ​ ​ ​ ​C​F​R​u​n​L​o​o​p​A​d​d​S​o​u​r​c​e​ ​(​C​F​R​u​n​L​o​o​p​G​e​t​C​u​r​r​e​n​t​(​)​,​ ​r​l​s​,​ ​k​C​F​R​u​n​L​o​o​p​C​o​m​m​o​n​M​o​d​e​s​)​;​

 ​ ​ ​ ​C​F​R​e​l​e​a​s​e​ ​(​r​l​s​)​;​
 ​ ​ ​ ​C​F​R​e​l​e​a​s​e​ ​(​s​o​c​k​e​t​R​e​f​)​;​

}​ ​/​/​ ​R​e​g​i​s​t​e​r​R​u​n​L​o​o​p​S​o​c​k​e​t​

s​t​a​t​i​c​ ​v​o​i​d​ ​u​s​e​r​C​a​l​l​b​a​c​k​ ​(​C​F​S​o​c​k​e​t​R​e​f​ ​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​S​o​c​k​e​t​C​a​l​l​B​a​c​k​T​y​p​e​ ​t​y​p​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​D​a​t​a​R​e​f​ ​a​d​d​r​e​s​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​d​a​t​a​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​v​o​i​d​ ​*​i​n​f​o​)​ ​{​
 ​ ​ ​ ​H​a​n​d​l​e​R​e​a​d​ ​(​(​C​h​a​t​t​e​r​U​s​e​r​ ​*​)​i​n​f​o​)​;​
}​ ​/​/​ ​u​s​e​r​C​a​l​l​b​a​c​k​

s​t​a​t​i​c​ ​v​o​i​d​ ​l​i​s​t​e​n​C​a​l​l​b​a​c​k​ ​(​C​F​S​o​c​k​e​t​R​e​f​ ​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​S​o​c​k​e​t​C​a​l​l​B​a​c​k​T​y​p​e​ ​t​y​p​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​D​a​t​a​R​e​f​ ​a​d​d​r​e​s​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​d​a​t​a​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​v​o​i​d​ ​*​i​n​f​o​)​ ​{​
 ​ ​ ​ ​C​F​S​o​c​k​e​t​N​a​t​i​v​e​H​a​n​d​l​e​ ​f​d​ ​=​ ​*​(​C​F​S​o​c​k​e​t​N​a​t​i​v​e​H​a​n​d​l​e​ ​*​)​ ​d​a​t​a​;​
 ​ ​ ​ ​C​h​a​t​t​e​r​U​s​e​r​ ​*​u​s​e​r​ ​=​ ​A​c​c​e​p​t​C​o​n​n​e​c​t​i​o​n​ ​(​f​d​)​;​
 ​ ​ ​ ​R​e​g​i​s​t​e​r​R​u​n​L​o​o​p​S​o​c​k​e​t​ ​(​f​d​,​ ​u​s​e​r​C​a​l​l​b​a​c​k​,​ ​N​O​,​ ​u​s​e​r​)​;​
}​ ​/​/​ ​l​i​t​e​n​C​a​l​l​b​a​c​k​

Finally, main() comes under the knife. Add an autorelease pool to the top and then register the runloop socket. Cut out the entire while loop and replace it with a -run call on the current run loop.

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​i​n​t​ ​e​x​i​t​_​s​t​a​t​u​s​ ​=​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​c​o​n​s​t​ ​b​o​o​l​ ​u​s​e​I​P​v​6​ ​=​ ​(​a​r​g​c​ ​>​ ​2​)​;​

 ​ ​ ​ ​i​n​t​ ​l​i​s​t​e​n​F​d​ ​=​ ​S​t​a​r​t​L​i​s​t​e​n​i​n​g​ ​(​u​s​e​I​P​v​6​)​;​

 ​ ​ ​ ​i​f​ ​(​l​i​s​t​e​n​F​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​*​*​*​ ​C​o​u​l​d​ ​n​o​t​ ​o​p​e​n​ ​l​i​s​t​e​n​i​n​g​ ​s​o​c​k​e​t​.​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​R​e​g​i​s​t​e​r​R​u​n​L​o​o​p​S​o​c​k​e​t​ ​(​l​i​s​t​e​n​F​d​,​ ​l​i​s​t​e​n​C​a​l​l​b​a​c​k​,​ ​Y​E​S​,​ ​N​U​L​L​)​;​

 ​ ​ ​ ​/​/​ ​B​l​o​c​k​ ​S​I​G​P​I​P​E​ ​s​o​ ​a​ ​d​r​o​p​p​e​d​ ​c​o​n​n​e​c​t​i​o​n​ ​w​o​n​'​t​ ​s​i​g​n​a​l​ ​u​s​.​
 ​ ​ ​ ​s​t​r​u​c​t​ ​s​i​g​a​c​t​i​o​n​ ​a​c​t​;​
 ​ ​ ​ ​a​c​t​.​s​a​_​h​a​n​d​l​e​r​ ​=​ ​S​I​G​_​I​G​N​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​s​i​g​a​c​t​i​o​n​ ​o​a​c​t​;​
 ​ ​ ​ ​i​n​t​ ​e​r​r​ ​=​ ​s​i​g​a​c​t​i​o​n​ ​(​S​I​G​P​I​P​E​,​ ​&​a​c​t​,​ ​&​o​a​c​t​)​;​
 ​ ​ ​ ​i​f​ ​(​e​r​r​ ​=​=​ ​-​1​)​ ​p​e​r​r​o​r​ ​(​"​s​i​g​a​c​t​i​o​n​(​S​I​G​P​I​P​E​,​ ​S​I​G​_​I​G​N​)​"​)​;​

 ​ ​ ​ ​/​/​ ​A​x​e​ ​e​v​e​r​y​t​h​i​n​g​ ​h​e​r​e​.​ ​ ​R​e​a​l​l​y​!​

 ​ ​ ​ ​[​[​N​S​R​u​n​L​o​o​p​ ​c​u​r​r​e​n​t​R​u​n​L​o​o​p​]​ ​r​u​n​]​;​

 ​ ​ ​ ​e​x​i​t​_​s​t​a​t​u​s​ ​=​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​e​x​i​t​_​s​t​a​t​u​s​;​
}​ ​/​/​ ​m​a​i​n​

You should be able compile and run your server. You should be able to connect to this server both from the command-line client as well as the GUI client.

The System Configuration Framework

The user is free to change aspects of their system configuration at any time, such as changing their network location from “Office” to “Home.” Changing the network location could change a number of lower-level settings, such as which IP addresses the machine has available and which DNS servers to use. There are also system parameters that change dynamically, such as the battery level in laptops. In some situations, your application needs to be able to react to these changes. For example, if you have a network socket listening on a particular IP address (as opposed to listening on all of them), you’ll need to listen on the new IP if the user changes it.

In Mac OS X 10.1, Apple introduced the system configuration framework, which provides an architecture for storing configuration and run-time information. Currently, it only supports network configuration and the battery monitor, but in the future more information could be supported.

Architecture

At the heart of the system configuration framework is configd, the system configuration daemon. configd holds the dynamic store, which is an online database of configuration information consisting of key-value pairs. These pairs are arranged in a hierarchy, like nested dictionaries, and are addressed by paths similar to the URLs used to identify Web pages. There are two major spaces, one for “setup” information (preferences set by configuration applications) and one for “state” information (the actual current system state). The keys look like State:/Network/Service/ServiceUUID/IPv4 for the current IP state and Setup:/Network/Service/ServiceUUID/PPP for PPP configuration, where ServiceUUID is a 128-bit unique ID.

Much of the persistent configuration information is stored in property lists under the /Library/Preferences/SystemConfiguration/ directory. configd reads these files and places their contents into the dynamic store. There are also configuration agents, which are bundles that configd loads that provide configuration and notification services. Currently, there are agents to monitor the preferences, to monitor the kernel, and to track the state of all network interfaces, as well as a PPP controller.

Basic API

The system configuration framework source code is included in Darwin if you are interested in poking around and seeing how things work. The API lives at the Core Foundation level, so it has a lot in common with the rest of the Core Foundation API. The examples here that query the dynamic store will be primarily Foundation-based and take advantage of the toll-free bridging between many equivalent Core Foundation and Foundation data structures. Toll-free bridging lets you manipulate a CF*Ref reference as its equivalent NS* object and vice-versa. Some common toll-free-bridged types are CFStringRef and NSString, CFArrayRef and NSArray, and CFDataRef and NSData. Take care not to assume that all such pairs of types are toll-free bridged. For example, the pairs CFBundleRef / NSBundle and CFRunLoopRef / NSRunLoop aren't toll-free bridged. When in doubt, check the documentation.

To query the dynamic store, perform these steps:

Connect to configd’s dynamic store with SCDynamicStoreCreate().

Construct some access keys, either by explicitly constructing the paths or by supplying a regular expression to SCDynamicStoreCopyKeyList().

Iterate over the keys and call SCDynamicStoreCopyValue() to fetch each one’s value. The system configuration could change in the course of your iteration; if this is a concern, use SCDynamicStoreCopyMultiple() to atomically fetch the requested values at once. This will provide a consistent snapshot of the keys’ values.

If you want to be notified when a particular value changes, use SCDynamicStoreSetNotificationKeys() to tell the system which keys you are interested in.

Here are the calls in more detail.

S​C​D​y​n​a​m​i​c​S​t​o​r​e​R​e​f​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​r​e​a​t​e​ ​(​C​F​A​l​l​o​c​a​t​o​r​R​e​f​ ​a​l​l​o​c​a​t​o​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​S​t​r​i​n​g​R​e​f​ ​n​a​m​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​a​l​l​B​a​c​k​ ​c​a​l​l​b​a​c​k​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​o​n​t​e​x​t​ ​*​c​o​n​t​e​x​t​)​;​

SCDynamicStoreCreate() creates a new session to talk to configd. The parameters are:

allocator | The allocator used to allocate memory for the local object and for any storage it may need. Use kCFAllocatorDefault to use the default memory allocator.
name | A string (which can be cast from an NSString pointer) that names the calling process.
callback | A callback function that will get called when values change for the keys given to SCDynamicStoreSetNotificationKeys().

context

A structure just like CFSocketContext, where you give the version, a pointer, and some function pointers to retain and release functions. From an Objective-C method, you can gain access to the calling object from the callback using a context like this:

 ​ ​ ​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​o​n​t​e​x​t​ ​c​o​n​t​e​x​t​ ​=​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​0​,​ ​s​e​l​f​,​ ​N​U​L​L​,​ ​N​U​L​L​,​ ​N​U​L​L​
 ​ ​ ​ ​}​;​

C​F​A​r​r​a​y​R​e​f​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​o​p​y​K​e​y​L​i​s​t​ ​(​S​C​D​y​n​a​m​i​c​S​t​o​r​e​R​e​f​ ​s​t​o​r​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​S​t​r​i​n​g​R​e​f​ ​p​a​t​t​e​r​n​)​;​

SCDynamicStoreCopyKeyList() returns an array of string keys that match a given regular expression.

store | The dynamic store reference that SCDynamicStoreCreate() returned.
pattern | A regular expression (not the simple shell globs used elsewhere) that matches the keys you’re looking for. For instance, CFSTR(".*") will match everything, and CFSTR("State:/Network/Service/[^/]+/IPv4") will match the IPv4 network services of all service IDs.

C​F​P​r​o​p​e​r​t​y​L​i​s​t​R​e​f​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​o​p​y​V​a​l​u​e​ ​(​S​C​D​y​n​a​m​i​c​S​t​o​r​e​R​e​f​ ​s​t​o​r​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​S​t​r​i​n​g​R​e​f​ ​k​e​y​)​;​

SCDynamicStoreCopyValue() returns either a string or a dictionary with the value requested.

store | The dynamic store reference from SCDynamicStoreCreate().
key | The key representing the data you want, either specified explicitly or taken from SCDynamicStoreCopyKeyList().

C​F​D​i​c​t​i​o​n​a​r​y​R​e​f​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​o​p​y​M​u​l​t​i​p​l​e​ ​(​S​C​D​y​n​a​m​i​c​S​t​o​r​e​R​e​f​ ​s​t​o​r​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​A​r​r​a​y​R​e​f​ ​k​e​y​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​A​r​r​a​y​R​e​f​ ​p​a​t​t​e​r​n​s​)​;​

SCDynamicStoreCopyMultiple() is like SCDynamicStoreCopyValue() but returns the values for a set of keys. It returns a dictionary containing the values.

store | The dynamic store reference from SCDynamicStoreCreate().
keys | The set of keys that indicate the values to return.
patterns | Regular expression patterns to match keys in the dynamic store.

B​o​o​l​e​a​n​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​S​e​t​N​o​t​i​f​i​c​a​t​i​o​n​K​e​y​s​ ​(​S​C​D​y​n​a​m​i​c​S​t​o​r​e​R​e​f​ ​s​t​o​r​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​A​r​r​a​y​R​e​f​ ​k​e​y​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​A​r​r​a​y​R​e​f​ ​p​a​t​t​e​r​n​s​)​;​

SCDynamicStoreSetNotificationKeys() tells the dynamic store which keys and patterns of keys are interesting. When the values change, the callback function specified with SCDynamicStoreCreate() is called.

store | The dynamic store connection.
keys | The keys of interest.
patterns | Regular expression indicating more keys of interest.

The callback function should look like this:

void StoreCallback (SCDynamicStoreRef store, CFArrayRef changedKeys,
                    void *info);

store | The dynamic store the callback has been associated with.
changedKeys | An array of keys that have new values.
info | The pointer specified in the second field of the context passed to SCDynamicStoreCreate().

System Configuration framework provides convenience functions to simplify retrieving common values from the store. SCDynamicStoreCopyLocalHostName() gets the current host name. SCDynamicStoreKeyCreateHostNames() returns the key needed to register using SCDynamicStoreSetNotificationkeys() to receive notification of changes to the HostNames entity, which includes the local host name.

Seeing all values

Example 15.3 is a Foundation tool that will show all of the keys and values in configd’s dynamic store:

Example 15.3. scf-dump.m

/​/​ ​s​c​f​-​d​u​m​p​.​m​ ​-​-​ ​D​u​m​p​s​ ​t​h​e​ ​c​u​r​r​e​n​t​ ​s​t​a​t​e​ ​o​f​ ​t​h​e​ ​S​y​s​t​e​m​ ​C​o​n​f​i​g​u​r​a​t​i​o​n​ ​d​y​n​a​m​i​c​ ​s​t​o​r​e​.​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​f​r​a​m​e​w​o​r​k​ ​S​y​s​t​e​m​C​o​n​f​i​g​u​r​a​t​i​o​n​
/​/​ ​ ​ ​ ​ ​-​o​ ​s​c​f​-​d​u​m​p​ ​s​c​f​-​d​u​m​p​.​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​
#​i​m​p​o​r​t​ ​<​S​y​s​t​e​m​C​o​n​f​i​g​u​r​a​t​i​o​n​/​S​y​s​t​e​m​C​o​n​f​i​g​u​r​a​t​i​o​n​.​h​>​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​ ​{​
 ​ ​ ​ ​C​F​A​r​r​a​y​R​e​f​ ​p​a​t​t​e​r​n​s​ ​=​ ​N​U​L​L​;​
 ​ ​ ​ ​C​F​D​i​c​t​i​o​n​a​r​y​R​e​f​ ​s​n​a​p​s​h​o​t​ ​=​ ​N​U​L​L​;​
 ​ ​ ​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​R​e​f​ ​s​t​o​r​e​ ​=​ ​N​U​L​L​;​

 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​/​/​ ​C​o​n​n​e​c​t​ ​t​o​ ​c​o​n​f​i​g​d​.​
 ​ ​ ​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​o​n​t​e​x​t​ ​c​o​n​t​e​x​t​ ​=​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​0​,​ ​N​U​L​L​,​ ​N​U​L​L​,​ ​N​U​L​L​,​ ​N​U​L​L​
 ​ ​ ​ ​}​;​

 ​ ​ ​ ​s​t​o​r​e​ ​=​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​r​e​a​t​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​ ​C​F​S​T​R​(​"​c​o​m​.​a​m​o​s​x​.​s​c​f​-​d​u​m​p​"​)​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​U​L​L​,​ ​/​/​ ​c​a​l​l​b​a​c​k​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​&​c​o​n​t​e​x​t​)​;​
 ​ ​ ​ ​i​f​ ​(​s​t​o​r​e​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​*​*​*​ ​U​n​a​b​l​e​ ​t​o​ ​c​o​n​n​e​c​t​ ​t​o​ ​d​y​n​a​m​i​c​ ​s​t​o​r​e​.​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​C​o​p​y​ ​a​l​l​ ​k​e​y​s​ ​a​n​d​ ​v​a​l​u​e​s​.​
 ​ ​ ​ ​/​/​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​o​p​y​M​u​l​t​i​p​l​e​(​)​ ​l​e​t​s​ ​y​o​u​ ​s​i​m​u​l​t​a​n​e​o​u​s​l​y​ ​r​e​q​u​e​s​t​ ​v​a​l​u​e​s​ ​f​o​r​
 ​ ​ ​ ​/​/​ ​b​o​t​h​ ​s​p​e​c​i​f​i​c​ ​k​e​y​s​ ​a​n​d​ ​a​l​l​ ​k​e​y​s​ ​m​a​t​c​h​i​n​g​ ​a​ ​l​i​s​t​ ​o​f​ ​p​a​t​t​e​r​n​s​.​

 ​ ​ ​ ​/​/​ ​B​u​i​l​d​ ​t​h​e​ ​p​a​t​t​e​r​n​s​ ​a​r​r​a​y​.​
 ​ ​ ​ ​/​/​ ​U​s​e​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​ ​i​n​s​t​e​a​d​ ​o​f​ ​C​F​S​t​r​i​n​g​R​e​f​ ​t​o​ ​a​v​o​i​d​ ​c​a​s​t​ ​i​n​ ​C​F​A​r​r​a​y​C​r​e​a​t​e​(​)​.​
 ​ ​ ​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​m​a​t​c​h​A​l​l​R​e​g​e​x​ ​=​ ​C​F​S​T​R​(​"​.​*​"​)​;​
 ​ ​ ​ ​p​a​t​t​e​r​n​s​ ​=​ ​C​F​A​r​r​a​y​C​r​e​a​t​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​ ​&​m​a​t​c​h​A​l​l​R​e​g​e​x​,​ ​1​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​&​k​C​F​T​y​p​e​A​r​r​a​y​C​a​l​l​B​a​c​k​s​)​;​
 ​ ​ ​ ​i​f​ ​(​p​a​t​t​e​r​n​s​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​*​*​*​ ​U​n​a​b​l​e​ ​t​o​ ​c​r​e​a​t​e​ ​k​e​y​ ​p​a​t​t​e​r​n​ ​a​r​r​a​y​.​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​P​e​r​f​o​r​m​ ​t​h​e​ ​c​o​p​y​.​
 ​ ​ ​ ​s​n​a​p​s​h​o​t​ ​=​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​o​p​y​M​u​l​t​i​p​l​e​ ​(​s​t​o​r​e​,​ ​N​U​L​L​,​ ​p​a​t​t​e​r​n​s​)​;​

 ​ ​ ​ ​i​f​ ​(​s​n​a​p​s​h​o​t​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​(​@​"​*​*​*​ ​U​n​a​b​l​e​ ​t​o​ ​c​o​p​y​ ​k​e​y​s​ ​a​n​d​ ​v​a​l​u​e​s​ ​f​r​o​m​ ​d​y​n​a​m​i​c​ ​s​t​o​r​e​.​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​U​s​e​ ​t​o​l​l​-​f​r​e​e​ ​b​r​i​d​g​i​n​g​ ​t​o​ ​g​e​t​ ​a​ ​d​e​s​c​r​i​p​t​i​o​n​.​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​d​e​s​c​ ​=​ ​[​(​i​d​)​s​n​a​p​s​h​o​t​ ​d​e​s​c​r​i​p​t​i​o​n​I​n​S​t​r​i​n​g​s​F​i​l​e​F​o​r​m​a​t​]​;​

 ​ ​ ​ ​/​/​ ​|​d​e​s​c​|​ ​a​l​r​e​a​d​y​ ​e​n​d​s​ ​i​n​ ​a​ ​n​e​w​l​i​n​e​,​ ​s​o​ ​u​s​e​ ​f​p​u​t​s​(​)​ ​i​n​s​t​e​a​d​ ​o​f​ ​p​u​t​s​(​)​
 ​ ​ ​ ​/​/​ ​t​o​ ​a​v​o​i​d​ ​a​p​p​e​n​d​i​n​g​ ​a​n​o​t​h​e​r​.​
 ​ ​ ​ ​f​p​u​t​s​ ​(​[​d​e​s​c​ ​U​T​F​8​S​t​r​i​n​g​]​,​ ​s​t​d​o​u​t​)​;​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​i​f​ ​(​p​a​t​t​e​r​n​s​)​ ​C​F​R​e​l​e​a​s​e​ ​(​p​a​t​t​e​r​n​s​)​;​
 ​ ​ ​ ​i​f​ ​(​s​n​a​p​s​h​o​t​)​ ​C​F​R​e​l​e​a​s​e​ ​(​s​n​a​p​s​h​o​t​)​;​
 ​ ​ ​ ​i​f​ ​(​s​t​o​r​e​)​ ​C​F​R​e​l​e​a​s​e​ ​(​s​t​o​r​e​)​;​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​ ​/​/​ ​m​a​i​n​

and part of a sample run:

$​ ​.​/​s​c​f​-​d​u​m​p​
"​S​t​a​t​e​:​/​N​e​t​w​o​r​k​/​I​n​t​e​r​f​a​c​e​/​e​n​0​/​I​P​v​6​"​ ​=​ ​{​
 ​ ​ ​ ​A​d​d​r​e​s​s​e​s​ ​=​ ​ ​ ​ ​ ​(​
 ​ ​ ​ ​ ​ ​ ​ ​"​f​e​8​0​:​:​2​1​e​:​c​2​f​f​:​f​e​1​4​:​5​e​b​b​"​
 ​ ​ ​ ​)​;​
 ​ ​ ​ ​F​l​a​g​s​ ​=​ ​ ​ ​ ​ ​(​
 ​ ​ ​ ​ ​ ​ ​ ​0​
 ​ ​ ​ ​)​;​
 ​ ​ ​ ​P​r​e​f​i​x​L​e​n​g​t​h​ ​=​ ​ ​ ​ ​ ​(​
 ​ ​ ​ ​ ​ ​ ​ ​6​4​
 ​ ​ ​ ​)​;​
}​;​
"​S​e​t​u​p​:​/​N​e​t​w​o​r​k​/​S​e​r​v​i​c​e​/​B​4​F​9​7​D​7​7​-​F​D​6​B​-​4​E​B​7​-​8​3​4​8​-​C​A​F​6​C​B​0​2​9​8​6​C​/​I​P​v​6​"​ ​=​ ​{​
 ​ ​ ​ ​C​o​n​f​i​g​M​e​t​h​o​d​ ​=​ ​A​u​t​o​m​a​t​i​c​;​
}​;​
"​S​e​t​u​p​:​/​N​e​t​w​o​r​k​/​S​e​r​v​i​c​e​/​C​D​C​A​0​E​2​3​-​7​5​4​6​-​4​1​6​D​-​9​9​A​3​-​3​E​0​C​4​4​A​0​5​5​E​E​/​I​P​v​4​"​ ​=​ ​{​
 ​ ​ ​ ​C​o​n​f​i​g​M​e​t​h​o​d​ ​=​ ​D​H​C​P​;​
}​;​
.​.​.​

Creating SCFMonitor

Now let’s use the System Configuration framework to create the SCFMonitor application shown in Figure 15.3. This application displays the current host name, the person logged in on the console, and all local IP addresses.

Notifications are registered to keep the values up to date, although the console user won’t change without a logout. The System Configuration framework does not provide any notification or information about users logged in via the network.

Figure 15.3  SCFMonitor window

In Xcode, create a new Cocoa Application project called SCFMonitor. Add the System Configuration framework. Then edit the MainMenu.xib file and layout the UI as shown in Figure 15.4 using six static text labels.

Figure 15.4  SCFMonitor static text labels

Figure 15.5 shows the SCFMonitor object diagram. There are three NSTextField outlets in the class: hostnameField, consoleUserField, and localIPField.

Figure 15.5  SCFMonitor object diagram

Edit the app delegate:

Example 15.4. SCFMonitorAppDelegate.h

#​i​m​p​o​r​t​ ​<​C​o​c​o​a​/​C​o​c​o​a​.​h​>​
#​i​m​p​o​r​t​ ​<​S​y​s​t​e​m​C​o​n​f​i​g​u​r​a​t​i​o​n​/​S​y​s​t​e​m​C​o​n​f​i​g​u​r​a​t​i​o​n​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​S​C​F​M​o​n​i​t​o​r​A​p​p​D​e​l​e​g​a​t​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​<​N​S​A​p​p​l​i​c​a​t​i​o​n​D​e​l​e​g​a​t​e​>​ ​{​
 ​ ​ ​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​_​h​o​s​t​n​a​m​e​F​i​e​l​d​;​
 ​ ​ ​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​_​c​o​n​s​o​l​e​U​s​e​r​F​i​e​l​d​;​
 ​ ​ ​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​_​l​o​c​a​l​I​P​F​i​e​l​d​;​

 ​ ​ ​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​R​e​f​ ​_​d​y​n​a​m​i​c​S​t​o​r​e​;​
 ​ ​ ​ ​C​F​R​u​n​L​o​o​p​S​o​u​r​c​e​R​e​f​ ​_​s​t​o​r​e​C​h​a​n​g​e​S​o​u​r​c​e​;​
}​

@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​h​o​s​t​n​a​m​e​F​i​e​l​d​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​c​o​n​s​o​l​e​U​s​e​r​F​i​e​l​d​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​l​o​c​a​l​I​P​F​i​e​l​d​;​

@​e​n​d​ ​/​/​ ​S​C​F​M​o​n​i​t​o​r​A​p​p​D​e​l​e​g​a​t​e​

Set each outlet of the SCFMonitorAppDelegate to point to the appropriate text field. Save the xib file and then add the app delegate:

Example 15.5. SCFMonitorAppDelegate.m

#​i​m​p​o​r​t​ ​"​S​C​F​M​o​n​i​t​o​r​A​p​p​D​e​l​e​g​a​t​e​.​h​"​

/​/​ ​U​s​e​f​u​l​ ​r​e​g​u​l​a​r​ ​e​x​p​r​e​s​s​i​o​n​s​.​ ​ ​S​e​e​ ​r​e​_​f​o​r​m​a​t​(​7​)​ ​f​o​r​ ​d​e​t​a​i​l​s​.​
/​/​ ​M​a​t​c​h​ ​a​n​y​ ​s​e​r​v​i​c​e​'​s​ ​I​P​ ​a​d​d​r​e​s​s​.​
/​/​ ​"​[​^​/​]​+​"​ ​m​e​a​n​s​ ​"​m​a​t​c​h​ ​a​t​ ​l​e​a​s​t​ ​o​n​e​ ​n​o​n​-​s​l​a​s​h​ ​c​h​a​r​a​c​t​e​r​.​"​
#​d​e​f​i​n​e​ ​A​N​Y​_​N​E​T​W​O​R​K​_​S​E​R​V​I​C​E​ ​"​S​t​a​t​e​:​/​N​e​t​w​o​r​k​/​S​e​r​v​i​c​e​/​[​^​/​]​+​"​

/​/​ ​"​[​[​:​d​i​g​i​t​:​]​]​"​ ​m​e​a​n​s​ ​m​a​t​c​h​ ​o​n​e​ ​d​i​g​i​t​.​
#​d​e​f​i​n​e​ ​k​I​P​P​a​t​t​e​r​n​ ​A​N​Y​_​N​E​T​W​O​R​K​_​S​E​R​V​I​C​E​ ​"​/​I​P​v​[​[​:​d​i​g​i​t​:​]​]​"​

s​t​a​t​i​c​ ​v​o​i​d​ ​S​t​o​r​e​D​i​d​C​h​a​n​g​e​(​S​C​D​y​n​a​m​i​c​S​t​o​r​e​R​e​f​,​ ​C​F​A​r​r​a​y​R​e​f​,​ ​v​o​i​d​ ​*​)​;​

@​i​n​t​e​r​f​a​c​e​ ​S​C​F​M​o​n​i​t​o​r​A​p​p​D​e​l​e​g​a​t​e​ ​(​)​

/​/​ ​D​e​c​l​a​r​e​ ​p​r​i​v​a​t​e​ ​a​c​c​e​s​s​o​r​s​.​ ​ ​T​h​e​s​e​ ​d​u​d​e​s​ ​a​l​w​a​y​s​ ​h​i​t​ ​t​h​e​ ​d​y​n​a​m​i​c​ ​s​t​o​r​e​ ​f​o​r​ ​v​a​l​u​e​s​.​
@​p​r​o​p​e​r​t​y​(​r​e​a​d​o​n​l​y​)​ ​N​S​S​t​r​i​n​g​ ​*​h​o​s​t​n​a​m​e​;​
@​p​r​o​p​e​r​t​y​(​r​e​a​d​o​n​l​y​)​ ​N​S​S​t​r​i​n​g​ ​*​c​o​n​s​o​l​e​U​s​e​r​;​
@​p​r​o​p​e​r​t​y​(​r​e​a​d​o​n​l​y​)​ ​N​S​S​t​r​i​n​g​ ​*​l​o​c​a​l​I​P​s​;​

/​/​ ​F​o​r​w​a​r​d​ ​r​e​f​e​r​e​n​c​e​s​
-​ ​(​S​C​D​y​n​a​m​i​c​S​t​o​r​e​R​e​f​)​ ​n​e​w​D​y​n​a​m​i​c​S​t​o​r​e​;​
-​ ​(​v​o​i​d​)​ ​s​e​t​N​o​t​i​f​i​c​a​t​i​o​n​K​e​y​s​;​
-​ ​(​v​o​i​d​)​ ​u​n​s​e​t​N​o​t​i​f​i​c​a​t​i​o​n​K​e​y​s​;​
-​ ​(​v​o​i​d​)​ ​u​p​d​a​t​e​U​I​;​

@​e​n​d​ ​/​/​ ​e​x​t​e​n​s​i​o​n​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​S​C​F​M​o​n​i​t​o​r​A​p​p​D​e​l​e​g​a​t​e​

@​s​y​n​t​h​e​s​i​z​e​ ​h​o​s​t​n​a​m​e​F​i​e​l​d​ ​=​ ​_​h​o​s​t​n​a​m​e​F​i​e​l​d​;​
@​s​y​n​t​h​e​s​i​z​e​ ​c​o​n​s​o​l​e​U​s​e​r​F​i​e​l​d​ ​=​ ​_​c​o​n​s​o​l​e​U​s​e​r​F​i​e​l​d​;​
@​s​y​n​t​h​e​s​i​z​e​ ​l​o​c​a​l​I​P​F​i​e​l​d​ ​=​ ​_​l​o​c​a​l​I​P​F​i​e​l​d​;​

-​ ​(​i​d​)​ ​i​n​i​t​ ​{​
 ​ ​ ​ ​i​f​ ​(​(​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​_​d​y​n​a​m​i​c​S​t​o​r​e​ ​=​ ​[​s​e​l​f​ ​n​e​w​D​y​n​a​m​i​c​S​t​o​r​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​s​e​t​N​o​t​i​f​i​c​a​t​i​o​n​K​e​y​s​]​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​ ​/​/​ ​i​n​i​t​

-​ ​(​v​o​i​d​)​ ​a​w​a​k​e​F​r​o​m​N​i​b​ ​{​
 ​ ​ ​ ​[​s​e​l​f​ ​u​p​d​a​t​e​U​I​]​;​
}​ ​/​/​ ​a​w​a​k​e​F​r​o​m​N​i​b​

-​ ​(​B​O​O​L​)​ ​a​p​p​l​i​c​a​t​i​o​n​S​h​o​u​l​d​T​e​r​m​i​n​a​t​e​A​f​t​e​r​L​a​s​t​W​i​n​d​o​w​C​l​o​s​e​d​:​ ​(​N​S​A​p​p​l​i​c​a​t​i​o​n​ ​*​)​ ​a​p​p​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​Y​E​S​;​
}​ ​/​/​ ​a​p​p​l​i​c​a​t​i​o​n​S​h​o​u​l​d​T​e​r​m​i​n​a​t​e​A​f​t​e​r​L​a​s​t​W​i​n​d​o​w​C​l​o​s​e​d​

-​ ​(​v​o​i​d​)​ ​d​e​a​l​l​o​c​ ​{​
 ​ ​ ​ ​[​s​e​l​f​ ​u​n​s​e​t​N​o​t​i​f​i​c​a​t​i​o​n​K​e​y​s​]​;​
 ​ ​ ​ ​C​F​R​e​l​e​a​s​e​(​_​d​y​n​a​m​i​c​S​t​o​r​e​)​ ​,​ ​_​d​y​n​a​m​i​c​S​t​o​r​e​ ​=​ ​N​U​L​L​;​
 ​ ​ ​ ​[​s​u​p​e​r​ ​d​e​a​l​l​o​c​]​;​
}​ ​/​/​ ​d​e​a​l​l​o​c​

/​/​ ​T​h​i​s​ ​v​a​l​u​e​ ​c​h​a​n​g​e​s​ ​i​f​ ​t​h​e​ ​u​s​e​r​ ​c​h​a​n​g​e​s​ ​t​h​e​ ​C​o​m​p​u​t​e​r​ ​N​a​m​e​ ​i​n​ ​t​h​e​
/​/​ ​S​h​a​r​i​n​g​ ​p​r​e​f​e​r​e​n​c​e​ ​p​a​n​e​.​
-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​h​o​s​t​n​a​m​e​ ​{​
 ​ ​ ​ ​i​f​ ​(​_​d​y​n​a​m​i​c​S​t​o​r​e​ ​=​=​ ​N​U​L​L​)​ ​r​e​t​u​r​n​ ​n​i​l​;​

 ​ ​ ​ ​C​F​S​t​r​i​n​g​R​e​f​ ​h​o​s​t​n​a​m​e​ ​=​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​o​p​y​L​o​c​a​l​H​o​s​t​N​a​m​e​ ​(​_​d​y​n​a​m​i​c​S​t​o​r​e​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​(​i​d​)​h​o​s​t​n​a​m​e​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​
}​ ​/​/​ ​h​o​s​t​n​a​m​e​

/​/​ ​T​h​i​s​ ​v​a​l​u​e​ ​c​h​a​n​g​e​s​ ​i​f​ ​t​h​i​s​ ​u​s​e​r​ ​l​o​g​s​ ​i​n​ ​a​s​ ​a​ ​d​i​f​f​e​r​e​n​t​ ​u​s​e​r​.​
/​/​ ​S​o​m​e​w​h​a​t​ ​d​i​f​f​i​c​u​l​t​ ​t​o​ ​c​a​t​c​h​ ​a​t​ ​r​u​n​t​i​m​e​.​
-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​c​o​n​s​o​l​e​U​s​e​r​ ​{​
 ​ ​ ​ ​i​f​ ​(​_​d​y​n​a​m​i​c​S​t​o​r​e​ ​=​=​ ​N​U​L​L​)​ ​r​e​t​u​r​n​ ​n​i​l​;​

 ​ ​ ​ ​u​i​d​_​t​ ​u​i​d​ ​=​ ​0​;​
 ​ ​ ​ ​g​i​d​_​t​ ​g​i​d​ ​=​ ​0​;​
 ​ ​ ​ ​C​F​S​t​r​i​n​g​R​e​f​ ​n​a​m​e​ ​=​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​o​p​y​C​o​n​s​o​l​e​U​s​e​r​(​_​d​y​n​a​m​i​c​S​t​o​r​e​,​ ​&​u​i​d​,​ ​&​g​i​d​)​;​

 ​ ​ ​ ​/​/​ ​B​u​i​l​d​ ​a​ ​s​t​r​i​n​g​ ​l​i​k​e​ ​"​n​a​m​e​ ​(​u​i​d​,​ ​g​i​d​)​ ​"​.​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​d​e​s​c​ ​=​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​ ​@​"​%​@​ ​(​%​l​u​,​ ​%​l​u​)​ ​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​a​m​e​,​ ​(​u​n​s​i​g​n​e​d​ ​l​o​n​g​)​ ​u​i​d​,​ ​(​u​n​s​i​g​n​e​d​ ​l​o​n​g​)​ ​g​i​d​]​;​
 ​ ​ ​ ​i​f​ ​(​n​a​m​e​ ​!​=​ ​N​U​L​L​)​ ​C​F​R​e​l​e​a​s​e​ ​(​n​a​m​e​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​d​e​s​c​;​
}​ ​/​/​ ​c​o​n​s​o​l​e​U​s​e​r​

/​/​ ​T​h​i​s​ ​v​a​l​u​e​ ​c​a​n​ ​c​h​a​n​g​e​ ​e​a​s​i​l​y​,​ ​e​s​p​e​c​i​a​l​l​y​ ​i​f​ ​a​ ​n​e​w​ ​n​e​t​w​o​r​k​ ​i​s​ ​j​o​i​n​e​d​.​
-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​l​o​c​a​l​I​P​s​ ​{​
 ​ ​ ​ ​i​f​ ​(​_​d​y​n​a​m​i​c​S​t​o​r​e​ ​=​=​ ​N​U​L​L​)​ ​r​e​t​u​r​n​ ​n​i​l​;​

 ​ ​ ​ ​s​t​a​t​i​c​ ​N​S​S​t​r​i​n​g​ ​*​c​o​n​s​t​ ​k​I​P​E​r​r​o​r​M​s​g​ ​=​ ​@​"​<​e​r​r​o​r​ ​r​e​t​r​i​e​v​i​n​g​ ​a​d​d​r​e​s​s​e​s​>​"​;​

 ​ ​ ​ ​/​/​ ​U​s​e​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​o​p​y​M​u​l​t​i​p​l​e​(​)​ ​t​o​ ​g​e​t​ ​a​ ​c​o​n​s​i​s​t​e​n​t​ ​s​n​a​p​s​h​o​t​.​
 ​ ​ ​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​p​a​t​t​e​r​n​ ​=​ ​C​F​S​T​R​ ​(​k​I​P​P​a​t​t​e​r​n​)​;​
 ​ ​ ​ ​C​F​A​r​r​a​y​R​e​f​ ​p​a​t​t​e​r​n​s​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​C​F​A​r​r​a​y​C​r​e​a​t​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​ ​&​p​a​t​t​e​r​n​,​ ​1​,​ ​&​k​C​F​T​y​p​e​A​r​r​a​y​C​a​l​l​B​a​c​k​s​)​;​

 ​ ​ ​ ​i​f​ ​(​p​a​t​t​e​r​n​s​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​%​s​:​ ​*​*​*​ ​U​n​a​b​l​e​ ​t​o​ ​c​r​e​a​t​e​ ​I​P​ ​p​a​t​t​e​r​n​ ​a​r​r​a​y​.​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​k​I​P​E​r​r​o​r​M​s​g​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​r​e​s​u​l​t​s​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​(​i​d​)​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​o​p​y​M​u​l​t​i​p​l​e​ ​(​_​d​y​n​a​m​i​c​S​t​o​r​e​,​ ​N​U​L​L​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​a​t​t​e​r​n​s​)​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​C​F​R​e​l​e​a​s​e​(​p​a​t​t​e​r​n​s​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​s​ ​=​=​ ​n​i​l​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​%​s​:​ ​*​*​*​ ​U​n​a​b​l​e​ ​t​o​ ​c​o​p​y​ ​I​P​ ​a​d​d​r​e​s​s​e​s​.​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​k​I​P​E​r​r​o​r​M​s​g​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​A​c​c​u​m​u​l​a​t​e​ ​t​h​e​ ​a​d​d​r​e​s​s​e​s​.​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​c​o​n​s​t​ ​s​e​p​a​r​a​t​o​r​ ​=​ ​@​"​,​ ​"​;​
 ​ ​ ​ ​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​*​a​d​d​r​e​s​s​S​t​r​i​n​g​s​ ​=​ ​[​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​a​r​r​a​y​]​;​

 ​ ​ ​ ​f​o​r​ ​(​N​S​S​t​r​i​n​g​ ​*​k​e​y​ ​i​n​ ​r​e​s​u​l​t​s​)​ ​{​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​E​a​c​h​ ​r​e​s​u​l​t​ ​d​i​c​t​i​o​n​a​r​y​ ​h​a​s​ ​a​ ​k​e​y​ ​"​A​d​d​r​e​s​s​e​s​"​ ​w​h​o​s​e​ ​v​a​l​u​e​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​i​s​ ​a​n​ ​a​r​r​a​y​ ​o​f​ ​I​P​ ​a​d​d​r​e​s​s​e​s​ ​a​s​ ​s​t​r​i​n​g​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​v​a​l​u​e​ ​=​ ​[​r​e​s​u​l​t​s​ ​o​b​j​e​c​t​F​o​r​K​e​y​:​ ​k​e​y​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​T​h​e​ ​s​a​m​e​ ​k​e​y​ ​i​s​ ​a​c​t​u​a​l​l​y​ ​u​s​e​d​ ​f​o​r​ ​b​o​t​h​ ​I​P​v​4​ ​a​n​d​ ​I​P​v​6​ ​a​d​d​r​e​s​s​e​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​a​d​d​r​e​s​s​e​s​ ​=​ ​[​v​a​l​u​e​ ​o​b​j​e​c​t​F​o​r​K​e​y​:​ ​(​i​d​)​ ​k​S​C​P​r​o​p​N​e​t​I​P​v​4​A​d​d​r​e​s​s​e​s​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​a​d​d​r​e​s​s​S​t​r​i​n​g​ ​=​ ​[​a​d​d​r​e​s​s​e​s​ ​c​o​m​p​o​n​e​n​t​s​J​o​i​n​e​d​B​y​S​t​r​i​n​g​:​ ​s​e​p​a​r​a​t​o​r​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​a​d​d​r​e​s​s​S​t​r​i​n​g​s​ ​a​d​d​O​b​j​e​c​t​:​ ​a​d​d​r​e​s​s​S​t​r​i​n​g​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​l​o​c​a​l​I​P​s​ ​=​ ​[​a​d​d​r​e​s​s​S​t​r​i​n​g​s​ ​c​o​m​p​o​n​e​n​t​s​J​o​i​n​e​d​B​y​S​t​r​i​n​g​:​ ​s​e​p​a​r​a​t​o​r​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​l​o​c​a​l​I​P​s​;​
}​ ​/​/​ ​l​o​c​a​l​I​P​s​

-​ ​(​v​o​i​d​)​ ​u​p​d​a​t​e​U​I​ ​{​
 ​ ​ ​ ​[​s​e​l​f​.​h​o​s​t​n​a​m​e​F​i​e​l​d​ ​s​e​t​S​t​r​i​n​g​V​a​l​u​e​:​ ​s​e​l​f​.​h​o​s​t​n​a​m​e​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​c​o​n​s​o​l​e​U​s​e​r​F​i​e​l​d​ ​s​e​t​S​t​r​i​n​g​V​a​l​u​e​:​ ​s​e​l​f​.​c​o​n​s​o​l​e​U​s​e​r​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​l​o​c​a​l​I​P​F​i​e​l​d​ ​s​e​t​S​t​r​i​n​g​V​a​l​u​e​:​ ​s​e​l​f​.​l​o​c​a​l​I​P​s​]​;​
}​ ​/​/​ ​u​p​d​a​t​e​U​I​

s​t​a​t​i​c​ ​v​o​i​d​ ​S​t​o​r​e​D​i​d​C​h​a​n​g​e​(​S​C​D​y​n​a​m​i​c​S​t​o​r​e​R​e​f​ ​s​t​o​r​e​,​ ​C​F​A​r​r​a​y​R​e​f​ ​c​h​a​n​g​e​d​K​e​y​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​v​o​i​d​ ​*​s​e​l​f​C​o​n​t​e​x​t​)​ ​{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​%​s​:​ ​<​S​C​D​y​n​a​m​i​c​S​t​o​r​e​R​e​f​:​ ​%​p​>​ ​c​h​a​n​g​e​d​ ​%​@​"​,​ ​_​_​f​u​n​c​_​_​,​ ​s​t​o​r​e​,​ ​c​h​a​n​g​e​d​K​e​y​s​)​;​
 ​ ​ ​ ​S​C​F​M​o​n​i​t​o​r​A​p​p​D​e​l​e​g​a​t​e​ ​*​s​e​l​f​ ​=​ ​(​S​C​F​M​o​n​i​t​o​r​A​p​p​D​e​l​e​g​a​t​e​ ​*​)​ ​s​e​l​f​C​o​n​t​e​x​t​;​
 ​ ​ ​ ​[​s​e​l​f​ ​u​p​d​a​t​e​U​I​]​;​
}​ ​/​/​ ​S​t​o​r​e​D​i​d​C​h​a​n​g​e​

-​ ​(​S​C​D​y​n​a​m​i​c​S​t​o​r​e​R​e​f​)​ ​n​e​w​D​y​n​a​m​i​c​S​t​o​r​e​ ​{​
 ​ ​ ​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​o​n​t​e​x​t​ ​s​e​l​f​C​o​n​t​e​x​t​ ​=​ ​{​ ​0​,​ ​s​e​l​f​,​ ​N​U​L​L​,​ ​N​U​L​L​,​ ​N​U​L​L​ ​}​;​

 ​ ​ ​ ​C​F​S​t​r​i​n​g​R​e​f​ ​n​a​m​e​ ​=​ ​C​F​B​u​n​d​l​e​G​e​t​I​d​e​n​t​i​f​i​e​r​(​C​F​B​u​n​d​l​e​G​e​t​M​a​i​n​B​u​n​d​l​e​(​)​)​;​
 ​ ​ ​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​R​e​f​ ​s​t​o​r​e​ ​=​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​r​e​a​t​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​ ​n​a​m​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​t​o​r​e​D​i​d​C​h​a​n​g​e​,​ ​&​s​e​l​f​C​o​n​t​e​x​t​)​;​
 ​ ​ ​ ​i​f​ ​(​s​t​o​r​e​ ​=​=​ ​N​U​L​L​)​ ​N​S​L​o​g​ ​(​@​"​%​s​:​ ​*​*​*​ ​F​a​i​l​e​d​ ​t​o​ ​c​r​e​a​t​e​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​R​e​f​.​"​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​t​o​r​e​;​
}​ ​/​/​ ​n​e​w​D​y​n​a​m​i​c​S​t​o​r​e​

-​ ​(​v​o​i​d​)​ ​s​e​t​N​o​t​i​f​i​c​a​t​i​o​n​K​e​y​s​ ​{​
 ​ ​ ​ ​/​/​ ​B​u​i​l​d​ ​t​h​e​ ​k​e​y​ ​l​i​s​t​.​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​h​o​s​t​N​a​m​e​K​e​y​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​(​i​d​)​S​C​D​y​n​a​m​i​c​S​t​o​r​e​K​e​y​C​r​e​a​t​e​H​o​s​t​N​a​m​e​s​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​)​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​c​o​n​s​o​l​e​U​s​e​r​K​e​y​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​(​i​d​)​S​C​D​y​n​a​m​i​c​S​t​o​r​e​K​e​y​C​r​e​a​t​e​C​o​n​s​o​l​e​U​s​e​r​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​)​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​C​F​A​r​r​a​y​R​e​f​ ​k​e​y​s​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​(​C​F​A​r​r​a​y​R​e​f​)​[​N​S​A​r​r​a​y​ ​a​r​r​a​y​W​i​t​h​O​b​j​e​c​t​s​:​ ​h​o​s​t​N​a​m​e​K​e​y​,​ ​c​o​n​s​o​l​e​U​s​e​r​K​e​y​,​ ​n​i​l​]​;​

 ​ ​ ​ ​/​/​ ​B​u​i​l​d​ ​t​h​e​ ​p​a​t​t​e​r​n​ ​l​i​s​t​.​
 ​ ​ ​ ​C​F​A​r​r​a​y​R​e​f​ ​p​a​t​t​e​r​n​s​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​(​C​F​A​r​r​a​y​R​e​f​)​ ​[​N​S​A​r​r​a​y​ ​a​r​r​a​y​W​i​t​h​O​b​j​e​c​t​:​ ​(​i​d​)​ ​C​F​S​T​R​(​k​I​P​P​a​t​t​e​r​n​)​ ​]​;​

 ​ ​ ​ ​/​/​ ​R​e​g​i​s​t​e​r​ ​f​o​r​ ​n​o​t​i​f​i​c​a​t​i​o​n​s​.​
 ​ ​ ​ ​B​o​o​l​e​a​n​ ​s​u​c​c​e​s​s​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​S​e​t​N​o​t​i​f​i​c​a​t​i​o​n​K​e​y​s​ ​(​_​d​y​n​a​m​i​c​S​t​o​r​e​,​ ​k​e​y​s​,​ ​p​a​t​t​e​r​n​s​)​;​
 ​ ​ ​ ​i​f​ ​(​!​s​u​c​c​e​s​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​(​@​"​%​s​:​ ​*​*​*​ ​F​a​i​l​e​d​ ​t​o​ ​s​e​t​ ​n​o​t​i​f​i​c​a​t​i​o​n​ ​k​e​y​s​.​"​,​ ​_​_​f​u​n​c​_​_​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​A​d​d​ ​t​h​e​ ​s​t​o​r​e​ ​t​o​ ​t​h​e​ ​r​u​n​ ​l​o​o​p​.​
 ​ ​ ​ ​_​s​t​o​r​e​C​h​a​n​g​e​S​o​u​r​c​e​ ​=​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​C​r​e​a​t​e​R​u​n​L​o​o​p​S​o​u​r​c​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​_​d​y​n​a​m​i​c​S​t​o​r​e​,​ ​0​)​;​
 ​ ​ ​ ​i​f​ ​(​_​s​t​o​r​e​C​h​a​n​g​e​S​o​u​r​c​e​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​(​@​"​%​s​:​ ​*​*​*​ ​F​a​i​l​e​d​ ​t​o​ ​c​r​e​a​t​e​ ​d​y​n​a​m​i​c​ ​s​t​o​r​e​ ​r​u​n​ ​l​o​o​p​ ​s​o​u​r​c​e​.​"​,​ ​_​_​f​u​n​c​_​_​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​u​n​s​e​t​N​o​t​i​f​i​c​a​t​i​o​n​K​e​y​s​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​C​F​R​u​n​L​o​o​p​R​e​f​ ​r​l​ ​=​ ​C​F​R​u​n​L​o​o​p​G​e​t​C​u​r​r​e​n​t​ ​(​)​;​
 ​ ​ ​ ​C​F​R​u​n​L​o​o​p​A​d​d​S​o​u​r​c​e​ ​(​r​l​,​ ​_​s​t​o​r​e​C​h​a​n​g​e​S​o​u​r​c​e​,​ ​k​C​F​R​u​n​L​o​o​p​C​o​m​m​o​n​M​o​d​e​s​)​;​

 ​ ​ ​ ​/​/​ ​O​r​d​i​n​a​r​i​l​y​ ​w​e​'​d​ ​r​e​l​e​a​s​e​ ​_​s​t​o​r​e​C​h​a​n​g​e​S​o​u​r​c​e​,​ ​b​u​t​ ​w​e​ ​w​i​l​l​ ​i​n​v​a​l​i​d​a​t​e​ ​i​t​
 ​ ​ ​ ​/​/​ ​w​h​e​n​ ​u​n​s​e​t​t​i​n​g​ ​t​h​e​ ​n​o​t​i​f​i​c​a​t​i​o​n​ ​k​e​y​s​.​

}​ ​/​/​ ​s​e​t​N​o​t​i​f​i​c​a​t​i​o​n​K​e​y​s​

-​ ​(​v​o​i​d​)​ ​u​n​s​e​t​N​o​t​i​f​i​c​a​t​i​o​n​K​e​y​s​ ​{​
 ​ ​ ​ ​i​f​ ​(​_​s​t​o​r​e​C​h​a​n​g​e​S​o​u​r​c​e​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​C​F​R​u​n​L​o​o​p​S​o​u​r​c​e​I​n​v​a​l​i​d​a​t​e​ ​(​_​s​t​o​r​e​C​h​a​n​g​e​S​o​u​r​c​e​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​B​o​o​l​e​a​n​ ​s​u​c​c​e​s​s​ ​=​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​S​e​t​N​o​t​i​f​i​c​a​t​i​o​n​K​e​y​s​ ​(​_​d​y​n​a​m​i​c​S​t​o​r​e​,​ ​N​U​L​L​,​ ​N​U​L​L​)​;​
 ​ ​ ​ ​i​f​ ​(​!​s​u​c​c​e​s​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​(​@​"​%​s​:​ ​S​C​D​y​n​a​m​i​c​S​t​o​r​e​S​e​t​N​o​t​i​f​i​c​a​t​i​o​n​K​e​y​s​ ​t​o​ ​N​U​L​L​ ​f​a​i​l​e​d​.​"​,​ ​ ​_​_​f​u​n​c​_​_​)​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​u​n​s​e​t​N​o​t​i​f​i​c​a​t​i​o​n​K​e​y​s​

@​e​n​d​ ​/​/​ ​S​C​F​M​o​n​i​t​o​r​A​p​p​D​e​l​e​g​a​t​e​x​

Build and run it. If you have two IP addresses, they should both show up in the window. If you have an Airport card and built-in ethernet, they can each have their own address. You will likely need to plug in an ethernet cable to have an IP address assigned to the corresponding interface.

If you change an address, you can see it disappear from the window for a couple of seconds and then reappear as the network interface is taken down and brought back up to effect the change. You can change the hostname by editing the Computer Name in the Sharing preference pane.

For the More Curious: Run Loop Observers

You can hook more stuff into the run loop, such as an observer. A run loop observer has its callback function invoked at a number of well defined places, shown Figure 15.6. These are the observation points:

When the run loop is entered

Before timers are fired

After timers are fired

Before sources (like the CFSocket) are checked

Before the run loop waits for activity (blocking until it can do something)

After it wakes up

When the run loop exits

Figure 15.6  Run loop observation points

Here is a function that will register an observer:

v​o​i​d​ ​a​d​d​R​u​n​L​o​o​p​O​b​s​e​r​v​e​r​ ​(​)​ ​{​
 ​ ​ ​ ​C​F​R​u​n​L​o​o​p​R​e​f​ ​r​l​;​
 ​ ​ ​ ​C​F​R​u​n​L​o​o​p​O​b​s​e​r​v​e​r​R​e​f​ ​o​b​s​e​r​v​e​r​;​

 ​ ​ ​ ​r​l​ ​=​ ​C​F​R​u​n​L​o​o​p​G​e​t​C​u​r​r​e​n​t​ ​(​)​;​

 ​ ​ ​ ​o​b​s​e​r​v​e​r​ ​=​ ​C​F​R​u​n​L​o​o​p​O​b​s​e​r​v​e​r​C​r​e​a​t​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​C​F​R​u​n​L​o​o​p​A​l​l​A​c​t​i​v​i​t​i​e​s​,​ ​/​/​ ​a​c​t​i​v​i​t​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​,​ ​/​/​ ​r​e​p​e​a​t​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​,​ ​/​/​ ​o​r​d​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​b​s​e​r​v​e​r​C​a​l​l​b​a​c​k​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​U​L​L​)​;​ ​/​/​ ​c​o​n​t​e​x​t​
 ​ ​ ​ ​C​F​R​u​n​L​o​o​p​A​d​d​O​b​s​e​r​v​e​r​ ​(​r​l​,​ ​o​b​s​e​r​v​e​r​,​ ​k​C​F​R​u​n​L​o​o​p​D​e​f​a​u​l​t​M​o​d​e​)​;​

 ​ ​ ​ ​C​F​R​e​l​e​a​s​e​ ​(​o​b​s​e​r​v​e​r​)​;​

}​ ​/​/​ ​a​d​d​R​u​n​L​o​o​p​O​b​s​e​r​v​e​r​

This works in a very similar way to the CFSocketRef, except here you do not have to create an independent run loop source. You can’t use an observer for anything useful outside of a run loop, so the designers removed that step.

Here is an observer function that will print out what action is being observed:

t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​o​b​s​e​r​v​e​r​A​c​t​i​v​i​t​y​ ​{​
 ​ ​ ​ ​i​n​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​c​t​i​v​i​t​y​;​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​n​a​m​e​;​
}​ ​o​b​s​e​r​v​e​r​A​c​t​i​v​i​t​y​;​

o​b​s​e​r​v​e​r​A​c​t​i​v​i​t​y​ ​g​_​a​c​t​i​v​i​t​i​e​s​[​]​ ​=​ ​{​
 ​ ​ ​ ​{​ ​k​C​F​R​u​n​L​o​o​p​E​n​t​r​y​,​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​R​u​n​ ​L​o​o​p​ ​E​n​t​r​y​"​ ​}​,​
 ​ ​ ​ ​{​ ​k​C​F​R​u​n​L​o​o​p​B​e​f​o​r​e​T​i​m​e​r​s​,​ ​ ​ ​"​B​e​f​o​r​e​ ​T​i​m​e​r​s​"​ ​}​,​
 ​ ​ ​ ​{​ ​k​C​F​R​u​n​L​o​o​p​B​e​f​o​r​e​S​o​u​r​c​e​s​,​ ​ ​"​B​e​f​o​r​e​ ​S​o​u​r​c​e​s​"​ ​}​,​
 ​ ​ ​ ​{​ ​k​C​F​R​u​n​L​o​o​p​B​e​f​o​r​e​W​a​i​t​i​n​g​,​ ​ ​"​B​e​f​o​r​e​ ​W​a​i​t​i​n​g​"​ ​}​,​
 ​ ​ ​ ​{​ ​k​C​F​R​u​n​L​o​o​p​A​f​t​e​r​W​a​i​t​i​n​g​,​ ​ ​ ​"​A​f​t​e​r​ ​W​a​i​t​i​n​g​"​ ​}​,​
 ​ ​ ​ ​{​ ​k​C​F​R​u​n​L​o​o​p​E​x​i​t​,​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​E​x​i​t​"​ ​}​
}​;​

v​o​i​d​ ​o​b​s​e​r​v​e​r​C​a​l​l​b​a​c​k​ ​(​C​F​R​u​n​L​o​o​p​O​b​s​e​r​v​e​r​R​e​f​ ​o​b​s​e​r​v​e​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​R​u​n​L​o​o​p​A​c​t​i​v​i​t​y​ ​a​c​t​i​v​i​t​y​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​v​o​i​d​ ​*​i​n​f​o​)​ ​{​
 ​ ​ ​ ​o​b​s​e​r​v​e​r​A​c​t​i​v​i​t​y​ ​*​s​c​a​n​,​ ​*​s​t​o​p​;​

 ​ ​ ​ ​s​c​a​n​ ​=​ ​g​_​a​c​t​i​v​i​t​i​e​s​;​
 ​ ​ ​ ​s​t​o​p​ ​=​ ​s​c​a​n​ ​+​ ​(​s​i​z​e​o​f​(​g​_​a​c​t​i​v​i​t​i​e​s​)​ ​/​ ​s​i​z​e​o​f​(​*​g​_​a​c​t​i​v​i​t​i​e​s​)​)​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​s​c​a​n​ ​<​ ​s​t​o​p​)​ ​{​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​s​c​a​n​-​>​a​c​t​i​v​i​t​y​ ​=​=​ ​a​c​t​i​v​i​t​y​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​%​s​"​,​ ​s​c​a​n​-​>​n​a​m​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​+​+​;​
 ​ ​ ​ ​}​

}​ ​/​/​ ​o​b​s​e​r​v​e​r​C​a​l​l​b​a​c​k​

Exercises

Add NSHost to CFChatterClient.

Add the observer callback to your CFChatterClient.

If you run scf-dump on a laptop, you’ll probably see something like this:

k​e​y​ ​i​s​ ​S​t​a​t​e​:​/​I​O​K​i​t​/​P​o​w​e​r​S​o​u​r​c​e​s​/​I​n​t​e​r​n​a​l​B​a​t​t​e​r​y​-​0​
 ​ ​ ​ ​M​a​x​ ​C​a​p​a​c​i​t​y​ ​:​ ​9​6​2​
 ​ ​ ​ ​C​u​r​r​e​n​t​ ​C​a​p​a​c​i​t​y​ ​:​ ​9​6​2​
 ​ ​ ​ ​N​a​m​e​ ​:​ ​I​n​t​e​r​n​a​l​B​a​t​t​e​r​y​-​0​
 ​ ​ ​ ​I​s​ ​P​r​e​s​e​n​t​ ​:​ ​1​
 ​ ​ ​ ​I​s​ ​C​h​a​r​g​i​n​g​ ​:​ ​0​
 ​ ​ ​ ​T​i​m​e​ ​t​o​ ​F​u​l​l​ ​C​h​a​r​g​e​ ​:​ ​0​
 ​ ​ ​ ​T​r​a​n​s​p​o​r​t​ ​T​y​p​e​ ​:​ ​I​n​t​e​r​n​a​l​
 ​ ​ ​ ​T​i​m​e​ ​t​o​ ​E​m​p​t​y​ ​:​ ​0​
 ​ ​ ​ ​P​o​w​e​r​ ​S​o​u​r​c​e​ ​S​t​a​t​e​ ​:​ ​A​C​ ​P​o​w​e​r​

Extend the SCFMonitor application to monitor battery information and display it, over time, in a tableview.

16
kqueue and FSEvents

Several of the technologies you have seen so far date back to Unix’s earlier days. Some of the APIs are awkward and difficult to use correctly, such as signal handling, and some are awkward and do not scale well, such as select() and poll().

In 2000, the FreeBSD project created Kernel Queues, abbreviated as “kqueues,” to address the shortcomings in the older APIs. Apple adopted kqueues in Mac OS X 10.3 and extended them in 10.6. kqueues are a unified notification mechanism used by the kernel that can inform your program about interesting events.

Inside the kernel are a number of filters, as shown in Figure 16.1. As the kernel does its work, it tells the filters what’s going on. There is a filter for process handling, another for the network stack, another one associated with the file system, and so on. Each of the filters has a set of interesting events that it watches for, such as data appears on a socket, a program has fork()ed, a signal has been sent, or a directory has had a file added to it.

Figure 16.1  Kernel filters adding new events to a kqueue

Your program registers its interest with the kernel about what entities it is interested in, like which file descriptors or signal numbers, and what specific events associated with those entities it wants to know about, such as “I want to know about new data to be read on the socket, but I do not care if I can write to it or not.” Events are put into a queue as they occur, and the program can pick them up at its leisure.

kqueue()

First, you ask the kernel to create a new queue for you by using kqueue() function:

int kqueue ();

kqueue() returns a file descriptor that represents the queue. Your application can have any number of active kqueues, and it is perfectly OK for a library or framework to make kqueues for its own use. When you are done with a kqueue, close() it like any other file descriptor. kqueue() returns -1 if an error occurred and sets errno appropriately.

The thread-safety of kqueues is not documented, so do not use the same kqueue in multiple threads simultaneously. It is safe, though, to manipulate an object such as a file in a different thread than the one that has kqueue watching it for changes. These operations, such as removing the events for a file that has been closed, happen atomically.

Even though kqueue() returns a file descriptor, you do not use it with read() or write(). Instead, you interact with the file descriptor via the kevent() function described below. Because the kqueue is represented by a file descriptor, you can put it into a select() or poll() call, into another kqueue, as well as into a runloop to see if there are any pending notifications. if there are any pending notifications, the file descriptor will behave as if there were bytes to be read from it, such as waking up select() or causing a runloop notification.

Events

You interact with kqueues using kevents, which describe which entities and filters you are interested in. struct kevent works on all Mac OS X systems starting with 10.3:

s​t​r​u​c​t​ ​k​e​v​e​n​t​ ​{​
 ​ ​ ​ ​u​i​n​t​p​t​r​_​t​ ​ ​ ​ ​ ​i​d​e​n​t​;​ ​ ​ ​ ​ ​ ​ ​/​*​ ​i​d​e​n​t​i​f​i​e​r​ ​f​o​r​ ​t​h​i​s​ ​e​v​e​n​t​ ​*​/​
 ​ ​ ​ ​i​n​t​1​6​_​t​ ​ ​ ​ ​ ​ ​ ​f​i​l​t​e​r​;​ ​ ​ ​ ​ ​ ​/​*​ ​f​i​l​t​e​r​ ​f​o​r​ ​e​v​e​n​t​ ​*​/​
 ​ ​ ​ ​u​i​n​t​1​6​_​t​ ​ ​ ​ ​ ​ ​f​l​a​g​s​;​ ​ ​ ​ ​ ​ ​ ​/​*​ ​g​e​n​e​r​a​l​ ​f​l​a​g​s​ ​*​/​
 ​ ​ ​ ​u​i​n​t​3​2​_​t​ ​ ​ ​ ​ ​ ​f​f​l​a​g​s​;​ ​ ​ ​ ​ ​ ​/​*​ ​f​i​l​t​e​r​-​s​p​e​c​i​f​i​c​ ​f​l​a​g​s​ ​*​/​
 ​ ​ ​ ​i​n​t​p​t​r​_​t​ ​ ​ ​ ​ ​ ​d​a​t​a​;​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​f​i​l​t​e​r​-​s​p​e​c​i​f​i​c​ ​d​a​t​a​ ​*​/​
 ​ ​ ​ ​v​o​i​d​ ​ ​ ​ ​ ​ ​ ​ ​ ​*​u​d​a​t​a​;​ ​ ​ ​ ​ ​ ​ ​/​*​ ​o​p​a​q​u​e​ ​u​s​e​r​ ​d​a​t​a​ ​i​d​e​n​t​i​f​i​e​r​ ​*​/​
}​;​

This structure is used both for communication from your program to the kernel and vice versa. When registering interest in an event you fill in the fields of the structure. When responding to an event you look at the fields to see what event happened and to find any associated data about the event.

Snow Leopard introduced 64-bit versions of the structures and calls for kevents. struct kevent64_s is available on Mac OS X 10.6 and iOS:

s​t​r​u​c​t​ ​k​e​v​e​n​t​6​4​_​s​ ​{​
 ​ ​ ​ ​u​i​n​t​6​4​_​t​ ​ ​ ​ ​ ​ ​i​d​e​n​t​;​ ​ ​ ​ ​ ​ ​ ​/​*​ ​i​d​e​n​t​i​f​i​e​r​ ​f​o​r​ ​t​h​i​s​ ​e​v​e​n​t​ ​*​/​
 ​ ​ ​ ​i​n​t​1​6​_​t​ ​ ​ ​ ​ ​ ​ ​f​i​l​t​e​r​;​ ​ ​ ​ ​ ​ ​/​*​ ​f​i​l​t​e​r​ ​f​o​r​ ​e​v​e​n​t​ ​*​/​
 ​ ​ ​ ​u​i​n​t​1​6​_​t​ ​ ​ ​ ​ ​ ​f​l​a​g​s​;​ ​ ​ ​ ​ ​ ​ ​/​*​ ​g​e​n​e​r​a​l​ ​f​l​a​g​s​ ​*​/​
 ​ ​ ​ ​u​i​n​t​3​2​_​t​ ​ ​ ​ ​ ​ ​f​f​l​a​g​s​;​ ​ ​ ​ ​ ​ ​/​*​ ​f​i​l​t​e​r​-​s​p​e​c​i​f​i​c​ ​f​l​a​g​s​ ​*​/​
 ​ ​ ​ ​i​n​t​6​4​_​t​ ​ ​ ​ ​ ​ ​ ​d​a​t​a​;​ ​ ​ ​ ​ ​ ​ ​ ​/​*​ ​f​i​l​t​e​r​-​s​p​e​c​i​f​i​c​ ​d​a​t​a​ ​*​/​
 ​ ​ ​ ​u​i​n​t​6​4​_​t​ ​ ​ ​ ​ ​ ​u​d​a​t​a​;​ ​ ​ ​ ​ ​ ​ ​/​*​ ​o​p​a​q​u​e​ ​u​s​e​r​ ​d​a​t​a​ ​i​d​e​n​t​i​f​i​e​r​ ​*​/​
 ​ ​ ​ ​u​i​n​t​6​4​_​t​ ​ ​ ​ ​ ​ ​e​x​t​[​2​]​;​ ​ ​ ​ ​ ​ ​/​*​ ​f​i​l​t​e​r​-​s​p​e​c​i​f​i​c​ ​e​x​t​e​n​s​i​o​n​s​ ​*​/​
}​;​

The main difference, aside from the larger pointer sizes, is that struct kevent64_s has two additional 64-bit values that are used to pass filter-specific information.

Here are the fields the two structures have in common plus the two additional 64-bit values:

ident

The identifier for the event. You can only have one particular pairing of filter and identifier with a given kqueue. What the identifier actually is varies based on the filter. A file descriptor is used for most of the filters, which is nice because a file descriptor can unambiguously identify a file in the file system. Sockets are represented by file descriptors. The signal filter uses the signal number here, such as SIGINT or SIGHUP.

filter

A constant that is defined for each of the filters supplied by the kernel. Some of the available filters include EVFILT_READ (data is available to be read on a file descriptor), EVFILT_WRITE (you can write to a file descriptor without blocking), EVFILT_VNODE (monitor changes to files and directories), EVFILT_PROC (trace process activity), EVFILT_SIGNAL (receive signals in a synchronous manner), and EVFILT_MACHPORT for Mach ports.

flags

A bitmask that tells kqueue what to do with the event as it is being registered. Valid flags include EV_ADD (add the event to the kqueue), EV_ENABLE (start issuing notifications about the event), EV_DISABLE (stop issuing notifications about the event), EV_DELETE (remove the event from the kqueue), EV_ONESHOT (notify about the event only once, then delete it from the kqueue), and EV_CLEAR (reset the state of the event). flags is also set on an event notification.

fflags | “Filter Flags” is a bitmask that contains arguments for a specific filter. The vnode filter has flags that indicate interest in specific file operations, such as the file getting deleted, the contents changing, the size increasing, and so on. Some filters do not have any extra flags.
data | An integer containing any filter-specific data. For example, data would have the number of bytes pending in the kernel’s read buffer for an EVFILT_READ notification. data would have the number of bytes you can write before blocking for an EVFILT_WRITE notification.

udata

“user data” is a pointer that you can use for any purpose. It’s like a rock you can hide data under. You can use it to store a pointer to a string, data structure, or object that you want to associate with the event. You can also store a function pointer here that you can use for a flexible event notification mechanism. The kernel passes this value back to you for each event so you can use it for your own purposes.

ext

Filter-specific extensions. These are two 64-bit values that filters can use for the cases where a set of flags is not sufficient. The Mach port filter uses ext[0] to return a pointer to the message when retrieving an event. Other filters pass ext[0] through untouched. ext[1] is not used by any filters, so its value is also passed through untouched, like a second udata field.

The EV_SET macro is provided for quick initialization of the structure:

EV_SET (&key, ident, filter, flags, fflags, data, udata);

You give the address to your struct kevent as the first argument, and then the subsequent arguments to the macro match the same-named entry in the kevent structure. There’s also a 64-bit version:

EV_SET64 (&kev, ident, filter, flags, fflags, data, udata, ext[0], ext[1]);

Registering and Handling Events

kevent() registers your interest in specific filters and events with a kqueue, and it is also the function used to block on a kqueue waiting for event notifications. The signature of kevent() is a bit long because it lets you do both operations at the same time.

int kevent (int kqueue,
            const struct kevent *changeList, int numChanges,
                  struct kevent *eventList,  int numEvents,
            const struct timespec *timeout);

The first argument to kevent() is the kqueue you want to manipulate. changeList holds the set of new events for the kqueue to monitor, as well as any changes to events the kqueue is already monitoring. If changeList is non-NULL and numChanges is greater than zero, then the events in changeList will be applied to the kqueue saying “tell me about anything that happens about these.”

eventList holds the events that kevent() is reporting. If eventList is non-NULL and numChanges is greater than zero, the event list gets filled in by kevent() with any pending events.

timeout is a struct timespec; its two fields specify how long you want to wait in seconds and nanoseconds.

s​t​r​u​c​t​ ​t​i​m​e​s​p​e​c​ ​{​
 ​ ​ ​ ​t​i​m​e​_​t​ ​ ​t​v​_​s​e​c​;​ ​ ​ ​ ​/​*​ ​s​e​c​o​n​d​s​ ​*​/​
 ​ ​ ​ ​l​o​n​g​ ​ ​ ​ ​t​v​_​n​s​e​c​;​ ​ ​ ​/​*​ ​n​a​n​o​s​e​c​o​n​d​s​ ​*​/​
}​;​

The system will do its best to honor your timeout as accurately as possible, but the granularity of the system clock can be much coarser than one nanosecond.

kevent() will wait indefinitely if the timeout parameter to kevent() is NULL unless eventList is also NULL, in which case you are only registering new events and not asking if any new events occurred. In this case, kevent() will return immediately. If you are waiting for events, kevent() will block until an event happens. If you want to poll the kqueue to see if there is anything there, give it a timeout of {0, 0}.

kevent() returns the number of events it has written to eventList. It can return zero if there were no events prior to the timeout expiring. If an error happened while processing changeList, kevent() tries to place an event with the EV_ERROR bit set in the flags field and the errno in the data field. Otherwise, kevent() returns -1, and errno will be set.

Here is how you would create a kqueue, register an event, and then block waiting for the event:

i​n​t​ ​k​q​ ​=​ ​k​q​u​e​u​e​ ​(​)​;​

i​f​ ​(​k​q​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​k​q​u​e​u​e​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​/​/​ ​e​x​i​t​ ​o​r​ ​g​o​t​o​ ​a​n​ ​e​r​r​o​r​ ​h​a​n​d​l​e​r​
}​

s​t​r​u​c​t​ ​k​e​v​e​n​t​ ​e​v​e​n​t​;​
E​V​_​S​E​T​ ​(​&​e​v​e​n​t​,​ ​ ​ ​ ​ ​/​/​ ​e​v​e​n​t​ ​s​t​r​u​c​t​u​r​e​
 ​ ​ ​ ​ ​ ​ ​ ​S​I​G​I​N​T​,​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​i​d​e​n​t​i​f​i​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​E​V​F​I​L​T​_​S​I​G​N​A​L​,​ ​/​/​ ​f​i​l​t​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​E​V​_​A​D​D​ ​|​ ​E​V​_​E​N​A​B​L​E​,​ ​/​/​ ​a​c​t​i​o​n​ ​f​l​a​g​s​
 ​ ​ ​ ​ ​ ​ ​ ​0​,​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​i​l​t​e​r​ ​f​l​a​g​s​
 ​ ​ ​ ​ ​ ​ ​ ​0​,​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​i​l​t​e​r​ ​d​a​t​a​
 ​ ​ ​ ​ ​ ​ ​ ​N​U​L​L​)​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​u​s​e​r​ ​d​a​t​a​

/​/​ ​r​e​g​i​s​t​e​r​ ​t​h​e​ ​e​v​e​n​t​
i​f​ ​(​k​e​v​e​n​t​(​k​q​,​ ​&​e​v​e​n​t​,​ ​1​,​ ​N​U​L​L​,​ ​0​,​ ​N​U​L​L​)​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​k​e​v​e​n​t​ ​s​i​g​n​a​l​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​/​/​ ​e​x​i​t​ ​o​r​ ​g​o​t​o​ ​a​n​ ​e​r​r​o​r​ ​h​a​n​d​l​e​r​
}​

.​.​.​

/​/​ ​b​l​o​c​k​ ​h​e​r​e​ ​u​n​t​i​l​ ​a​n​ ​e​v​e​n​t​ ​a​r​r​i​v​e​s​
i​f​ ​(​k​e​v​e​n​t​(​k​q​,​ ​N​U​L​L​,​ ​0​,​ ​&​e​v​e​n​t​,​ ​1​,​ ​N​U​L​L​)​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​k​e​v​e​n​t​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​/​/​ ​e​x​i​t​ ​o​r​ ​g​o​t​o​ ​a​n​ ​e​r​r​o​r​ ​h​a​n​d​l​e​r​
}​

/​/​ ​l​o​o​k​ ​a​t​ ​c​h​a​n​g​e​ ​a​n​d​ ​h​a​n​d​l​e​ ​t​h​e​ ​e​v​e​n​t​

/​/​ ​c​l​o​s​e​ ​t​h​e​ ​k​q​u​e​u​e​ ​w​h​e​n​ ​f​i​n​i​s​h​e​d​ ​w​i​t​h​ ​i​t​
c​l​o​s​e​ ​(​k​q​)​;​

Here we are re-using event. kevent() copies out the data it needs, so you do not have hang onto the struct kevent you used to register the event. You just fill out the structure and hand it to the kernel via kevent().

The EV_ADD and EV_ENABLE flags are necessary to have the event added to the kqueue and to have event notification enabled. You can pass EV_CLEAR to say that you are not interested in any previous events that may be been recorded. The state of a kernel resource, such as data in a read buffer for a network socket, can also trigger an event. Using EV_CLEAR will suppress an event notification in this case.

You use kevent64 to register and receive events using struct kevent64_s:

int kevent64 (int kqueue,
             const struct kevent64_s *changeList, int numChanges,
                   struct kevent64_s *eventlist, int numEvents,
                   unsigned int flags,
                   const struct timespec *timeout);

It is nearly the same as kevent(), but takes 64-bit kevent structures and also takes a flags argument, the use of which is currently undocumented, so just pass in zero.

kqueues for Signal Handling

Unix signals, due to their asynchronous nature, are difficult to handle correctly. Race conditions are possible, plus you have to be aware of functions you can or can’t call at signal interrupt time. Most of the time, you really do not have to handle a signal the instant it is delivered. You can handle it later at a more convenient time. That is why a common signal handling technique is to set a global variable and then check it later.

You can use kqueues and kevent() to handle signals, saving you from the headaches caused by the signal handling API. The filter for signals is also one of the simplest of the kqueue filters.

kqueue event handling can be used in tandem with the classic signal handling API. Calls like signal() and sigaction() have priority over events registered with a kqueue, so if someone has installed a signal handler or left the handler as SIG_DFL, it will get called, and your kqueue event will not. Therefore, you will need to remove any default signal handlers by calling

s​i​g​n​a​l​ ​(​s​i​g​n​u​m​,​ ​S​I​G​_​I​G​N​)​;​

To handle a signal, place the signal number of interest into the ident portion of the struct kevent. Set filter to EVFILT_SIGNAL. When kevent() reports a signal-related event, the data member of the kevent structure will have a count of the number of times the signal has occurred since the last time it appeared on kevent(). This is a huge improvement over the traditional signal API where you never know exactly how many times a signal has happened due to signal coalescing.

Example 16.1 is a command-line tool that registers a number of signals with a kqueue and then blocks on kevent() waiting for signals to happen. When they do, the name of the signal is printed out along with the number of times that signal has happened since the last time it was seen in kevent(). A timeout of 5 seconds is used to show a heartbeat for the program – to show that it actually is alive and stays alive while signals get handled. A SIGINT signal will cause the program to exit cleanly.

Example 16.1. sigwatcher.m

/​/​ ​s​i​g​w​a​t​c​h​e​r​.​m​ ​-​-​ ​w​a​t​c​h​ ​f​o​r​ ​s​i​g​n​a​l​s​ ​h​a​p​p​e​n​i​n​g​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​s​i​g​w​a​t​c​h​e​r​ ​s​i​g​w​a​t​c​h​e​r​.​m​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​f​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​(​)​
#​i​m​p​o​r​t​ ​<​s​y​s​/​e​v​e​n​t​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​k​q​u​e​u​e​(​)​ ​e​t​c​.​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​i​g​n​a​l​.​h​>​ ​ ​/​/​ ​f​o​r​ ​S​I​G​I​N​T​,​ ​e​t​c​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​i​m​e​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​u​c​t​ ​t​i​m​e​s​p​e​c​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​g​e​t​p​i​d​(​)​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​/​/​ ​P​r​o​g​r​a​m​ ​s​u​c​c​e​s​s​/​f​a​i​l​u​r​e​ ​r​e​s​u​l​t​.​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​

 ​ ​ ​ ​/​/​ ​R​e​g​i​s​t​e​r​ ​s​i​g​n​a​l​ ​e​v​e​n​t​s​ ​w​i​t​h​ ​t​h​i​s​ ​q​u​e​u​e​.​
 ​ ​ ​ ​i​n​t​ ​k​q​ ​=​ ​k​q​u​e​u​e​ ​(​)​;​
 ​ ​ ​ ​i​f​ ​(​k​q​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​k​q​u​e​u​e​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​l​i​s​t​ ​o​f​ ​e​v​e​n​t​s​ ​w​e​'​r​e​ ​i​n​t​e​r​e​s​t​e​d​ ​i​n​,​ ​m​o​s​t​l​y​ ​j​u​s​t​ ​p​u​l​l​e​d​
 ​ ​ ​ ​/​/​ ​a​t​ ​r​a​n​d​o​m​ ​f​r​o​m​ ​<​s​y​s​/​s​i​g​n​a​l​.​h​>​.​
 ​ ​ ​ ​i​n​t​ ​s​i​g​n​a​l​s​[​]​ ​=​ ​{​ ​S​I​G​H​U​P​,​ ​S​I​G​I​N​T​,​ ​S​I​G​Q​U​I​T​,​ ​S​I​G​I​L​L​,​ ​S​I​G​T​R​A​P​,​ ​S​I​G​A​B​R​T​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​I​G​B​U​S​,​ ​S​I​G​S​E​G​V​,​ ​S​I​G​P​I​P​E​,​ ​S​I​G​T​E​R​M​,​ ​S​I​G​C​H​L​D​,​ ​S​I​G​W​I​N​C​H​ ​}​;​
 ​ ​ ​ ​i​n​t​ ​*​s​c​a​n​ ​=​ ​s​i​g​n​a​l​s​;​
 ​ ​ ​ ​i​n​t​ ​*​s​t​o​p​ ​=​ ​s​c​a​n​ ​+​ ​s​i​z​e​o​f​(​s​i​g​n​a​l​s​)​ ​/​ ​s​i​z​e​o​f​(​*​s​i​g​n​a​l​s​)​;​

 ​ ​ ​ ​/​/​ ​R​e​g​i​s​t​e​r​ ​e​a​c​h​ ​e​v​e​n​t​ ​w​i​t​h​ ​t​h​e​ ​k​q​u​e​u​e​.​
 ​ ​ ​ ​w​h​i​l​e​ ​(​s​c​a​n​ ​<​ ​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​k​e​v​e​n​t​ ​e​v​e​n​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​E​V​_​S​E​T​ ​(​&​e​v​e​n​t​,​ ​*​s​c​a​n​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​E​V​F​I​L​T​_​S​I​G​N​A​L​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​E​V​_​A​D​D​ ​|​ ​E​V​_​E​N​A​B​L​E​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​,​ ​0​,​ ​N​U​L​L​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​k​q​u​e​u​e​ ​e​v​e​n​t​ ​h​a​n​d​l​i​n​g​ ​h​a​p​p​e​n​s​ ​a​f​t​e​r​ ​l​e​g​a​c​y​ ​A​P​I​,​ ​s​o​ ​m​a​k​e​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​s​u​r​e​ ​i​t​ ​t​h​e​ ​s​i​g​n​a​l​ ​d​o​e​s​n​'​t​ ​g​e​t​ ​e​a​t​e​n​.​
 ​ ​ ​ ​ ​ ​ ​ ​s​i​g​n​a​l​ ​(​*​s​c​a​n​,​ ​S​I​G​_​I​G​N​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​R​e​g​i​s​t​e​r​ ​t​h​e​ ​s​i​g​n​a​l​ ​e​v​e​n​t​.​ ​ ​k​e​v​e​n​t​(​)​ ​w​i​l​l​ ​r​e​t​u​r​n​ ​i​m​m​e​d​i​a​t​e​l​y​.​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​k​e​v​e​n​t​(​k​q​,​ ​&​e​v​e​n​t​,​ ​1​,​ ​N​U​L​L​,​ ​0​,​ ​N​U​L​L​)​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​k​e​v​e​n​t​ ​s​i​g​n​a​l​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​d​o​n​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​+​+​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​I​ ​a​m​ ​p​i​d​ ​%​d​\​n​"​,​ ​g​e​t​p​i​d​(​)​)​;​

 ​ ​ ​ ​/​/​ ​N​o​w​ ​b​l​o​c​k​ ​a​n​d​ ​d​i​s​p​l​a​y​ ​a​n​y​ ​s​i​g​n​a​l​s​ ​r​e​c​e​i​v​e​d​.​
 ​ ​ ​ ​w​h​i​l​e​ ​(​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​t​i​m​e​s​p​e​c​ ​t​i​m​e​o​u​t​ ​=​ ​{​ ​5​,​ ​0​ ​}​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​k​e​v​e​n​t​ ​e​v​e​n​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​s​t​a​t​u​s​ ​=​ ​k​e​v​e​n​t​ ​(​k​q​,​ ​N​U​L​L​,​ ​0​,​ ​&​e​v​e​n​t​,​ ​1​,​ ​&​t​i​m​e​o​u​t​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​s​t​a​t​u​s​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​T​i​m​e​o​u​t​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​l​u​b​ ​d​u​b​.​.​.​\​n​"​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​s​t​a​t​u​s​ ​>​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​e​ ​g​o​t​ ​s​i​g​n​a​l​!​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​w​e​ ​g​o​t​ ​s​i​g​n​a​l​:​ ​%​d​ ​(​%​s​)​,​ ​d​e​l​i​v​e​r​e​d​:​ ​%​d​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​i​n​t​)​e​v​e​n​t​.​i​d​e​n​t​,​ ​s​t​r​s​i​g​n​a​l​(​(​i​n​t​)​e​v​e​n​t​.​i​d​e​n​t​)​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​i​n​t​)​e​v​e​n​t​.​d​a​t​a​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​e​v​e​n​t​.​i​d​e​n​t​ ​=​=​ ​S​I​G​I​N​T​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​d​o​n​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​n​d​ ​n​o​t​ ​k​e​v​e​n​t​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​d​o​n​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

d​o​n​e​:​
 ​ ​ ​ ​c​l​o​s​e​ ​(​k​q​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​r​e​s​u​l​t​;​

}​ ​/​/​ ​m​a​i​n​

And here is a sample run. One terminal window is running the program, and another terminal window has the kill commands used to send signals to sigwatcher.

$​ ​.​/​s​i​g​w​a​t​c​h​e​r​
I​ ​a​m​ ​p​i​d​ ​1​5​3​4​1​
l​u​b​ ​d​u​b​.​.​.​
w​e​ ​g​o​t​ ​s​i​g​n​a​l​:​ ​1​1​ ​(​S​e​g​m​e​n​t​a​t​i​o​n​ ​f​a​u​l​t​)​,​ ​d​e​l​i​v​e​r​e​d​:​ ​1​
w​e​ ​g​o​t​ ​s​i​g​n​a​l​:​ ​1​1​ ​(​S​e​g​m​e​n​t​a​t​i​o​n​ ​f​a​u​l​t​)​,​ ​d​e​l​i​v​e​r​e​d​:​ ​1​
l​u​b​ ​d​u​b​.​.​.​
w​e​ ​g​o​t​ ​s​i​g​n​a​l​:​ ​1​1​ ​(​S​e​g​m​e​n​t​a​t​i​o​n​ ​f​a​u​l​t​)​,​ ​d​e​l​i​v​e​r​e​d​:​ ​3​
l​u​b​ ​d​u​b​.​.​.​
l​u​b​ ​d​u​b​.​.​.​
w​e​ ​g​o​t​ ​s​i​g​n​a​l​:​ ​2​ ​(​I​n​t​e​r​r​u​p​t​)​,​ ​d​e​l​i​v​e​r​e​d​:​ ​1​

And these commands were issued in the other terminal:

$​ ​k​i​l​l​ ​-​s​ ​S​E​G​V​ ​1​5​3​4​1​
$​ ​k​i​l​l​ ​-​s​ ​S​E​G​V​ ​1​5​3​4​1​
$​ ​k​i​l​l​ ​-​s​ ​S​E​G​V​ ​1​5​3​4​1​ ​;​ ​k​i​l​l​ ​-​s​ ​S​E​G​V​ ​1​5​3​4​1​ ​;​ ​k​i​l​l​ ​-​s​ ​S​E​G​V​ ​1​5​3​4​1​
$​ ​k​i​l​l​ ​-​s​ ​I​N​T​ ​1​5​3​4​1​

kqueues for Socket Monitoring

Like the signal handling API, the API for monitoring sockets and other file descriptors can be awkward and inefficient. select() is the function used most often on Mac OS X to determine the liveness of a socket (is it still connected?) and to see if there is any activity on a socket (can I read from it without blocking?)

The problem with select(), and its functionally equivalent counterpart poll(), is that it is a stateless call. Every time you call select() or poll() you have to tell the kernel what file descriptors you are interested in. The kernel then copies this list of descriptors into its own memory space, does whatever work it does to test for liveness and activity, and then copies stuff back to the program’s address space to let the program know what is going on.

The main problem with this is scalability. If you are dealing with hundreds or thousands of file descriptors, you have to ask the kernel about all of them every time even if only a small percentage actually have anything interesting going on. High volume server applications can often find themselves spending a lot of CPU time on maintenance of the FD_SETs for select().

The other problem with the select() technique is convenience. When select() tells you a file descriptor has data to be read, you have to loop over the returned FD_SET looking for the file descriptors of interest. Then you have to attempt a read() to see if the connection is still open. If the connection is still open, you do not know how much data is available to read. You have to guess how much to read, usually using a hard-coded argument, and looping if there is more than that amount.

Both of these problems are solved when using kqueues for monitoring your sockets. You register all of your sockets of interest with kevent(), and then subsequent kevent() calls will tell you exactly which sockets have activity. Included in the event is how much data is there to be read. You can then slurp this data up with a single read(), rather than issuing multiple reads and stitching together the data.

For read operations on a socket (or file, or pipe), use the EVFILT_READ filter. It takes a file descriptor as the struct kevent identifier. You can use listen() sockets and sockets that have already been accept()ed. When kevent() returns, the data field of the event has the amount of data in the kernel buffers waiting to be read.

This behavior is called a “level-triggered” event because the event is triggered based on the level of data in the buffer. You will be notified by the EVFILT_READ filter if there is any data to be read from the socket. This allows you to read a convenient number of bytes from the socket, say the size of a message, and leave the rest of the data there. The next time kevent() is called, you will be told about the rest of the unread data.

Other filters are said to use “edge-triggered” events, whereby you get notifications when the entity of interest changes state. This is used more often with the EVFILT_VNODE filter, which is described below.

Getting back to EVFILT_READ, the filter also sets the EV_EOF flag in the flags field if a socket has been shut down, with any errno value in the event’s fflags field. It is possible for EV_EOF to be set in the event flags and there still be data pending in the socket buffer.

The EVFILT_WRITE filter works in a similar manner. When a write event is received for a file descriptor, the data field of the event structure will contain the amount of space remaining in the write buffer, which is how much you can write before blocking. This filter will also set EV_EOF when the reader disconnects.

kqueues for File System Monitoring

Usually when you hear kqueue being discussed on the Internet, it is regarding monitoring the file system. You can use EVFILT_VNODE to watch a file or a directory for changes and then react to those changes. You might want to watch a directory for changes and then pick up any files that have been placed in that directory, leading to “drop-box” functionality for the user. You could also implement an efficient tail -f feature by waiting for a file to have data written to it.

The filter used is EVFILT_VNODE. A vnode, short for virtual node, is a kernel data structure that contains information about a file or folder, with a unique vnode allocated in the kernel for each active file or folder. vnodes are part of VFS, a virtual file system in the kernel, which provides an abstraction around specific file system implementations.

There are a number of events you can monitor on a vnode, represented by bit flags you can bitwise-OR together in the filter flags of the event structure. When you receive a notification, you can bitwise-AND the filter flags to see what event(s) happened.

Here are some of the different events relating to EVFILT_VNODE:

NOTE_DELETE | The unlink() function was called on the file.
NOTE_WRITE | The contents of the file have been changed due to a write() operation.
NOTE_EXTEND | The file’s size increased.
NOTE_ATTRIB | The file has had its attributes changed.
NOTE_LINK | The (hard) link count to the file has changed. This is not triggered if a new symbolic link is made to the file.
NOTE_RENAME | The file has been renamed.
NOTE_REVOKE | Access to the file was revoked via the revoke() system call, or the underlying file system has been unmounted.

dirwatcher is a command-line tool that takes a set of directories as program arguments, as in Example 16.2. Each of those directories is opened, and the resulting file descriptor is placed in a kqueue using the NOTE_WRITE filter flag. When a directory changes, such as a file being added or removed, kevent() will return reporting which directory has changed. The program then prints out the name of the directory that has changed.

Because dirwatcher puts a pointer to the directory’s name into the event’s user data pointer field, mapping the change event to the directory name is simple. The program also catches SIGINT to do a clean shutdown. Many kinds of events can be mixed and matched in the same kqueue. You can use the user data field of the event structure to determine what kind of event it is, which is what dirwatcher does, or you can look at the filter field to see which filter generated the event.

Example 16.2. dirwatcher.m

/​/​ ​d​i​r​w​a​t​c​h​e​r​.​m​ ​-​-​ ​w​a​t​c​h​ ​d​i​r​e​c​t​o​r​i​e​s​ ​f​o​r​ ​c​h​a​n​g​e​s​

/​/​ ​g​c​c​ ​-​s​t​d​=​c​9​9​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​d​i​r​w​a​t​c​h​e​r​ ​d​i​r​w​a​t​c​h​e​r​.​m​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​f​c​n​t​l​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​O​_​R​D​O​N​L​Y​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​f​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​(​)​
#​i​m​p​o​r​t​ ​<​s​y​s​/​e​v​e​n​t​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​k​q​u​e​u​e​(​)​ ​e​t​c​.​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​c​l​o​s​e​(​)​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​

 ​ ​ ​ ​/​/​ ​P​r​o​g​r​a​m​ ​s​u​c​c​e​s​s​/​f​a​i​l​u​r​e​ ​r​e​s​u​l​t​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​

 ​ ​ ​ ​/​/​ ​M​a​k​e​ ​s​u​r​e​ ​t​h​e​r​e​'​s​ ​a​t​ ​l​e​a​s​t​ ​o​n​e​ ​d​i​r​e​c​t​o​r​y​ ​t​o​ ​m​o​n​i​t​o​r​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​=​=​ ​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​%​s​ ​d​i​r​e​c​t​o​r​y​n​a​m​e​ ​[​.​.​.​]​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​ ​ ​ ​ ​w​a​t​c​h​e​s​ ​d​i​r​e​c​t​o​r​y​n​a​m​e​ ​f​o​r​ ​c​h​a​n​g​e​s​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​d​o​n​e​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​q​u​e​u​e​ ​t​o​ ​r​e​g​i​s​t​e​r​ ​t​h​e​ ​d​i​r​-​w​a​t​c​h​i​n​g​ ​e​v​e​n​t​s​ ​w​i​t​h​
 ​ ​ ​ ​i​n​t​ ​k​q​ ​=​ ​k​q​u​e​u​e​ ​(​)​;​

 ​ ​ ​ ​i​f​ ​(​k​q​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​k​q​u​e​u​e​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​W​a​l​k​ ​t​h​e​ ​s​e​t​ ​o​f​ ​d​i​r​e​c​t​o​r​i​e​s​ ​p​r​o​v​i​d​e​d​ ​b​y​ ​t​h​e​ ​u​s​e​r​ ​a​n​d​ ​m​o​n​i​t​o​r​ ​t​h​e​m​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​1​;​ ​i​ ​<​ ​a​r​g​c​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​T​h​e​ ​v​n​o​d​e​ ​m​o​n​i​t​o​r​ ​r​e​q​u​i​r​e​s​ ​a​ ​f​i​l​e​ ​d​e​s​c​r​i​p​t​o​r​,​ ​s​o​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​o​p​e​n​ ​t​h​e​ ​d​i​r​e​c​t​o​r​y​ ​t​o​ ​g​e​t​ ​o​n​e​.​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​d​i​r​n​a​m​e​ ​=​ ​a​r​g​v​[​i​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​d​i​r​f​d​ ​=​ ​o​p​e​n​ ​(​d​i​r​n​a​m​e​,​ ​O​_​R​D​O​N​L​Y​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​d​i​r​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​o​p​e​n​(​%​s​)​.​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​r​n​a​m​e​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​i​n​u​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​F​i​l​l​ ​o​u​t​ ​t​h​e​ ​e​v​e​n​t​ ​s​t​r​u​c​t​u​r​e​.​ ​ ​S​t​o​r​e​ ​t​h​e​ ​n​a​m​e​ ​o​f​ ​t​h​e​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​d​i​r​e​c​t​o​r​y​ ​i​n​ ​t​h​e​ ​u​s​e​r​ ​d​a​t​a​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​k​e​v​e​n​t​ ​d​i​r​e​v​e​n​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​E​V​_​S​E​T​ ​(​&​d​i​r​e​v​e​n​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​r​f​d​,​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​i​d​e​n​t​i​f​i​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​E​V​F​I​L​T​_​V​N​O​D​E​,​ ​ ​ ​ ​/​/​ ​f​i​l​t​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​E​V​_​A​D​D​ ​|​ ​E​V​_​C​L​E​A​R​ ​|​ ​E​V​_​E​N​A​B​L​E​,​ ​ ​/​/​ ​a​c​t​i​o​n​ ​f​l​a​g​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​O​T​E​_​W​R​I​T​E​,​ ​ ​ ​ ​ ​ ​/​/​ ​f​i​l​t​e​r​ ​f​l​a​g​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​,​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​i​l​t​e​r​ ​d​a​t​a​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​v​o​i​d​ ​*​)​d​i​r​n​a​m​e​)​;​ ​/​/​ ​u​s​e​r​ ​d​a​t​a​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​r​e​g​i​s​t​e​r​ ​t​h​e​ ​e​v​e​n​t​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​k​e​v​e​n​t​(​k​q​,​ ​&​d​i​r​e​v​e​n​t​,​ ​1​,​ ​N​U​L​L​,​ ​0​,​ ​N​U​L​L​)​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​k​e​v​e​n​t​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​d​o​n​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​R​e​g​i​s​t​e​r​ ​i​n​t​e​r​e​s​t​ ​i​n​ ​S​I​G​I​N​T​ ​w​i​t​h​ ​t​h​e​ ​q​u​e​u​e​.​ ​ ​T​h​e​ ​u​s​e​r​ ​d​a​t​a​
 ​ ​ ​ ​/​/​ ​i​s​ ​N​U​L​L​,​ ​w​h​i​c​h​ ​i​s​ ​h​o​w​ ​w​e​'​l​l​ ​d​i​f​f​e​r​e​n​t​i​a​t​e​ ​b​e​t​w​e​e​n​
 ​ ​ ​ ​/​/​ ​a​ ​d​i​r​e​c​t​o​r​y​-​m​o​d​i​f​i​c​a​t​i​o​n​ ​e​v​e​n​t​ ​a​n​d​ ​a​ ​S​I​G​I​N​T​-​r​e​c​e​i​v​e​d​ ​e​v​e​n​t​.​

 ​ ​ ​ ​s​t​r​u​c​t​ ​k​e​v​e​n​t​ ​s​i​g​e​v​e​n​t​;​
 ​ ​ ​ ​E​V​_​S​E​T​ ​(​&​s​i​g​e​v​e​n​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​I​G​I​N​T​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​E​V​F​I​L​T​_​S​I​G​N​A​L​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​E​V​_​A​D​D​ ​|​ ​E​V​_​E​N​A​B​L​E​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​,​ ​0​,​ ​N​U​L​L​)​;​

 ​ ​ ​ ​/​/​ ​k​q​u​e​u​e​ ​e​v​e​n​t​ ​h​a​n​d​l​i​n​g​ ​h​a​p​p​e​n​s​ ​a​f​t​e​r​ ​t​h​e​ ​l​e​g​a​c​y​ ​A​P​I​,​ ​s​o​ ​m​a​k​e​
 ​ ​ ​ ​/​/​ ​s​u​r​e​ ​i​t​ ​d​o​e​s​n​ ​e​a​t​ ​t​h​e​ ​s​i​g​n​a​l​ ​b​e​f​o​r​e​ ​t​h​e​ ​k​q​u​e​u​e​ ​c​a​n​ ​s​e​e​ ​i​t​.​
 ​ ​ ​ ​s​i​g​n​a​l​ ​(​S​I​G​I​N​T​,​ ​S​I​G​_​I​G​N​)​;​

 ​ ​ ​ ​/​/​ ​R​e​g​i​s​t​e​r​ ​t​h​e​ ​s​i​g​n​a​l​ ​e​v​e​n​t​.​
 ​ ​ ​ ​i​f​ ​(​k​e​v​e​n​t​(​k​q​,​ ​&​s​i​g​e​v​e​n​t​,​ ​1​,​ ​N​U​L​L​,​ ​0​,​ ​N​U​L​L​)​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​k​e​v​e​n​t​ ​s​i​g​n​a​l​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​d​o​n​e​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​w​h​i​l​e​ ​(​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​c​a​m​p​ ​o​n​ ​k​e​v​e​n​t​(​)​ ​u​n​t​i​l​ ​s​o​m​e​t​h​i​n​g​ ​i​n​t​e​r​e​s​t​i​n​g​ ​h​a​p​p​e​n​s​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​k​e​v​e​n​t​ ​c​h​a​n​g​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​k​e​v​e​n​t​(​k​q​,​ ​N​U​L​L​,​ ​0​,​ ​&​c​h​a​n​g​e​,​ ​1​,​ ​N​U​L​L​)​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​n​d​ ​n​o​t​ ​k​e​v​e​n​t​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​d​o​n​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​T​h​e​ ​s​i​g​n​a​l​ ​e​v​e​n​t​ ​h​a​s​ ​N​U​L​L​ ​i​n​ ​t​h​e​ ​u​s​e​r​ ​d​a​t​a​.​ ​ ​C​h​e​c​k​ ​f​o​r​ ​t​h​a​t​ ​f​i​r​s​t​.​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​c​h​a​n​g​e​.​u​d​a​t​a​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​t​h​a​t​'​s​ ​a​l​l​ ​f​o​l​k​s​.​.​.​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​d​o​n​e​;​

 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​u​d​a​t​a​ ​i​s​ ​n​o​n​-​n​u​l​l​,​ ​s​o​ ​i​t​'​s​ ​t​h​e​ ​n​a​m​e​ ​o​f​ ​t​h​e​ ​d​i​r​e​c​t​o​r​y​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​t​h​a​t​ ​c​h​a​n​g​e​d​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​s​\​n​"​,​ ​(​c​h​a​r​*​)​c​h​a​n​g​e​.​u​d​a​t​a​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

d​o​n​e​:​
 ​ ​ ​ ​c​l​o​s​e​ ​(​k​q​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​r​e​s​u​l​t​;​

}​ ​/​/​ ​m​a​i​n​

Here is a sample run that watches the user’s home directory and /tmp:

$​ ​.​/​d​i​r​w​a​t​c​h​e​r​ ​~​ ​/​t​m​p​
/​t​m​p​
/​U​s​e​r​s​/​m​a​r​k​d​
/​U​s​e​r​s​/​m​a​r​k​d​
/​t​m​p​
^​C​
t​h​a​t​'​s​ ​a​l​l​ ​f​o​l​k​s​.​.​.​

Files were created and deleted in each directory using touch and rm in another terminal window. These triggered the output in dirwatcher. dirwatcher will also notice changes that are made with the Finder. Finally Control-C, which generates a SIGINT, is used to shut down the program.

kqueues and Runloops

You can use kqueues in your Cocoa and Carbon applications to monitor OS events of interest by making a thread that sits on kevent(), or you can use the runloop.

You can create a thread that contains the kevent() call.

If given no timeout, kevent() will block the thread until something interesting happens. That thread, after it wakes up, can notify the application’s main thread about what happened or do whatever work is appropriate.

It is safe, though, to manipulate a file descriptor in one thread while another thread is monitoring it with a kqueue. If the file is closed, the kernel atomically removes any pending events associated with this file descriptor in the process. However, there is one gotcha. If a file descriptor gets recycled (by being closed and then a new file opened with the same file descriptor number) while an event is currently being serviced on the old file, then there may be confusion about what file is actually being referenced. This is known as a “recycle race.” You can use the udata field in the event structure to disambiguate things if this is a problem for you. (Thanks to Jim Magee for pointing this out.)

Another technique is to put the file descriptor into the main thread’s runloop. A kqueue is referenced by a file descriptor, and its behavior is such that select(), poll(), and kevent() can be used to see if there is an event notification waiting on a particular queue. You can wrap the kqueue file descriptor in a CFSocket just like you did with CFChatterClient and add it to the runloop. This is safe to do so long as you do not try to directly read or write using the kqueue file descriptor.

Figure 16.2 shows CocoaDirWatcher, a Cocoa application that watches three directories: /tmp, the user’s home directory, and the user’s preferences directory. When any of these directories change, the name of the directory is sent to the console via NSLog(), and the directory is also added to the NSTextView in the window.

Figure 16.2  CocoaDirWatcher in action

Create the CocoaDirWatcher project in Xcode. Edit CocoaDirWatcherAppDelegate.h to look like Example 16.3

Example 16.3. CocoaDirWatcherAppDelegate.h

#​i​m​p​o​r​t​ ​<​C​o​c​o​a​/​C​o​c​o​a​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​C​o​c​o​a​D​i​r​W​a​t​c​h​e​r​A​p​p​D​e​l​e​g​a​t​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​<​N​S​A​p​p​l​i​c​a​t​i​o​n​D​e​l​e​g​a​t​e​>​ ​{​
 ​ ​ ​ ​N​S​W​i​n​d​o​w​ ​*​_​w​i​n​d​o​w​;​ ​/​/​ ​f​r​o​m​ ​t​e​m​p​l​a​t​e​

 ​ ​ ​ ​/​/​ ​D​i​r​e​c​t​o​r​y​ ​c​h​a​n​g​e​d​ ​m​e​s​s​a​g​e​s​ ​a​r​e​ ​a​p​p​e​n​d​e​d​ ​h​e​r​e​.​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​V​i​e​w​ ​*​_​l​o​g​V​i​e​w​;​

 ​ ​ ​ ​/​/​ ​F​i​l​e​ ​d​e​s​c​r​i​p​t​o​r​ ​f​o​r​ ​t​h​e​ ​d​i​r​e​c​t​o​r​y​-​w​a​t​c​h​i​n​g​ ​k​q​u​e​u​e​.​
 ​ ​ ​ ​i​n​t​ ​_​k​q​f​d​;​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​s​o​c​k​e​t​ ​p​l​a​c​e​d​ ​i​n​t​o​ ​t​h​e​ ​r​u​n​l​o​o​p​
 ​ ​ ​ ​C​F​S​o​c​k​e​t​R​e​f​ ​_​r​u​n​L​o​o​p​S​o​c​k​e​t​;​
}​

@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​W​i​n​d​o​w​ ​*​w​i​n​d​o​w​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​V​i​e​w​ ​*​l​o​g​V​i​e​w​;​

@​e​n​d​ ​/​/​ ​C​o​c​o​a​D​i​r​W​a​t​c​h​e​r​A​p​p​D​e​l​e​g​a​t​e​

Open MainMenu.xib. Add an NSTextView to the window and connect the logView outlet. Tweak the window’s layout to look nice.

Example 16.4 shows CocoaDirWatcherAppDelegate.m, which uses some code taken from CFChatterClient. When CocoaDirWatcherAppDelegate’s -applicationDidFinishLaunching: method is called, it creates a new kqueue and starts a watch on the three directories. An NSString with the directory name is put into the user data field of the kevent structure. That is how we’ll know what to add to the logview when a directory changes.

After adding the events we are interested in to the kqueue, the kqueue’s file descriptor is added to the current runloop with the addFileDescriptorMonitor: method. This method puts the file descriptor into a CFSocket, sticks that into a CFSocketRunLoopSource, and then adds that to the runloop. self, the pointer to the CocoaDirWatcherAppDelegate object, is used for the context of the CFSocket.

The callback function is called when an event is placed in the kqueue by the kernel. The callback then finds the pointer to the CocoaDirWatcherAppDelegate object that had been placed in the callback’s context pointer. The kqueue file descriptor is read from the CocoaDirWatcherAppDelegate and used in a call to kevent(). A new event is pulled off of the kqueue. This event has the name of the directory in the user data pointer. Finally, that directory name is put into the logview.

Previous editions of this book had the socketCallBack() access the file descriptor directly from the CocoaDirWatcherAppDelegate structure using the arrow (->) operator. This technique is no longer required, now that private @properties will let you create an API to the guts of a class without requiring that they go into the public interface in the header file.

Example 16.4. CocoaDirWatcherAppDelegate.m

#​i​m​p​o​r​t​ ​"​C​o​c​o​a​D​i​r​W​a​t​c​h​e​r​A​p​p​D​e​l​e​g​a​t​e​.​h​"​

#​i​m​p​o​r​t​ ​<​s​y​s​/​e​v​e​n​t​.​h​>​ ​ ​/​/​ ​f​o​r​ ​k​q​u​e​u​e​(​)​ ​a​n​d​ ​k​e​v​e​n​t​(​)​
#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​s​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​(​)​

@​i​n​t​e​r​f​a​c​e​ ​C​o​c​o​a​D​i​r​W​a​t​c​h​e​r​A​p​p​D​e​l​e​g​a​t​e​ ​(​)​
/​/​ ​A​d​d​ ​a​ ​p​r​o​p​e​r​t​y​ ​f​o​r​ ​t​h​e​ ​k​q​u​e​u​e​ ​f​i​l​e​ ​d​e​s​c​r​i​p​t​o​r​.​ ​ ​T​h​i​s​ ​w​i​l​l​ ​l​e​t​ ​t​h​e​ ​r​u​n​l​o​o​p​ ​c​a​l​l​b​a​c​k​
/​/​ ​f​u​n​c​t​i​o​n​ ​t​o​ ​g​e​t​ ​t​h​e​ ​f​d​.​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​i​n​t​ ​k​q​f​d​;​
@​e​n​d​ ​/​/​ ​e​x​t​e​n​s​i​o​n​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​C​o​c​o​a​D​i​r​W​a​t​c​h​e​r​A​p​p​D​e​l​e​g​a​t​e​

@​s​y​n​t​h​e​s​i​z​e​ ​w​i​n​d​o​w​ ​=​ ​_​w​i​n​d​o​w​;​
@​s​y​n​t​h​e​s​i​z​e​ ​l​o​g​V​i​e​w​ ​=​ ​_​l​o​g​V​i​e​w​;​
@​s​y​n​t​h​e​s​i​z​e​ ​k​q​f​d​ ​=​ ​_​k​q​f​d​;​

/​/​ ​I​n​f​o​r​m​ ​t​h​e​ ​u​s​e​r​ ​t​h​a​t​ ​s​o​m​e​t​h​i​n​g​ ​i​n​t​e​r​e​s​t​i​n​g​ ​h​a​p​p​e​n​e​d​ ​t​o​ ​p​a​t​h​
-​ ​(​v​o​i​d​)​ ​l​o​g​A​c​t​i​v​i​t​y​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​p​a​t​h​ ​{​
 ​ ​ ​ ​/​/​ ​l​o​g​ ​i​t​ ​t​o​ ​t​h​e​ ​c​o​n​s​o​l​e​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​a​c​t​i​v​i​t​y​ ​o​n​ ​%​@​"​,​ ​p​a​t​h​)​;​

 ​ ​ ​ ​/​/​ ​A​d​d​ ​i​t​ ​t​o​ ​t​h​e​ ​t​e​x​t​ ​v​i​e​w​.​
 ​ ​ ​ ​[​s​e​l​f​.​l​o​g​V​i​e​w​.​t​e​x​t​S​t​o​r​a​g​e​.​m​u​t​a​b​l​e​S​t​r​i​n​g​ ​a​p​p​e​n​d​S​t​r​i​n​g​:​ ​p​a​t​h​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​l​o​g​V​i​e​w​.​t​e​x​t​S​t​o​r​a​g​e​.​m​u​t​a​b​l​e​S​t​r​i​n​g​ ​a​p​p​e​n​d​S​t​r​i​n​g​:​ ​@​"​\​n​"​]​;​

 ​ ​ ​ ​/​/​ ​S​c​r​o​l​l​ ​t​o​ ​t​h​e​ ​e​n​d​
 ​ ​ ​ ​N​S​R​a​n​g​e​ ​e​n​d​P​o​i​n​t​ ​=​ ​N​S​M​a​k​e​R​a​n​g​e​ ​(​[​[​s​e​l​f​.​l​o​g​V​i​e​w​ ​s​t​r​i​n​g​]​ ​l​e​n​g​t​h​]​,​ ​0​)​;​
 ​ ​ ​ ​[​s​e​l​f​.​l​o​g​V​i​e​w​ ​s​c​r​o​l​l​R​a​n​g​e​T​o​V​i​s​i​b​l​e​:​ ​e​n​d​P​o​i​n​t​]​;​

}​ ​/​/​ ​l​o​g​A​c​t​i​v​i​t​y​

/​/​ ​S​o​m​e​ ​a​c​t​i​v​i​t​y​ ​h​a​s​ ​h​a​p​p​e​n​e​d​ ​o​n​ ​t​h​e​ ​k​q​u​e​u​e​ ​f​i​l​e​ ​d​e​s​c​r​i​p​t​o​r​.​
/​/​ ​C​a​l​l​ ​k​e​v​e​n​t​(​)​ ​t​o​ ​p​i​c​k​ ​u​p​ ​t​h​e​ ​n​e​w​ ​e​v​e​n​t​ ​w​a​i​t​i​n​g​ ​f​o​r​ ​u​s​
v​o​i​d​ ​s​o​c​k​e​t​C​a​l​l​B​a​c​k​ ​(​C​F​S​o​c​k​e​t​R​e​f​ ​s​o​c​k​e​t​r​e​f​,​ ​C​F​S​o​c​k​e​t​C​a​l​l​B​a​c​k​T​y​p​e​ ​t​y​p​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​D​a​t​a​R​e​f​ ​a​d​d​r​e​s​s​,​ ​c​o​n​s​t​ ​v​o​i​d​ ​*​d​a​t​a​,​ ​v​o​i​d​ ​*​i​n​f​o​)​ ​{​
 ​ ​ ​ ​C​o​c​o​a​D​i​r​W​a​t​c​h​e​r​A​p​p​D​e​l​e​g​a​t​e​ ​*​m​e​ ​=​ ​(​C​o​c​o​a​D​i​r​W​a​t​c​h​e​r​A​p​p​D​e​l​e​g​a​t​e​ ​*​)​ ​i​n​f​o​;​

 ​ ​ ​ ​s​t​r​u​c​t​ ​k​e​v​e​n​t​ ​e​v​e​n​t​;​
 ​ ​ ​ ​i​f​ ​(​k​e​v​e​n​t​(​m​e​.​k​q​f​d​,​ ​N​U​L​L​,​ ​0​,​ ​&​e​v​e​n​t​,​ ​1​,​ ​N​U​L​L​)​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​o​u​l​d​ ​n​o​t​ ​p​i​c​k​ ​u​p​ ​e​v​e​n​t​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​m​e​ ​l​o​g​A​c​t​i​v​i​t​y​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​e​v​e​n​t​.​u​d​a​t​a​]​;​
 ​ ​ ​ ​}​

}​ ​/​/​ ​s​o​c​k​e​t​C​a​l​l​B​a​c​k​

/​/​ ​A​d​d​ ​t​h​e​ ​g​i​v​e​n​ ​d​i​r​e​c​t​o​r​y​ ​t​o​ ​t​h​e​ ​k​q​u​e​u​e​ ​f​o​r​ ​w​a​t​c​h​i​n​g​
-​ ​(​v​o​i​d​)​ ​w​a​t​c​h​D​i​r​e​c​t​o​r​y​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​d​i​r​n​a​m​e​ ​{​
 ​ ​ ​ ​i​n​t​ ​d​i​r​f​d​ ​=​ ​o​p​e​n​ ​(​[​d​i​r​n​a​m​e​ ​f​i​l​e​S​y​s​t​e​m​R​e​p​r​e​s​e​n​t​a​t​i​o​n​]​,​ ​O​_​R​D​O​N​L​Y​)​;​

 ​ ​ ​ ​i​f​ ​(​d​i​r​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​o​u​l​d​ ​n​o​t​ ​o​p​e​n​ ​%​@​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​r​n​a​m​e​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​s​t​r​u​c​t​ ​k​e​v​e​n​t​ ​d​i​r​e​v​e​n​t​;​
 ​ ​ ​ ​E​V​_​S​E​T​ ​(​&​d​i​r​e​v​e​n​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​r​f​d​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​E​V​F​I​L​T​_​V​N​O​D​E​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​E​V​_​A​D​D​ ​|​ ​E​V​_​C​L​E​A​R​ ​|​ ​E​V​_​E​N​A​B​L​E​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​O​T​E​_​W​R​I​T​E​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​0​,​ ​[​d​i​r​n​a​m​e​ ​c​o​p​y​]​)​;​

 ​ ​ ​ ​/​/​ ​R​e​g​i​s​t​e​r​ ​e​v​e​n​t​.​
 ​ ​ ​ ​i​f​ ​(​k​e​v​e​n​t​(​s​e​l​f​.​k​q​f​d​,​ ​&​d​i​r​e​v​e​n​t​,​ ​1​,​ ​N​U​L​L​,​ ​0​,​ ​N​U​L​L​)​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​o​u​l​d​ ​n​o​t​ ​k​e​v​e​n​t​ ​w​a​t​c​h​i​n​g​ ​%​@​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​r​n​a​m​e​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​}​

}​ ​/​/​ ​w​a​t​c​h​D​i​r​e​c​t​o​r​y​

/​/​ ​A​d​d​ ​t​h​e​ ​f​i​l​e​ ​d​e​s​c​r​i​p​t​o​r​ ​t​o​ ​t​h​e​ ​r​u​n​l​o​o​p​.​ ​ ​W​h​e​n​ ​a​c​t​i​v​i​t​y​ ​h​a​p​p​e​n​s​,​
/​/​ ​s​u​c​h​ ​a​s​ ​n​e​w​ ​d​a​t​a​ ​o​n​ ​a​ ​s​o​c​k​e​t​ ​o​r​ ​a​ ​n​e​w​ ​e​v​e​n​t​ ​i​n​ ​a​ ​k​q​u​e​u​e​(​)​,​
/​/​ ​c​a​l​l​ ​t​h​e​ ​s​o​c​k​e​t​C​a​l​l​B​a​c​k​ ​f​u​n​c​t​i​o​n​.​
-​ ​(​v​o​i​d​)​ ​a​d​d​F​i​l​e​D​e​s​c​r​i​p​t​o​r​M​o​n​i​t​o​r​:​ ​(​i​n​t​)​ ​f​d​ ​{​
 ​ ​ ​ ​C​F​S​o​c​k​e​t​C​o​n​t​e​x​t​ ​c​o​n​t​e​x​t​ ​=​ ​{​ ​0​,​ ​s​e​l​f​,​ ​N​U​L​L​,​ ​N​U​L​L​,​ ​N​U​L​L​ ​}​;​

 ​ ​ ​ ​_​r​u​n​L​o​o​p​S​o​c​k​e​t​ ​=​ ​C​F​S​o​c​k​e​t​C​r​e​a​t​e​W​i​t​h​N​a​t​i​v​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​d​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​C​F​S​o​c​k​e​t​R​e​a​d​C​a​l​l​B​a​c​k​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​o​c​k​e​t​C​a​l​l​B​a​c​k​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​&​c​o​n​t​e​x​t​)​;​
 ​ ​ ​ ​i​f​ ​(​_​r​u​n​L​o​o​p​S​o​c​k​e​t​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​o​u​l​d​ ​n​o​t​ ​C​F​S​o​c​k​e​t​C​r​e​a​t​e​W​i​t​h​N​a​t​i​v​e​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​C​F​R​u​n​L​o​o​p​S​o​u​r​c​e​R​e​f​ ​r​l​s​ ​=​ ​C​F​S​o​c​k​e​t​C​r​e​a​t​e​R​u​n​L​o​o​p​S​o​u​r​c​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​_​r​u​n​L​o​o​p​S​o​c​k​e​t​,​ ​0​)​;​
 ​ ​ ​ ​i​f​ ​(​r​l​s​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​o​u​l​d​ ​n​o​t​ ​c​r​e​a​t​e​ ​a​ ​r​u​n​ ​l​o​o​p​ ​s​o​u​r​c​e​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​C​F​R​u​n​L​o​o​p​A​d​d​S​o​u​r​c​e​ ​(​C​F​R​u​n​L​o​o​p​G​e​t​C​u​r​r​e​n​t​(​)​,​ ​r​l​s​,​ ​k​C​F​R​u​n​L​o​o​p​D​e​f​a​u​l​t​M​o​d​e​)​;​
 ​ ​ ​ ​C​F​R​e​l​e​a​s​e​ ​(​r​l​s​)​;​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​r​e​t​u​r​n​;​

}​ ​/​/​ ​a​d​d​F​i​l​e​D​e​s​c​r​i​p​t​o​r​M​o​n​i​t​o​r​

-​ ​(​v​o​i​d​)​ ​a​p​p​l​i​c​a​t​i​o​n​D​i​d​F​i​n​i​s​h​L​a​u​n​c​h​i​n​g​:​ ​(​N​S​N​o​t​i​f​i​c​a​t​i​o​n​ ​*​)​ ​n​o​t​i​f​i​c​a​t​i​o​n​ ​{​
 ​ ​ ​ ​s​e​l​f​.​k​q​f​d​ ​=​ ​k​q​u​e​u​e​ ​(​)​;​

 ​ ​ ​ ​i​f​ ​(​s​e​l​f​.​k​q​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​c​o​u​l​d​ ​n​o​t​ ​c​r​e​a​t​e​ ​k​q​u​e​u​e​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​N​S​A​p​p​l​i​c​a​t​i​o​n​ ​s​h​a​r​e​d​A​p​p​l​i​c​a​t​i​o​n​]​ ​t​e​r​m​i​n​a​t​e​:​ ​s​e​l​f​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​[​s​e​l​f​ ​w​a​t​c​h​D​i​r​e​c​t​o​r​y​:​ ​@​"​/​t​m​p​"​]​;​
 ​ ​ ​ ​[​s​e​l​f​ ​w​a​t​c​h​D​i​r​e​c​t​o​r​y​:​ ​N​S​H​o​m​e​D​i​r​e​c​t​o​r​y​(​)​]​;​
 ​ ​ ​ ​[​s​e​l​f​ ​w​a​t​c​h​D​i​r​e​c​t​o​r​y​:​ ​[​@​"​~​/​L​i​b​r​a​r​y​/​P​r​e​f​e​r​e​n​c​e​s​"​ ​s​t​r​i​n​g​B​y​E​x​p​a​n​d​i​n​g​T​i​l​d​e​I​n​P​a​t​h​]​]​;​

 ​ ​ ​ ​[​s​e​l​f​ ​a​d​d​F​i​l​e​D​e​s​c​r​i​p​t​o​r​M​o​n​i​t​o​r​:​ ​s​e​l​f​.​k​q​f​d​]​;​

}​ ​/​/​ ​a​p​p​l​i​c​a​t​i​o​n​D​i​d​F​i​n​i​s​h​L​a​u​n​c​h​i​n​g​

@​e​n​d​ ​/​/​ ​C​o​c​o​a​D​i​r​W​a​t​c​h​e​r​A​p​p​D​e​l​e​g​a​t​e​

Compile and run the program. Make some changes to the watched directories and see the program react. Opening and closing GUI applications can also cause activity in the ~/Library/Preferences directory.

fsevents

kqueue is a wonderful tool if you want to monitor individual things, like detecting single file changes or modifications to a single folder. However, kqueue doesn’t scale well (API-wise) if you need to monitor a hierarchy of directories. To see changes to a directory hierarchy, you have to open each of the directories and subdirectories and add them to a kqueue. You also need to open any new subdirectories that are created and add them to your kqueue. Open file descriptors are a scarce resource, so you could run out of them before you run out of directories to monitor. OS X 10.5 introduced FSEvents, file system events, which will notify you if something changes in the file system as a whole, or in a particular directory hierarchy. FSEvents is a good alternative to kqueue for these kinds of operations.

Tiger (OS X 10.4) has an undocumented way of watching file system events that happen in real time, through the device /dev/fsevents. Spotlight uses this to keep up to date on changes to files and directories as they happen. The problem with /dev/fsevents, aside from it being undocumented, is that it has a limited buffer to store changes before they are read. Userland programs are not responsive enough to really keep up with the stream of events. If the file system event buffer overflows, events get dropped, and Spotlight may end up having to recrawl and reindex your drive. Not a good situation to get into. Later versions of Mac OS X still have /dev/fsevents, with the same limitations, but they have an API wrapped around the device.

fseventsd

Rather than having multiple programs listening to /dev/fsevents, OS X introduces a daemon, fseventsd, that reads from the device, as shown in Figure 16.3. fseventsd coalesces the change stream into fewer events and then notifies other programs that have registered an interest in particular directories. Apple provides an API that talks to fseventsd under the hood and feeds you file system changes via a callback function whenever an interesting event happens.

Figure 16.3  fseventsd notifying a file system change

You will not be able to get events in absolutely real time because of this intermediary daemon. This is fine for most applications, although Spotlight needs to see everything, as do things like virus checkers, so those applications would not use the FSEvents API. Time Machine, though, does not need to know about changes immediately, so it uses FSEvents to do its work when it is time to do a backup. Because of the non-real time nature of the events, you can provide a latency to FSEvents, saying “I want to be notified of events, but it’s ok if it is 10 seconds stale.”

Watching Directories

One way fseventsd reduces the amount of data it, and other applications, have to slog through is by only reporting file system changes on a per-directory basis. If the user adds a file to ~/blah/, modifies a file in there, and deletes another file, you’ll only get one “hey, ~/blah/ changed” notification. It is up to you to decide what to do in that case. In order to figure out which file changed, you will need to have a cached copy of the contents of the directories and then compare. If you are implementing something like drop boxes, where putting a file in a directory causes some action to happen, you can just scan the directory for new files.

You can choose which directory hierarchies to watch, and you can choose whether to watch them on a particular device or across the whole system. You will get notifications of any newly mounted drives and disk images if you watch the entire system, as well as notifications when these file systems go away. If you watch individual devices, you will not get these mount and unmount notifications.

Application bundles, as well as bundled files like Aperture vaults or Final Cut Pro documents, also count as directory hierarchies. You can use FSEvents to watch a document directory and notice when new graphic assets are dragged in, for instance.

Events

FSEvents are identified by a 64-bit event ID. These event IDs have no relationship with any particular date and time; they are just guaranteed monotonically increasing values. They are not necessarily continuous values because you might not see the activity of another user for security reasons. Also, when you mount a drive, its event database might have a higher eventID value than the current system maximum, so the system will increase its current event ID value to account for this new device.

Each disk has as an event database on it, which can be discarded if the disk is modified by an older version of OS X. If the disk is modified by another operating system, you will not know about what changes were made to it. Alas, FSEvents is not a perfect solution to all problems.

You can save event IDs for use later. If you do this, you will want to look at events on a per-device basis rather than across the whole machine. Each drive gets a unique identifier in the form of a UUID, which is a 128-bit universally unique identifier value guaranteed to be unique across time and space. You can use this UUID to make sure you are looking at the drive you think you are looking at, since it is possible for the user to mount a different drive with the same name as the one you were watching beforehand. Your program might get confused if it sees a strange stream of event IDs or an unexpected directory hierarchy. If you are just watching a drop box or an application bundle, then per-host event streams are fine to use.

Even though 64 bits can hold impossibly huge values (increasing a 64 bit value 1000 times a second will take over 500 million years to roll over), it is possible for the event IDs to wrap around on a system. Your FSEvents callback function will be passed a special notification flag when this happens. Perhaps this will become a Y500,000K problem.

History

FSEvents keeps a history of these directory changes and stores them in a database of sorts at the top of each device in a directory named .fseventsd. If you want to prevent the accumulation of this change history, say for security reasons or general paranoia, create an empty file called no_log in this directory.

Because each device keeps a history of changes, an application can be notified of events that have happened since the last time the program was run. Only directory changes are kept, so this database is fairly small. You can ask FSEvents “Hey, what happened since this last event I saw?” or “Hey, what happened since last Tuesday?” Your application can even crash and then pick up from the last event ID that it remembers seeing.

If you save events, be careful if you are monitoring the directory where the event ID is being stored, such as ~/Library/Preferences if you’re saving it in the user’s defaults. Saving the preference will trigger a notification, which will cause you to save the new id, which will trigger a notification, over and over again.

Also, even if you save off an event ID and a UUID, you may still get events earlier than the last one stored. The user may have restored a volume from a backup, the IDs may have wrapped, or even may have been manually purged by the user or an administrator.

There are still possibilities for buffer overruns in /dev/fsevents, and in fseventd, so you may be notified that you need to recrawl a particular directory hierarchy.

Visibility

FSEvents checks the permissions of directories before telling you about changes. This way you won’t be able to see another user’s directory names if they have sufficiently locked-down permissions. So if you have a directory named PinkWearsPants, and you know that Pink will be offended if you accuse him of wearing pants, then you should keep this directory in one with strict directory permissions that Pink would not be able to see into. Processes running as root will of course be able to see all events.

FSEvents API

The FSEvents notification mechanism is a runloop source, sitting around waiting for stuff to happen. A C callback function is called when a message from fseventsd shows up. The FSEventsStreamRef type is what you use to reference a particular event stream.

Creating the stream

Before you can start receiving events, you need to create the event stream with one of these two functions:

FSEventStreamRef FSEventStreamCreate (CFAllocatorRef allocator,
                                      FSEventStreamCallback callback,
                                      FSEventStreamContext *context,
                                      CFArrayRef pathsToWatch,
                                      FSEventStreamEventId sinceWhen,
                                      CFTimeInterval latency,
                                      FSEventStreamCreateFlags flags);

or

FSEventStreamRef
FSEventStreamCreateRelativeToDevice (CFAllocatorRef allocator,
                                     FSEventStreamCallback callback,
                                     FSEventStreamContext *context,
                                     dev_t deviceToWatch,
                                     CFArrayRef pathsToWatchRelativeToDevice,
                                     FSEventStreamEventId sinceWhen,
                                     CFTimeInterval latency,
                                     FSEventStreamCreateFlags flags);

There are a lot of common parameters between these two functions. CFAllocator is the typical Core Foundation allocator. Most often you pass NULL or kCFAllocatorDefault. callback is a pointer to the callback function to call when new events are available. context is the typical Core Foundation 5-element context pointer that you saw earlier in Chapter 15: CFRunLoop.

Each function takes a CFArray of CFStrings of paths to watch. You can use an NSArray of NSStrings because they are toll-free bridged. FSEventStreamCreateRelativeToDevice takes relative paths to the root of the device, and FSEventStreamCreate takes absolute paths from the top of the file system.

sinceWhen is an eventID. Pass kFSEventStreamEventIdSinceNow to start seeing new events, or pass an eventID that you have stored previously. If you pass an eventID, FSEvents will start feeding you historic events until you have caught up with the current event, and then it will start feeding you new events. The callback function is given a set of flags, one of which tells you when FSEvents has stopped feeding you history and is now giving you new events.

The latency parameter is the number of seconds that will pass between new events. A longer latency will result in a less responsive user interface, but it also reduces the number of events you will get, which can ultimately lead to more efficient processing. A user might unpack a 10,000 file tarball or zip archive in their home directory. With a low latency, you will get a lot of events because of the home directory being modified. With a high latency the entire tarball might be extracted between events, so you will only get one home directory notification event.

FSEventStreamCreateRelativeToDevice takes an additional argument, a dev_t for a device to watch. This dev_t is easy to obtain using stat():

 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​O​n​D​e​v​i​c​e​ ​=​ ​"​/​V​o​l​u​m​e​n​s​/​V​i​k​k​i​"​;​
 ​ ​ ​ ​s​t​r​u​c​t​ ​s​t​a​t​ ​s​t​a​t​b​u​f​;​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​s​t​a​t​(​[​p​a​t​h​ ​f​i​l​e​S​y​s​t​e​m​R​e​p​r​e​s​e​n​t​a​t​i​o​n​]​,​ ​&​s​t​a​t​b​u​f​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​/​/​ ​h​a​n​d​l​e​ ​e​r​r​o​r​
 ​ ​ ​ ​d​e​v​_​t​ ​d​e​v​i​c​e​T​o​W​a​t​c​h​ ​=​ ​s​t​a​t​b​u​f​.​s​t​_​d​e​v​;​

If you have a stream and want to see what device it is watching, use this call:

dev_t FSEventStreamGetDeviceBeingWatched (ConstFSEventStreamRef streamRef);

This returns the device the stream is watching. If the stream is not watching a device, it returns zero.

The flags parameter can be one of these values or a bitwise-OR of flags you want to combine:

k​F​S​E​v​e​n​t​S​t​r​e​a​m​C​r​e​a​t​e​F​l​a​g​N​o​n​e​
k​F​S​E​v​e​n​t​S​t​r​e​a​m​C​r​e​a​t​e​F​l​a​g​U​s​e​C​F​T​y​p​e​s​
k​F​S​E​v​e​n​t​S​t​r​e​a​m​C​r​e​a​t​e​F​l​a​g​N​o​D​e​f​e​r​
k​F​S​E​v​e​n​t​S​t​r​e​a​m​C​r​e​a​t​e​F​l​a​g​W​a​t​c​h​R​o​o​t​

kFSEventStreamCreateFlagUseCFTypes tells FSEvents that your callback function will be given a CFArray containing CFStrings of the paths have been modified. A C array of UTF-8 C strings is passed otherwise.

kFSEventStreamCreateFlagNoDefer controls what happens when the latency timer expires. fseventsd waits for the latency period before notifying your application if this flag is not set, leading to a possible double length of time before you get notifications. By setting this flag, you tell fseventsd to deliver any pending events as soon as the latency timer expires. You will want to set this flag if you have an interactive program to improve responsiveness. Background programs should leave this flag clear.

Setting kFSEventStreamCreateFlagWatchRoot requests notifications of changes along the paths you are watching. If you watch /Users/markd/blah/ for notifications with this flag set, you will receive a notification if the blah directory is moved or if the markd directory is moved or deleted, on up to the root of the path.

The stream objects have retain / release semantics. Use FSEventStreamRetain to increment the reference count and FSEventStreamRelease to decrement it.

Hook up to the runloop

You need to schedule your stream with a runloop and runloop mode:

void FSEventStreamScheduleWithRunLoop (FSEventStreamRef streamRef,
                                       CFRunLoopRef runLoop,
                                       CFStringRef runLoopMode);

You can get the CFRunLoop from an NSRunLoop by using -getCFRunLoop.

Then tell FSEvents to start feeding you events:

Boolean FSEventStreamStart (FSEventStreamRef streamRef);

FSEventStreamStart() returns True if it succeeds, False if it fails. If it fails, you won’t be getting any events from fseventsd.

Your callback will be called for new events while the run loop runs. Tell FSEvents to stop sending events once you are done. You can re-start the stream if you wish, and the event stream will pick up where it left off.

void FSEventStreamStop (FSEventStreamRef streamRef);

And then unschedule it from the runloop with these calls:

void FSEventStreamUnscheduleFromRunLoop (FSEventStreamRef streamRef,
                                         CFRunLoopRef runLoop,
                                         CFStringRef runLoopMode);

And then invalidate the stream. This unschedules it from any runloops it has been scheduled in.

void FSEventStreamInvalidate (FSEventStreamRef streamRef)l

Finally, release it when done.

The FSEvents callback

The callback function should have this signature:

void callbackFunction (ConstFSEventStreamRef streamRef,
                       void *context,
                       size_t numEvents,
                       void *eventPaths,
                       const FSEventStreamEventFlags eventFlags[],
                       const FSEventStreamEventId eventIds[]);

streamRef is the event stream created earlier. context is the pointer you supplied as the callback info when the stream was created. numEvents tells you how many events to process in this call.

If you set kFSEventStreamCreateFlagUseCFTypes during stream creation, eventPaths will be a pointer to a CFArray containing numEvents CFStrings. If you did not set this flag, eventPaths is a C array of numEvents UTF-8 C strings.

eventFlags and eventIds contain numEvents elements, too, which parallel the eventPaths array. eventIds contains the 64-bit event id for each path. eventFlags is the bitwise-OR of zero or more of these flags:

k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​M​u​s​t​S​c​a​n​S​u​b​D​i​r​s​
k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​U​s​e​r​D​r​o​p​p​e​d​
k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​K​e​r​n​e​l​D​r​o​p​p​e​d​

Sometimes events are dropped because they happen too fast, in which case the MustScanSubDirs flag will be set. You then need to scan the directory and any of its subdirectories to see what might have changed. The UserDropped or KernelDropped flag will be set depending on where the drop happened. This is mainly informational. There is not a lot you can do about dropped events, but it might help in debugging. You just need to look at the MustScanSubDirs flag.

k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​E​v​e​n​t​I​d​s​W​r​a​p​p​e​d​

The end of time has been reached, and the 64-bit event ID has wrapped around. If you are storing an event ID, say to know when to ignore historic event notifications, you will need to update your stored value.

k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​H​i​s​t​o​r​y​D​o​n​e​

When you specify a sinceWhen value at stream creation, FSEvents will feed you any events that happened since then and now. An event is sent with this flag set to mark the end of the historical events. Any events received from now on will be new. Ignore the path if you get this flag.

k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​R​o​o​t​C​h​a​n​g​e​d​

This is another special event, which is sent when one of the parent directories of a watched path change. When this flag is set, the event ID is zero, and the path is the watched path that changed. This path might not exist because it or something earlier in the path may have been renamed or removed. You will only get this event if you include kFSEventStreamCreateFlagWatchRoot when you created the stream.

k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​M​o​u​n​t​
k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​U​n​m​o​u​n​t​

This is another special event sent when a volume is mounted or unmounted on a directory (or subdirectory) that you are watching. The path passed to the callback is the volume mount point. You will not receive this event if you create an event stream relative to a device, only if you are watching events system-wide. These notifications are mainly informational. If you’re wanting to monitor disk mounts and unmounts, or wish to exert some kind of control over them, you will want to use the disk arbitration framework.

Miscellaneous calls

You can find out the current event ID, either system-wide or for a specific stream.

FSEventStreamEventId FSEventsGetCurrentEventId (void);

FSEventStreamEventId FSEventStreamGetLatestEventId (ConstFSEventStreamRef streamRef);

The event ID returned is the current system-wide event ID or the most recent one the stream has seen at the time of the call. Due to the asynchronous nature of all of this stuff, this value can change after you make this call and potentially use the event ID elsewhere.

You can ask for the last event ID for a given device before a given time:

FSEventStreamEventId
FSEventsGetLastEventIdForDeviceBeforeTime (dev_t dev, CFAbsoluteTime time);

This is a conservative value. That is, the returned event ID can be used as the sinceWhen parameter for an FSEventStreamCreateRelativeToDevice call. You will not miss any unseen events, but you might see some older events. Be aware time can be slippery. The system clock can change due to daylight savings time changes, or even if the user’s laptop travels to a different time zone.

CFAbsoluteTime is the number of seconds since January 1, 2001. To get a CFAbsoluteTime from an NSDate, you can pass the NSDate to CFDateGetAbsoluteTime. If you have a posix timestamp, which is 1970-based, subtract kCFAbsoluteTimeIntervalSince1970 from it.

When you are creating a stream relative to a device and you are using a stored event ID, you should check to make sure that the device you are using is the one you think you’re using. FSEventsCopyUUIDForDevice will give you a unique identifier for a given device. You can store this with your event ID and compare the device identifier the next time you create an event stream.

CFUUIDRef FSEventsCopyUUIDForDevice (dev_t dev);

This function returns a pointer to a UUID with copy semantics, so you are responsible for CFRelease()ing it when you are done. You will get NULL back if there is no UUID available, say for a read-only device, or if there are no historical events available. In that case, do not supply a sinceWhen value other than kFSEventStreamEventIdSinceNow when creating the stream.

You can extract the UUID’s bytes from the CFUUID object, or you can convert between string representations of the UUID and a CFUUID object by using these calls:

CFUUIDRef CFUUIDCreateFromString (CFAllocatorRef alloc, CFStringRef uuidStr);

CFStringRef CFUUIDCreateString (CFAllocatorRef alloc, CFUUIDRef uuid);

These calls create objects, so you are responsible for CFRelease()ing them. If you are going to be saving UUIDs, it is easiest to convert it to a string, store that string, and then convert it back to a CFUUID when needed. You can compare UUID equality easily by testing for string equality.

Example

Example 16.5 shows a program that displays all of the historical events for the boot drive that have happened since it was last run. After that, it displays new events as they occur. To keep things simple, it exits after seeing 20 new events. It saves the device UUID and last event ID to the user defaults when it quits.

Example 16.5. fsevents.m

/​/​ ​f​s​e​v​e​n​t​s​.​m​ ​-​-​ ​W​a​t​c​h​ ​f​i​l​e​ ​s​y​s​t​e​m​ ​e​v​e​n​t​s​.​

/​/​ ​g​c​c​ ​-​s​t​d​=​c​9​9​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​f​r​a​m​e​w​o​r​k​ ​C​o​r​e​S​e​r​v​i​c​e​s​
/​/​ ​ ​ ​ ​ ​-​o​ ​f​s​e​v​e​n​t​s​ ​f​s​e​v​e​n​t​s​.​m​

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

#​i​m​p​o​r​t​ ​<​C​o​r​e​S​e​r​v​i​c​e​s​/​C​o​r​e​S​e​r​v​i​c​e​s​.​h​>​ ​ ​/​/​ ​F​S​E​v​e​n​t​s​(​)​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​t​a​t​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​s​t​a​t​(​)​

/​/​ ​D​u​m​p​ ​o​u​t​ ​a​l​l​ ​h​i​s​t​o​r​i​c​ ​e​v​e​n​t​s​ ​b​e​f​o​r​e​ ​e​x​i​t​i​n​g​ ​a​f​t​e​r​ ​s​e​e​i​n​g​ ​N​ ​n​e​w​ ​e​v​e​n​t​s​.​
#​d​e​f​i​n​e​ ​M​A​X​_​C​A​L​L​B​A​C​K​S​ ​2​0​
s​t​a​t​i​c​ ​B​O​O​L​ ​g​_​n​e​w​E​v​e​n​t​s​ ​=​ ​Y​E​S​;​

/​/​ ​S​t​r​i​n​g​ ​c​o​n​s​t​a​n​t​s​ ​s​o​ ​t​y​p​o​s​ ​w​o​n​'​t​ ​c​a​u​s​e​ ​d​e​b​u​g​g​i​n​g​ ​h​e​a​d​a​c​h​e​s​.​
#​d​e​f​i​n​e​ ​L​A​S​T​_​E​V​E​N​T​_​K​E​Y​ ​@​"​f​s​e​v​e​n​t​s​S​a​m​p​l​e​L​a​s​t​E​v​e​n​t​"​
#​d​e​f​i​n​e​ ​D​E​V​I​C​E​_​U​U​I​D​_​K​E​Y​ ​@​"​f​s​e​v​e​n​t​s​S​a​m​p​l​e​D​e​v​i​c​e​U​U​I​D​"​

/​/​ ​L​o​o​k​u​p​ ​t​a​b​l​e​ ​f​o​r​ ​d​i​s​p​l​a​y​i​n​g​ ​h​u​m​a​n​-​r​e​a​d​a​b​i​l​e​ ​t​r​a​n​s​l​a​t​i​o​n​s​ ​o​f​ ​f​l​a​g​s​
t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​F​l​a​g​M​a​p​ ​{​
 ​ ​ ​ ​i​n​t​ ​b​i​t​f​l​a​g​;​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​d​e​s​c​r​i​p​t​i​o​n​;​
}​ ​F​l​a​g​M​a​p​;​

s​t​a​t​i​c​ ​F​l​a​g​M​a​p​ ​f​l​a​g​m​a​p​[​]​ ​=​ ​{​
 ​ ​ ​ ​{​ ​k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​M​u​s​t​S​c​a​n​S​u​b​D​i​r​s​,​ ​"​m​u​s​t​ ​s​c​a​n​ ​s​u​b​d​i​r​s​"​ ​ ​ ​ ​ ​}​,​
 ​ ​ ​ ​{​ ​k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​U​s​e​r​D​r​o​p​p​e​d​,​ ​ ​ ​ ​ ​"​u​s​e​r​ ​d​r​o​p​p​e​d​ ​e​v​e​n​t​s​"​ ​ ​ ​}​,​
 ​ ​ ​ ​{​ ​k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​K​e​r​n​e​l​D​r​o​p​p​e​d​,​ ​ ​ ​"​k​e​r​n​e​l​ ​d​r​o​p​p​e​d​ ​e​v​e​n​t​s​"​ ​}​,​
 ​ ​ ​ ​{​ ​k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​E​v​e​n​t​I​d​s​W​r​a​p​p​e​d​,​ ​"​e​v​e​n​t​ ​i​d​s​ ​w​r​a​p​p​e​d​"​ ​ ​ ​ ​ ​}​,​
 ​ ​ ​ ​{​ ​k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​H​i​s​t​o​r​y​D​o​n​e​,​ ​ ​ ​ ​ ​"​h​i​s​t​o​r​y​ ​p​l​a​y​b​a​c​k​ ​d​o​n​e​"​ ​}​,​
 ​ ​ ​ ​{​ ​k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​R​o​o​t​C​h​a​n​g​e​d​,​ ​ ​ ​ ​ ​"​r​o​o​t​ ​c​h​a​n​g​e​d​"​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​,​
 ​ ​ ​ ​{​ ​k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​M​o​u​n​t​,​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​m​o​u​n​t​e​d​"​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​,​
 ​ ​ ​ ​{​ ​k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​U​n​m​o​u​n​t​,​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​u​n​m​o​u​n​t​e​d​"​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
}​;​

s​t​a​t​i​c​ ​v​o​i​d​ ​c​a​l​l​b​a​c​k​F​u​n​c​t​i​o​n​ ​(​C​o​n​s​t​F​S​E​v​e​n​t​S​t​r​e​a​m​R​e​f​ ​s​t​r​e​a​m​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​v​o​i​d​ ​*​c​l​i​e​n​t​C​a​l​l​B​a​c​k​I​n​f​o​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​_​t​ ​n​u​m​E​v​e​n​t​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​v​o​i​d​ ​*​e​v​e​n​t​P​a​t​h​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​s​ ​e​v​e​n​t​F​l​a​g​s​[​]​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​s​t​ ​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​I​d​ ​e​v​e​n​t​I​d​s​[​]​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​\​n​"​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​e​v​e​n​t​ ​c​o​u​n​t​:​ ​%​l​u​\​n​"​,​ ​n​u​m​E​v​e​n​t​s​)​;​

 ​ ​ ​ ​/​/​ ​K​e​e​p​ ​t​r​a​c​k​ ​o​f​ ​t​h​e​ ​e​v​e​n​t​I​d​s​ ​s​o​ ​w​e​ ​c​a​n​ ​s​a​v​e​ ​i​t​ ​o​f​f​ ​w​h​e​n​ ​w​e​'​r​e​
 ​ ​ ​ ​/​/​ ​d​o​n​e​ ​l​o​o​k​i​n​g​ ​a​t​ ​e​v​e​n​t​s​.​
 ​ ​ ​ ​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​I​d​ ​c​u​r​r​e​n​t​E​v​e​n​t​ ​=​ ​0​;​

 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​n​u​m​E​v​e​n​t​s​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​A​d​d​ ​a​n​ ​e​x​t​r​a​ ​b​l​a​n​k​ ​l​i​n​e​ ​f​o​r​ ​b​r​e​a​t​h​i​n​g​ ​r​o​o​m​.​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​n​"​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​D​u​m​p​ ​o​u​t​ ​t​h​e​ ​a​r​g​u​m​e​n​t​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​p​a​t​h​[​%​d​]​ ​:​ ​%​s​ ​:​ ​i​d​ ​%​l​l​u​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​,​ ​(​(​c​h​a​r​ ​*​*​)​e​v​e​n​t​P​a​t​h​s​)​[​i​]​,​ ​e​v​e​n​t​I​d​s​[​i​]​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​s​ ​f​l​a​g​s​ ​=​ ​e​v​e​n​t​F​l​a​g​s​[​i​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​f​l​a​g​s​:​ ​%​x​\​n​"​,​ ​(​i​n​t​)​f​l​a​g​s​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​f​l​a​g​s​ ​=​=​ ​k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​N​o​n​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​ ​ ​s​o​m​e​t​h​i​n​g​ ​h​a​p​p​e​n​e​d​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​D​i​s​p​l​a​y​ ​a​l​l​ ​o​f​ ​t​h​e​ ​s​e​t​ ​f​l​a​g​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​F​l​a​g​M​a​p​ ​*​s​c​a​n​ ​=​ ​f​l​a​g​m​a​p​;​
 ​ ​ ​ ​ ​ ​ ​ ​F​l​a​g​M​a​p​ ​*​s​t​o​p​ ​=​ ​s​c​a​n​ ​+​ ​s​i​z​e​o​f​(​f​l​a​g​m​a​p​)​ ​/​ ​s​i​z​e​o​f​(​*​f​l​a​g​m​a​p​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​w​h​i​l​e​ ​(​s​c​a​n​ ​<​ ​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​f​l​a​g​s​ ​&​ ​s​c​a​n​-​>​b​i​t​f​l​a​g​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​ ​ ​%​s​\​n​"​,​ ​s​c​a​n​-​>​d​e​s​c​r​i​p​t​i​o​n​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​c​a​n​+​+​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​f​l​a​g​s​ ​&​ ​k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​F​l​a​g​H​i​s​t​o​r​y​D​o​n​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​e​ ​c​a​n​ ​s​t​o​p​ ​p​r​i​n​t​i​n​g​ ​h​i​s​t​o​r​i​c​ ​e​v​e​n​t​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​_​n​e​w​E​v​e​n​t​s​ ​=​ ​Y​E​S​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​D​o​n​'​t​ ​d​r​o​p​ ​i​n​t​o​ ​n​e​w​ ​e​v​e​n​t​ ​c​a​s​e​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​i​ ​=​=​ ​n​u​m​E​v​e​n​t​s​ ​-​ ​1​)​ ​g​o​t​o​ ​d​o​n​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​R​e​m​e​m​b​e​r​ ​w​h​a​t​ ​o​u​r​ ​l​a​s​t​ ​e​v​e​n​t​ ​w​a​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​c​u​r​r​e​n​t​E​v​e​n​t​ ​=​ ​e​v​e​n​t​I​d​s​[​i​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​D​o​n​'​t​ ​c​o​u​n​t​ ​h​i​s​t​o​r​y​ ​e​v​e​n​t​s​ ​a​g​a​i​n​s​t​ ​o​u​r​ ​c​a​l​l​b​a​c​k​ ​c​o​u​n​t​.​
 ​ ​ ​ ​i​f​ ​(​!​g​_​n​e​w​E​v​e​n​t​s​)​ ​g​o​t​o​ ​d​o​n​e​;​

 ​ ​ ​ ​s​t​a​t​i​c​ ​i​n​t​ ​s​_​c​a​l​l​b​a​c​k​C​o​u​n​t​;​
 ​ ​ ​ ​s​_​c​a​l​l​b​a​c​k​C​o​u​n​t​+​+​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​d​ ​l​e​f​t​\​n​"​,​ ​M​A​X​_​C​A​L​L​B​A​C​K​S​ ​-​ ​s​_​c​a​l​l​b​a​c​k​C​o​u​n​t​)​;​

 ​ ​ ​ ​i​f​ ​(​s​_​c​a​l​l​b​a​c​k​C​o​u​n​t​ ​>​=​ ​M​A​X​_​C​A​L​L​B​A​C​K​S​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​U​s​e​r​D​e​f​a​u​l​t​s​ ​*​d​e​f​s​ ​=​ ​[​N​S​U​s​e​r​D​e​f​a​u​l​t​s​ ​s​t​a​n​d​a​r​d​U​s​e​r​D​e​f​a​u​l​t​s​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​S​a​v​e​ ​t​h​e​ ​l​a​s​t​ ​e​v​e​n​t​ ​I​D​ ​s​e​e​n​ ​i​n​t​o​ ​u​s​e​r​ ​d​e​f​a​u​l​t​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​N​u​m​b​e​r​ ​*​e​v​e​n​t​I​d​N​u​m​b​e​r​ ​=​ ​[​N​S​N​u​m​b​e​r​ ​n​u​m​b​e​r​W​i​t​h​U​n​s​i​g​n​e​d​L​o​n​g​L​o​n​g​:​ ​c​u​r​r​e​n​t​E​v​e​n​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​d​e​f​s​ ​s​e​t​O​b​j​e​c​t​:​ ​e​v​e​n​t​I​d​N​u​m​b​e​r​ ​ ​f​o​r​K​e​y​:​ ​L​A​S​T​_​E​V​E​N​T​_​K​E​Y​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​S​a​v​e​ ​t​h​e​ ​d​e​v​i​c​e​ ​U​U​I​D​.​
 ​ ​ ​ ​ ​ ​ ​ ​d​e​v​_​t​ ​d​e​v​i​c​e​ ​=​ ​F​S​E​v​e​n​t​S​t​r​e​a​m​G​e​t​D​e​v​i​c​e​B​e​i​n​g​W​a​t​c​h​e​d​ ​(​s​t​r​e​a​m​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​d​e​v​i​c​e​ ​!​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​U​U​I​D​R​e​f​ ​d​e​v​U​U​I​D​ ​=​ ​F​S​E​v​e​n​t​s​C​o​p​y​U​U​I​D​F​o​r​D​e​v​i​c​e​ ​(​d​e​v​i​c​e​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​d​e​v​U​U​I​D​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​S​t​r​i​n​g​R​e​f​ ​s​t​r​i​n​g​F​o​r​m​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​U​U​I​D​C​r​e​a​t​e​S​t​r​i​n​g​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​ ​d​e​v​U​U​I​D​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​d​e​f​s​ ​s​e​t​O​b​j​e​c​t​:​ ​(​i​d​)​s​t​r​i​n​g​F​o​r​m​ ​ ​f​o​r​K​e​y​:​ ​D​E​V​I​C​E​_​U​U​I​D​_​K​E​Y​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​R​e​l​e​a​s​e​ ​(​d​e​v​U​U​I​D​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​M​a​k​e​ ​s​u​r​e​ ​i​t​ ​r​e​a​c​h​e​s​ ​t​h​e​ ​d​i​s​k​.​
 ​ ​ ​ ​ ​ ​ ​ ​[​d​e​f​s​ ​s​y​n​c​h​r​o​n​i​z​e​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​a​l​l​ ​d​o​n​e​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​e​x​i​t​ ​(​E​X​I​T​_​S​U​C​C​E​S​S​)​;​
 ​ ​ ​ ​}​

d​o​n​e​:​
 ​ ​ ​ ​r​e​t​u​r​n​;​

}​ ​/​/​ ​c​a​l​l​b​a​c​k​F​u​n​c​t​i​o​n​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​/​/​ ​W​a​t​c​h​ ​t​h​e​ ​w​h​o​l​e​ ​d​e​v​i​c​e​.​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​ ​=​ ​@​"​/​"​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​d​e​v​i​c​e​ ​I​D​.​
 ​ ​ ​ ​s​t​r​u​c​t​ ​s​t​a​t​ ​s​t​a​t​b​u​f​;​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​s​t​a​t​(​[​p​a​t​h​ ​f​i​l​e​S​y​s​t​e​m​R​e​p​r​e​s​e​n​t​a​t​i​o​n​]​,​ ​&​s​t​a​t​b​u​f​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​e​r​r​o​r​ ​w​i​t​h​ ​s​t​a​t​.​ ​ ​%​d​\​n​"​,​ ​e​r​r​n​o​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​d​e​v​_​t​ ​d​e​v​i​c​e​ ​=​ ​s​t​a​t​b​u​f​.​s​t​_​d​e​v​;​

 ​ ​ ​ ​/​/​ ​F​i​n​d​ ​t​h​e​ ​l​a​s​t​ ​e​v​e​n​t​ ​w​e​ ​s​a​w​.​
 ​ ​ ​ ​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​I​d​ ​l​a​s​t​E​v​e​n​t​ ​=​ ​k​F​S​E​v​e​n​t​S​t​r​e​a​m​E​v​e​n​t​I​d​S​i​n​c​e​N​o​w​;​
 ​ ​ ​ ​N​S​U​s​e​r​D​e​f​a​u​l​t​s​ ​*​d​e​f​s​ ​=​ ​[​N​S​U​s​e​r​D​e​f​a​u​l​t​s​ ​s​t​a​n​d​a​r​d​U​s​e​r​D​e​f​a​u​l​t​s​]​;​
 ​ ​ ​ ​N​S​N​u​m​b​e​r​ ​*​l​a​s​t​E​v​e​n​t​N​u​m​b​e​r​ ​=​ ​[​d​e​f​s​ ​o​b​j​e​c​t​F​o​r​K​e​y​:​ ​L​A​S​T​_​E​V​E​N​T​_​K​E​Y​]​;​

 ​ ​ ​ ​i​f​ ​(​l​a​s​t​E​v​e​n​t​N​u​m​b​e​r​ ​!​=​ ​n​i​l​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​M​a​k​e​ ​s​u​r​e​ ​i​t​'​s​ ​t​h​e​ ​r​i​g​h​t​ ​d​e​v​i​c​e​ ​b​y​ ​m​a​k​i​n​g​ ​s​u​r​e​ ​t​h​e​ ​U​U​I​D​s​ ​m​a​t​c​h​.​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​u​u​i​d​S​t​r​i​n​g​ ​=​ ​[​d​e​f​s​ ​s​t​r​i​n​g​F​o​r​K​e​y​:​ ​D​E​V​I​C​E​_​U​U​I​D​_​K​E​Y​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​C​F​U​U​I​D​R​e​f​ ​d​e​v​U​U​I​D​ ​=​ ​F​S​E​v​e​n​t​s​C​o​p​y​U​U​I​D​F​o​r​D​e​v​i​c​e​ ​(​d​e​v​i​c​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​C​F​S​t​r​i​n​g​R​e​f​ ​d​e​v​S​t​r​i​n​g​ ​=​ ​C​F​U​U​I​D​C​r​e​a​t​e​S​t​r​i​n​g​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​ ​d​e​v​U​U​I​D​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​[​u​u​i​d​S​t​r​i​n​g​ ​i​s​E​q​u​a​l​T​o​S​t​r​i​n​g​:​ ​(​i​d​)​d​e​v​S​t​r​i​n​g​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​e​ ​h​a​v​e​ ​a​ ​g​o​o​d​ ​p​o​i​n​t​ ​t​o​ ​p​i​c​k​ ​u​p​ ​f​r​o​m​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​l​a​s​t​E​v​e​n​t​ ​=​ ​[​l​a​s​t​E​v​e​n​t​N​u​m​b​e​r​ ​u​n​s​i​g​n​e​d​L​o​n​g​L​o​n​g​V​a​l​u​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​P​i​c​k​i​n​g​ ​u​p​ ​w​h​e​r​e​ ​w​e​ ​l​e​f​t​ ​o​f​f​:​ ​ ​%​l​l​u​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​l​a​s​t​E​v​e​n​t​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​e​ ​s​h​o​u​l​d​ ​b​e​ ​g​e​t​t​i​n​g​ ​s​o​m​e​ ​h​i​s​t​o​r​y​ ​t​h​e​n​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​_​n​e​w​E​v​e​n​t​s​ ​=​ ​N​O​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​u​u​i​d​ ​m​i​s​m​a​t​c​h​:​ ​%​s​ ​v​s​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​u​u​i​d​S​t​r​i​n​g​ ​U​T​F​8​S​t​r​i​n​g​]​,​ ​[​(​i​d​)​d​e​v​S​t​r​i​n​g​ ​U​T​F​8​S​t​r​i​n​g​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​C​F​A​b​s​o​l​u​t​e​T​i​m​e​ ​l​a​t​e​n​c​y​ ​=​ ​3​.​0​;​ ​/​/​ ​l​a​t​e​n​c​y​ ​i​n​ ​s​e​c​o​n​d​s​
 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​p​a​t​h​s​ ​=​ ​[​N​S​A​r​r​a​y​ ​a​r​r​a​y​W​i​t​h​O​b​j​e​c​t​:​ ​p​a​t​h​]​;​

 ​ ​ ​ ​F​S​E​v​e​n​t​S​t​r​e​a​m​R​e​f​ ​s​t​r​e​a​m​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​F​S​E​v​e​n​t​S​t​r​e​a​m​C​r​e​a​t​e​R​e​l​a​t​i​v​e​T​o​D​e​v​i​c​e​ ​(​k​C​F​A​l​l​o​c​a​t​o​r​D​e​f​a​u​l​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​a​l​l​b​a​c​k​F​u​n​c​t​i​o​n​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​U​L​L​,​ ​/​/​ ​c​o​n​t​e​x​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​v​i​c​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​C​F​A​r​r​a​y​R​e​f​)​p​a​t​h​s​,​ ​/​/​ ​r​e​l​a​t​i​v​e​ ​t​o​ ​d​e​v​i​c​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​l​a​s​t​E​v​e​n​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​l​a​t​e​n​c​y​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​F​S​E​v​e​n​t​S​t​r​e​a​m​C​r​e​a​t​e​F​l​a​g​N​o​n​e​)​;​

 ​ ​ ​ ​i​f​ ​(​s​t​r​e​a​m​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​c​o​u​l​d​ ​n​o​t​ ​c​r​e​a​t​e​ ​s​t​r​e​a​m​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​N​S​R​u​n​L​o​o​p​ ​*​l​o​o​p​ ​=​ ​[​N​S​R​u​n​L​o​o​p​ ​c​u​r​r​e​n​t​R​u​n​L​o​o​p​]​;​
 ​ ​ ​ ​F​S​E​v​e​n​t​S​t​r​e​a​m​S​c​h​e​d​u​l​e​W​i​t​h​R​u​n​L​o​o​p​ ​(​s​t​r​e​a​m​,​ ​[​l​o​o​p​ ​g​e​t​C​F​R​u​n​L​o​o​p​]​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​C​F​R​u​n​L​o​o​p​D​e​f​a​u​l​t​M​o​d​e​)​;​
 ​ ​ ​ ​B​O​O​L​ ​s​u​c​c​e​s​s​ ​=​ ​F​S​E​v​e​n​t​S​t​r​e​a​m​S​t​a​r​t​ ​(​s​t​r​e​a​m​)​;​
 ​ ​ ​ ​i​f​ ​(​!​s​u​c​c​e​s​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​c​o​u​l​d​ ​n​o​t​ ​s​t​a​r​t​ ​F​S​E​v​e​n​t​ ​s​t​r​e​a​m​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​[​l​o​o​p​ ​r​u​n​]​;​

 ​ ​ ​ ​/​/​ ​T​h​i​s​ ​i​s​n​'​t​ ​a​c​t​u​a​l​l​y​ ​r​e​a​c​h​e​d​,​ ​b​u​t​ ​y​o​u​ ​w​o​u​l​d​ ​c​l​e​a​n​ ​u​p​ ​t​h​e​
 ​ ​ ​ ​/​/​ ​s​t​r​e​a​m​ ​l​i​k​e​ ​t​h​i​s​.​
 ​ ​ ​ ​F​S​E​v​e​n​t​S​t​r​e​a​m​S​t​o​p​ ​(​s​t​r​e​a​m​)​;​
 ​ ​ ​ ​F​S​E​v​e​n​t​S​t​r​e​a​m​U​n​s​c​h​e​d​u​l​e​F​r​o​m​R​u​n​L​o​o​p​ ​(​s​t​r​e​a​m​,​ ​[​l​o​o​p​ ​g​e​t​C​F​R​u​n​L​o​o​p​]​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​k​C​F​R​u​n​L​o​o​p​D​e​f​a​u​l​t​M​o​d​e​)​;​
 ​ ​ ​ ​F​S​E​v​e​n​t​S​t​r​e​a​m​I​n​v​a​l​i​d​a​t​e​ ​(​s​t​r​e​a​m​)​;​
 ​ ​ ​ ​F​S​E​v​e​n​t​S​t​r​e​a​m​R​e​l​e​a​s​e​ ​(​s​t​r​e​a​m​)​;​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

And some sample runs. Here is the first run, watching new events.

$​ ​.​/​f​s​e​v​e​n​t​s​

-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
e​v​e​n​t​ ​c​o​u​n​t​:​ ​1​

p​a​t​h​[​0​]​ ​:​ ​U​s​e​r​s​/​m​a​r​k​d​/​L​i​b​r​a​r​y​/​P​r​e​f​e​r​e​n​c​e​s​/​ ​:​ ​i​d​ ​8​3​1​9​6​3​6​
 ​ ​f​l​a​g​s​:​ ​0​
 ​ ​ ​ ​s​o​m​e​t​h​i​n​g​ ​h​a​p​p​e​n​e​d​
1​9​ ​l​e​f​t​
-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
e​v​e​n​t​ ​c​o​u​n​t​:​ ​2​

p​a​t​h​[​0​]​ ​:​ ​U​s​e​r​s​/​m​a​r​k​d​/​.​e​m​a​c​s​.​d​/​a​u​t​o​-​s​a​v​e​-​l​i​s​t​/​ ​:​ ​i​d​ ​8​3​1​9​6​4​2​
 ​ ​f​l​a​g​s​:​ ​0​
 ​ ​ ​ ​s​o​m​e​t​h​i​n​g​ ​h​a​p​p​e​n​e​d​

p​a​t​h​[​1​]​ ​:​ ​U​s​e​r​s​/​m​a​r​k​d​/​W​r​i​t​i​n​g​/​c​o​r​e​-​o​s​x​/​k​q​u​e​u​e​-​c​h​a​p​/​P​r​o​j​e​c​t​s​/​ ​:​ ​i​d​ ​8​3​1​9​6​5​4​
 ​ ​f​l​a​g​s​:​ ​0​
 ​ ​ ​ ​s​o​m​e​t​h​i​n​g​ ​h​a​p​p​e​n​e​d​
1​8​ ​l​e​f​t​
-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
.​.​.​

You can see that the first callback saw a change in my preferences directory. The second callback had two events: one for an editor autosave list and the other editing this chapter’s source file.

Next, the system has had a chance to do stuff without fsevents running.

$​ ​.​/​f​s​e​v​e​n​t​s​
P​i​c​k​i​n​g​ ​u​p​ ​w​h​e​r​e​ ​w​e​ ​l​e​f​t​ ​o​f​f​!​ ​ ​8​3​2​0​6​3​0​
-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
e​v​e​n​t​ ​c​o​u​n​t​:​ ​1​

p​a​t​h​[​0​]​ ​:​ ​U​s​e​r​s​/​m​a​r​k​d​/​L​i​b​r​a​r​y​/​P​r​e​f​e​r​e​n​c​e​s​/​ ​:​ ​i​d​ ​8​3​2​0​6​4​6​
 ​ ​f​l​a​g​s​:​ ​0​
 ​ ​ ​ ​s​o​m​e​t​h​i​n​g​ ​h​a​p​p​e​n​e​d​
-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
e​v​e​n​t​ ​c​o​u​n​t​:​ ​1​

p​a​t​h​[​0​]​ ​:​ ​U​s​e​r​s​/​m​a​r​k​d​/​L​i​b​r​a​r​y​/​C​a​c​h​e​s​/​M​e​t​a​d​a​t​a​/​S​a​f​a​r​i​/​H​i​s​t​o​r​y​/​ ​:​ ​i​d​ ​8​3​2​0​7​1​0​
 ​ ​f​l​a​g​s​:​ ​0​
 ​ ​ ​ ​s​o​m​e​t​h​i​n​g​ ​h​a​p​p​e​n​e​d​
-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​
e​v​e​n​t​ ​c​o​u​n​t​:​ ​1​

p​a​t​h​[​0​]​ ​:​ ​U​s​e​r​s​/​m​a​r​k​d​/​D​o​c​u​m​e​n​t​s​/​i​P​h​o​n​e​.​v​p​d​o​c​/​p​a​g​e​s​/​d​/​ ​:​ ​i​d​ ​8​3​2​0​8​0​3​
 ​ ​f​l​a​g​s​:​ ​0​
 ​ ​ ​ ​s​o​m​e​t​h​i​n​g​ ​h​a​p​p​e​n​e​d​
-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​-​

Here you can see that while fsevents was not running, the preferences directory was updated, a web page was visited, and a VoodooPad document was edited.

You can see the current settings with the defaults command.

$​ ​d​e​f​a​u​l​t​s​ ​r​e​a​d​ ​f​s​e​v​e​n​t​s​
{​
 ​ ​ ​ ​f​s​e​v​e​n​t​s​S​a​m​p​l​e​D​e​v​i​c​e​U​U​I​D​ ​=​ ​"​0​1​3​8​C​B​9​E​-​E​B​B​F​-​4​2​4​B​-​8​B​3​9​-​A​F​5​2​8​4​E​9​3​6​B​C​"​;​
 ​ ​ ​ ​f​s​e​v​e​n​t​s​S​a​m​p​l​e​L​a​s​t​E​v​e​n​t​ ​=​ ​8​3​2​1​3​5​3​;​
}​

And you can use defaults to remove the settings to run the program from a beginning state again.

$​ ​d​e​f​a​u​l​t​s​ ​d​e​l​e​t​e​ ​f​s​e​v​e​n​t​s​

Exercises

Add kqueue support to fsevents.m so that it will keep on showing all new events until the program is interrupted by a Control-C (SIGINT). Save off the event stream information and device UUID when that happens.

Modify fsevents.m to watch the entire system. Watch what happens if you mount a file system and modify things on that file system. You can create read/write disk images with Disk Utility.

Modify fsevents.m to watch more than one directory. Also watch what happens when you move a directory being watched.

Find out where the directory got moved to by opening the root of the directory hierarchy with open() and then use fcntl() with F_GETPATH to see where it is living in the file system.

17
Bonjour

Bonjour, which is Apple’s implementation of the Zeroconf standard, is a very compelling idea. It extends the idea of DNS so that things on the network can declare their intentions. For example, when a Zeroconf-compliant device is plugged into a network, it can declare “I am grouse.local, and my IP address is 168.254.32.1!” If another device already has that IP address, it can complain. The new device will then change its address. Thus, a network device can get an IP address without a DHCP server. This capability is known as link local addressing.

However, the beauty of Zeroconf does not end at devices and IP addresses. It also allows services to declare their name, type, address, and port number. Thus, the local network is informed of new devices and services as they are added. Furthermore, it adds the ability for the services to be browsed. That is, if you are looking for a type of service, you can ask for all the information about all the individual servers on a particular network.

The DNS standard actually already had a mechanism by which a service could be advertised – the SRV record. For example, to find all the ftp servers in the bignerdranch.com domain, your ftp client would ask for the SRV record for _ftp._tcp.bignerdranch.com. There are two reasons why you have probably never heard of the SRV record:

No one uses it.

Only a DNS server can advertise the service.

To make it possible for many, many machines to advertise services on a network, Multicast DNS was created. Essentially, every machine is running a Multicast DNS server. This daemon is /usr/sbin/mDNSResponder.

Thus, Zeroconf is a clever marriage of link local addressing and multicast DNS. Zeroconf is the name of the standard, and Bonjour is Apple’s implementation of that standard. There are three different APIs for dealing with Bonjour:

The mach-level API called DNSServiceDiscovery is what all the other APIs are based upon.

The C API in the CoreServices framework uses DNSServiceDiscovery but integrates more easily with the rest of Core Foundation.

The Objective-C classes NSNetService and NSNetServiceBrowser are part of the Foundation framework.

In this chapter, we will use the Objective-C API to let our chatter server declare its availability and let our chatter client browse for servers.

Publishing an NSNetService

In a server, you advertise the availability of your service by creating an instance of NSNetService and publishing it. You give the server a name, you tell it what service it provides, what port it runs on, and the name of the network it is part of (the domain). The important methods are:

-​ ​(​i​d​)​ ​i​n​i​t​W​i​t​h​D​o​m​a​i​n​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​d​o​m​a​i​n​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​y​p​e​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​t​y​p​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​a​m​e​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​n​a​m​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​o​r​t​:​ ​(​i​n​t​)​ ​p​o​r​t​

This is the initializer that you use when creating a service to be published. You may pass in @"" as the domain, and the host’s default domain will be used. The type is a string of the form “service_type.protocol”. As a convention, host names are not prepended with an underscore, whereas service and protocols are. The protocol is usually either _tcp or _udp. (For our example, the type will be @"_chatter._tcp.".) You can use any Unicode string as the name. The only tricky bit is that your name may conflict with another. The port is the port number upon which the server is waiting.

-​ ​(​v​o​i​d​)​ ​p​u​b​l​i​s​h​

This method advertises the service on the network. It returns immediately, and the delegate is informed later if it was a failure.

The delegate can implement:

-​ ​(​v​o​i​d​)​ ​n​e​t​S​e​r​v​i​c​e​:​ ​(​N​S​N​e​t​S​e​r​v​i​c​e​ ​*​)​ ​s​e​n​d​e​r​
 ​ ​ ​ ​ ​ ​d​i​d​N​o​t​P​u​b​l​i​s​h​:​ ​(​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​)​ ​e​r​r​o​r​D​i​c​t​

Notifies the delegate that the service offered by sender could not be published. You can use the dictionary keys NSNetServicesErrorCode and NSNetServicesErrorDomain to determine the cause of the error. A common error is that the name of your server was already claimed by another server in the same domain.

Make chatterserver Zeroconf-compliant

Copy chatterserver-runloop.m to chatterserver-bonjour.m and edit it. To make chatterserver Bonjour-aware, you will need to accept a command-line parameter with the service name, along with creating and configuring the NSNetService object. NSNetService can take a delegate, and it uses the delegate to report errors. So far, nothing in chatterserver is Objective-C object-oriented goodness, so you will need a small class just to be a delegate. Place this right after the wall of header file #includes:

@​i​n​t​e​r​f​a​c​e​ ​N​e​t​S​e​r​v​i​c​e​D​e​l​e​g​a​t​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​<​N​S​N​e​t​S​e​r​v​i​c​e​D​e​l​e​g​a​t​e​>​
@​e​n​d​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​N​e​t​S​e​r​v​i​c​e​D​e​l​e​g​a​t​e​

-​ ​(​v​o​i​d​)​ ​n​e​t​S​e​r​v​i​c​e​:​ ​(​N​S​N​e​t​S​e​r​v​i​c​e​ ​*​)​ ​s​e​n​d​e​r​
 ​ ​ ​ ​ ​ ​d​i​d​N​o​t​P​u​b​l​i​s​h​:​ ​(​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​)​ ​e​r​r​o​r​D​i​c​t​ ​{​

 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​f​a​i​l​e​d​ ​t​o​ ​p​u​b​l​i​s​h​ ​n​e​t​ ​s​e​r​v​i​c​e​:​ ​%​@​"​,​ ​e​r​r​o​r​D​i​c​t​)​;​

}​ ​/​/​ ​d​i​d​N​o​t​P​u​b​l​i​s​h​

@​e​n​d​ ​/​/​ ​N​e​t​S​e​r​v​i​c​e​D​e​l​e​g​a​t​e​

Now edit main to do the rest of the stuff:

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​=​=​ ​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​u​s​a​g​e​:​ ​%​s​ ​<​s​e​r​v​i​c​e​n​a​m​e​>​ ​[​i​p​v​6​]​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​i​n​t​ ​l​i​s​t​e​n​F​d​ ​=​ ​S​t​a​r​t​L​i​s​t​e​n​i​n​g​ ​(​u​s​e​I​P​v​6​)​;​

 ​ ​ ​ ​i​f​ ​(​l​i​s​t​e​n​F​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​*​*​*​ ​C​o​u​l​d​ ​n​o​t​ ​o​p​e​n​ ​l​i​s​t​e​n​i​n​g​ ​s​o​c​k​e​t​.​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​R​e​g​i​s​t​e​r​R​u​n​L​o​o​p​S​o​c​k​e​t​ ​(​l​i​s​t​e​n​F​d​,​ ​l​i​s​t​e​n​C​a​l​l​b​a​c​k​,​ ​Y​E​S​,​ ​N​U​L​L​)​;​

 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​s​e​r​v​i​c​e​N​a​m​e​ ​=​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​U​T​F​8​S​t​r​i​n​g​:​ ​a​r​g​v​[​1​]​]​;​
 ​ ​ ​ ​N​S​N​e​t​S​e​r​v​i​c​e​ ​*​n​e​t​S​e​r​v​i​c​e​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​N​S​N​e​t​S​e​r​v​i​c​e​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​D​o​m​a​i​n​:​ ​@​"​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​y​p​e​:​ ​@​"​_​c​h​a​t​t​e​r​.​_​t​c​p​.​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​a​m​e​:​ ​s​e​r​v​i​c​e​N​a​m​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​o​r​t​:​ ​k​P​o​r​t​N​u​m​b​e​r​]​;​
 ​ ​ ​ ​N​e​t​S​e​r​v​i​c​e​D​e​l​e​g​a​t​e​ ​*​d​e​l​e​g​a​t​e​
 ​ ​ ​ ​ ​ ​ ​ ​=​ ​[​[​[​N​e​t​S​e​r​v​i​c​e​D​e​l​e​g​a​t​e​ ​a​l​l​o​c​]​ ​i​n​i​t​]​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​n​e​t​S​e​r​v​i​c​e​.​d​e​l​e​g​a​t​e​ ​=​ ​d​e​l​e​g​a​t​e​;​
 ​ ​ ​ ​[​n​e​t​S​e​r​v​i​c​e​ ​p​u​b​l​i​s​h​]​;​

 ​ ​ ​ ​ ​.​.​.​

Et voilà, the trick, she is done! A Bonjour-aware server. Build and run it. It must be run with at least one argument – the name of the service. You can run it with two arguments to put the server into IPv6 mode.

Browsing Net Services

When a client needs to find a service, it multicasts a message onto the network. The published servers respond. Notice, however, that with busy servers on a busy or slow network, this might take some time. So a browser is told to start a search, and then, as responses come in, the delegate is informed.

NSNetServiceBrowser has the following method:

- (void)searchForServicesOfType:(NSString *)type
                       inDomain:(NSString *)domainString

This method kicks off the search for services of the given type in the given domain. Once again, @"" can be supplied as the domain.

The delegate gets sent these messages:

- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser
           didFindService:(NSNetService *)aNetService
               moreComing:(BOOL)moreComing

This method gets called as net services are discovered. If there are several to be processed, the moreComing flag will be YES. Thus, you will know to wait before updating your user interface.

- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser
         didRemoveService:(NSNetService *)aNetService
               moreComing:(BOOL)moreComing

This method gets sent if a server disappears.

Make ChatterClient browse for servers

Open the CFChatterClient project.

First, we are going to replace the hostname text field with a combo box. Open CFChatterClientAppDelegate.h. Change the type of the _hostname pointer and add a mutable array for the discovered services, as well as a net service browser.

#​i​m​p​o​r​t​ ​<​C​o​c​o​a​/​C​o​c​o​a​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​C​F​C​h​a​t​t​e​r​C​l​i​e​n​t​A​p​p​D​e​l​e​g​a​t​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​<​N​S​A​p​p​l​i​c​a​t​i​o​n​D​e​l​e​g​a​t​e​>​ ​{​
 ​ ​ ​ ​N​S​W​i​n​d​o​w​ ​*​w​i​n​d​o​w​;​

 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​_​u​s​e​r​n​a​m​e​F​i​e​l​d​;​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​N​S​B​u​t​t​o​n​ ​ ​ ​ ​*​_​j​o​i​n​L​e​a​v​e​B​u​t​t​o​n​;​

 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​V​i​e​w​ ​ ​*​_​t​r​a​n​s​c​r​i​p​t​;​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​_​m​e​s​s​a​g​e​F​i​e​l​d​;​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​N​S​B​u​t​t​o​n​ ​ ​ ​ ​*​_​s​e​n​d​B​u​t​t​o​n​;​

 ​ ​ ​ ​C​F​S​o​c​k​e​t​N​a​t​i​v​e​H​a​n​d​l​e​ ​ ​_​s​o​c​k​f​d​;​
 ​ ​ ​ ​C​F​S​o​c​k​e​t​R​e​f​ ​_​s​o​c​k​e​t​R​e​f​;​

 ​ ​ ​ ​N​S​N​e​t​S​e​r​v​i​c​e​B​r​o​w​s​e​r​ ​*​_​b​r​o​w​s​e​r​;​
 ​ ​ ​ ​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​ ​ ​ ​ ​ ​*​_​s​e​r​v​i​c​e​s​;​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​N​S​C​o​m​b​o​B​o​x​ ​*​_​h​o​s​t​F​i​e​l​d​;​
}​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​W​i​n​d​o​w​ ​*​w​i​n​d​o​w​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​a​d​o​n​l​y​,​ ​g​e​t​t​e​r​=​i​s​C​o​n​n​e​c​t​e​d​)​ ​B​O​O​L​ ​c​o​n​n​e​c​t​e​d​;​

-​ ​(​I​B​A​c​t​i​o​n​)​ ​s​e​n​d​M​e​s​s​a​g​e​:​ ​(​i​d​)​ ​s​e​n​d​e​r​;​
-​ ​(​I​B​A​c​t​i​o​n​)​ ​j​o​i​n​:​ ​(​i​d​)​ ​s​e​n​d​e​r​;​
-​ ​(​I​B​A​c​t​i​o​n​)​ ​l​e​a​v​e​:​ ​(​i​d​)​ ​s​e​n​d​e​r​;​

@​e​n​d​ ​/​/​ ​C​F​C​h​a​t​t​e​r​C​l​i​e​n​t​A​p​p​D​e​l​e​g​a​t​e​

Save CFChatterClientAppDelegate.h and open MainMenu.xib. Remove the host name field and add a combo box in its place. Point its datasource to the app delegate, and point the app delegate to the combo box as well. Inspect the combo box and check the box that says “Uses data source.”

Update the -awakeFromNib method in CFChatterClientAppDelegate.m, create a browser, and start the search:

-​ ​(​v​o​i​d​)​ ​a​w​a​k​e​F​r​o​m​N​i​b​ ​{​
 ​ ​ ​ ​/​/​ ​P​r​e​p​o​p​u​l​a​t​e​ ​t​h​e​ ​u​s​e​r​n​a​m​e​ ​f​i​e​l​d​ ​a​s​ ​a​ ​c​o​n​v​e​n​i​e​n​c​e​.​x​
 ​ ​ ​ ​[​_​u​s​e​r​n​a​m​e​F​i​e​l​d​ ​s​e​t​S​t​r​i​n​g​V​a​l​u​e​:​ ​N​S​U​s​e​r​N​a​m​e​(​)​]​;​
 ​ ​ ​ ​[​s​e​l​f​ ​u​p​d​a​t​e​U​I​]​;​

 ​ ​ ​ ​_​b​r​o​w​s​e​r​ ​=​ ​[​[​N​S​N​e​t​S​e​r​v​i​c​e​B​r​o​w​s​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​_​s​e​r​v​i​c​e​s​ ​=​ ​[​[​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​_​b​r​o​w​s​e​r​.​d​e​l​e​g​a​t​e​ ​=​ ​s​e​l​f​;​
 ​ ​ ​ ​[​_​b​r​o​w​s​e​r​ ​s​e​a​r​c​h​F​o​r​S​e​r​v​i​c​e​s​O​f​T​y​p​e​:​ ​@​"​_​c​h​a​t​t​e​r​.​_​t​c​p​.​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​D​o​m​a​i​n​:​ ​@​"​l​o​c​a​l​.​"​]​;​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​B​e​g​u​n​ ​b​r​o​w​s​i​n​g​:​ ​%​@​"​,​ ​_​b​r​o​w​s​e​r​)​;​
}​ ​/​/​ ​a​w​a​k​e​F​r​o​m​N​i​b​

Before using a discovered net service, you will want to resolve it – that is, look up an address for it. This is another method that returns immediately but actually waits in the background for success.

If the net service has not resolved, when you ask it for its array of addresses (yes, there might be more than one), it will return an empty array.

Add these browser delegate methods to CFChatterClientAppDelegate.m:

-​ ​(​v​o​i​d​)​n​e​t​S​e​r​v​i​c​e​B​r​o​w​s​e​r​:​ ​(​N​S​N​e​t​S​e​r​v​i​c​e​B​r​o​w​s​e​r​ ​*​)​ ​a​N​e​t​S​e​r​v​i​c​e​B​r​o​w​s​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​d​F​i​n​d​S​e​r​v​i​c​e​:​ ​(​N​S​N​e​t​S​e​r​v​i​c​e​ ​*​)​ ​a​N​e​t​S​e​r​v​i​c​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​m​o​r​e​C​o​m​i​n​g​:​ ​(​B​O​O​L​)​ ​m​o​r​e​C​o​m​i​n​g​
{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​A​d​d​i​n​g​ ​n​e​w​ ​s​e​r​v​i​c​e​:​ ​%​@​"​,​ ​a​N​e​t​S​e​r​v​i​c​e​)​;​
 ​ ​ ​ ​[​_​s​e​r​v​i​c​e​s​ ​a​d​d​O​b​j​e​c​t​:​ ​a​N​e​t​S​e​r​v​i​c​e​]​;​
 ​ ​ ​ ​i​f​ ​(​!​m​o​r​e​C​o​m​i​n​g​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​_​h​o​s​t​F​i​e​l​d​ ​r​e​l​o​a​d​D​a​t​a​]​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​d​i​d​F​i​n​d​S​e​r​v​i​c​e​

-​ ​(​v​o​i​d​)​n​e​t​S​e​r​v​i​c​e​B​r​o​w​s​e​r​:​ ​(​N​S​N​e​t​S​e​r​v​i​c​e​B​r​o​w​s​e​r​ ​*​)​ ​a​N​e​t​S​e​r​v​i​c​e​B​r​o​w​s​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​d​R​e​m​o​v​e​S​e​r​v​i​c​e​:​ ​(​N​S​N​e​t​S​e​r​v​i​c​e​ ​*​)​ ​a​N​e​t​S​e​r​v​i​c​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​m​o​r​e​C​o​m​i​n​g​:​ ​(​B​O​O​L​)​ ​m​o​r​e​C​o​m​i​n​g​
{​
 ​ ​ ​ ​N​S​L​o​g​ ​(​@​"​R​e​m​o​v​i​n​g​ ​s​e​r​v​i​c​e​"​)​;​
 ​ ​ ​ ​f​o​r​ ​(​N​S​N​e​t​S​e​r​v​i​c​e​ ​*​s​e​r​v​i​c​e​ ​i​n​ ​[​_​s​e​r​v​i​c​e​s​ ​o​b​j​e​c​t​E​n​u​m​e​r​a​t​o​r​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​[​[​s​e​r​v​i​c​e​ ​n​a​m​e​]​ ​i​s​E​q​u​a​l​:​ ​[​a​N​e​t​S​e​r​v​i​c​e​ ​n​a​m​e​]​]​ ​&​&​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​[​s​e​r​v​i​c​e​ ​t​y​p​e​]​ ​i​s​E​q​u​a​l​:​ ​[​a​N​e​t​S​e​r​v​i​c​e​ ​t​y​p​e​]​]​ ​&​&​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​[​s​e​r​v​i​c​e​ ​d​o​m​a​i​n​]​ ​i​s​E​q​u​a​l​:​ ​[​a​N​e​t​S​e​r​v​i​c​e​ ​d​o​m​a​i​n​]​]​)​ ​{​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​_​s​e​r​v​i​c​e​s​ ​r​e​m​o​v​e​O​b​j​e​c​t​:​ ​s​e​r​v​i​c​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​
 ​ ​ ​ ​i​f​ ​(​!​m​o​r​e​C​o​m​i​n​g​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​_​h​o​s​t​F​i​e​l​d​ ​r​e​l​o​a​d​D​a​t​a​]​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​d​i​d​R​e​m​o​v​e​S​e​r​v​i​c​e​

Add methods for the combo box data source:

-​ ​(​i​n​t​)​ ​n​u​m​b​e​r​O​f​I​t​e​m​s​I​n​C​o​m​b​o​B​o​x​:​ ​(​N​S​C​o​m​b​o​B​o​x​ ​*​)​ ​a​C​o​m​b​o​B​o​x​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​_​s​e​r​v​i​c​e​s​ ​c​o​u​n​t​]​;​
}​ ​/​/​ ​n​u​m​b​e​r​O​f​I​t​e​m​s​I​n​C​o​m​b​o​B​o​x​

-​ ​(​i​d​)​ ​c​o​m​b​o​B​o​x​:​ ​(​N​S​C​o​m​b​o​B​o​x​ ​*​)​ ​a​C​o​m​b​o​B​o​x​ ​ ​o​b​j​e​c​t​V​a​l​u​e​F​o​r​I​t​e​m​A​t​I​n​d​e​x​:​ ​(​i​n​t​)​ ​i​n​d​e​x​ ​{​
 ​ ​ ​ ​N​S​N​e​t​S​e​r​v​i​c​e​ ​*​i​t​e​m​;​
 ​ ​ ​ ​i​t​e​m​ ​=​ ​[​_​s​e​r​v​i​c​e​s​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​i​n​d​e​x​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​i​t​e​m​ ​n​a​m​e​]​;​
}​ ​/​/​ ​o​b​j​e​c​t​V​a​l​u​e​F​o​r​I​t​e​m​A​t​I​n​d​e​x​

-​ ​(​u​n​s​i​g​n​e​d​ ​i​n​t​)​ ​c​o​m​b​o​B​o​x​:​ ​(​N​S​C​o​m​b​o​B​o​x​ ​*​)​ ​a​C​o​m​b​o​B​o​x​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​d​e​x​O​f​I​t​e​m​W​i​t​h​S​t​r​i​n​g​V​a​l​u​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​ ​s​t​r​i​n​g​ ​{​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​i​n​t​ ​m​a​x​ ​=​ ​[​_​s​e​r​v​i​c​e​s​ ​c​o​u​n​t​]​;​
 ​ ​ ​ ​f​o​r​ ​(​u​n​s​i​g​n​e​d​ ​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​m​a​x​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​N​e​t​S​e​r​v​i​c​e​ ​*​i​t​e​m​ ​=​ ​[​_​s​e​r​v​i​c​e​s​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​ ​i​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​[​s​t​r​i​n​g​ ​i​s​E​q​u​a​l​:​[​i​t​e​m​ ​n​a​m​e​]​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​i​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​i​n​d​e​x​O​f​I​t​e​m​W​i​t​h​S​t​r​i​n​g​V​a​l​u​e​

In -join:, kick off a resolution of the net service and then connect when the address is resolved.

-​ ​(​I​B​A​c​t​i​o​n​)​ ​j​o​i​n​:​ ​(​i​d​)​ ​s​e​n​d​e​r​ ​{​
 ​ ​ ​ ​i​f​ ​(​[​s​e​l​f​ ​i​s​C​o​n​n​e​c​t​e​d​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​_​m​e​s​s​a​g​e​F​i​e​l​d​ ​s​e​t​S​t​r​i​n​g​V​a​l​u​e​:​ ​@​"​u​n​s​u​b​s​c​r​i​b​e​ ​f​i​r​s​t​!​"​]​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​N​e​t​S​e​r​v​i​c​e​ ​*​c​u​r​r​e​n​t​S​e​r​v​i​c​e​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​_​s​e​r​v​i​c​e​s​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​ ​[​_​h​o​s​t​F​i​e​l​d​ ​i​n​d​e​x​O​f​S​e​l​e​c​t​e​d​I​t​e​m​]​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​u​r​r​e​n​t​S​e​r​v​i​c​e​.​d​e​l​e​g​a​t​e​ ​=​ ​s​e​l​f​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​c​u​r​r​e​n​t​S​e​r​v​i​c​e​ ​r​e​s​o​l​v​e​W​i​t​h​T​i​m​e​o​u​t​:​ ​3​0​]​;​
 ​ ​ ​ ​}​

}​ ​/​/​ ​j​o​i​n​

-​ ​(​v​o​i​d​)​ ​n​e​t​S​e​r​v​i​c​e​D​i​d​R​e​s​o​l​v​e​A​d​d​r​e​s​s​:​ ​(​N​S​N​e​t​S​e​r​v​i​c​e​ ​*​)​ ​c​u​r​r​e​n​t​S​e​r​v​i​c​e​ ​{​
 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​a​d​d​r​e​s​s​e​s​ ​=​ ​[​c​u​r​r​e​n​t​S​e​r​v​i​c​e​ ​a​d​d​r​e​s​s​e​s​]​;​

 ​ ​ ​ ​/​/​ ​T​a​k​e​ ​t​h​e​ ​f​i​r​s​t​ ​a​d​d​r​e​s​s​.​
 ​ ​ ​ ​N​S​D​a​t​a​ ​*​a​d​d​r​e​s​s​ ​=​ ​[​a​d​d​r​e​s​s​e​s​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​ ​0​]​;​

 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​u​s​e​r​n​a​m​e​ ​=​ ​[​_​u​s​e​r​n​a​m​e​F​i​e​l​d​ ​s​t​r​i​n​g​V​a​l​u​e​]​;​
 ​ ​ ​ ​[​s​e​l​f​ ​c​o​n​n​e​c​t​T​o​A​d​d​r​e​s​s​:​ ​a​d​d​r​e​s​s​ ​ ​a​s​U​s​e​r​:​ ​u​s​e​r​n​a​m​e​]​;​

 ​ ​ ​ ​[​s​e​l​f​ ​u​p​d​a​t​e​U​I​]​;​

 ​ ​ ​ ​i​f​ ​(​[​s​e​l​f​ ​i​s​C​o​n​n​e​c​t​e​d​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​h​o​s​t​n​a​m​e​ ​=​ ​@​"​T​O​O​O​"​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​c​o​n​n​e​c​t​M​e​s​s​a​g​e​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​ ​@​"​(​ ​*​ ​*​ ​*​ ​c​o​n​n​e​c​t​e​d​ ​t​o​ ​%​@​ ​a​s​ ​%​@​ ​*​ ​*​ ​*​ ​)​ ​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​o​s​t​n​a​m​e​,​ ​u​s​e​r​n​a​m​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​a​p​p​e​n​d​M​e​s​s​a​g​e​:​ ​c​o​n​n​e​c​t​M​e​s​s​a​g​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​_​m​e​s​s​a​g​e​F​i​e​l​d​ ​w​i​n​d​o​w​]​ ​m​a​k​e​F​i​r​s​t​R​e​s​p​o​n​d​e​r​:​ ​_​m​e​s​s​a​g​e​F​i​e​l​d​]​;​
 ​ ​ ​ ​}​

}​ ​/​/​ ​n​e​t​S​e​r​v​i​c​e​D​i​d​R​e​s​o​l​v​e​A​d​d​r​e​s​s​

-​ ​(​v​o​i​d​)​ ​n​e​t​S​e​r​v​i​c​e​:​ ​(​N​S​N​e​t​S​e​r​v​i​c​e​ ​*​)​ ​s​e​n​d​e​r​
 ​ ​ ​ ​ ​ ​d​i​d​N​o​t​R​e​s​o​l​v​e​:​ ​(​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​)​ ​e​r​r​o​r​D​i​c​t​ ​{​

 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​e​r​r​o​r​S​t​r​i​n​g​ ​=​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​ ​@​"​U​n​a​b​l​e​ ​t​o​ ​r​e​s​o​l​v​e​ ​%​@​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​s​e​n​d​e​r​ ​n​a​m​e​]​]​;​
 ​ ​ ​ ​[​_​m​e​s​s​a​g​e​F​i​e​l​d​ ​s​e​t​S​t​r​i​n​g​V​a​l​u​e​:​ ​e​r​r​o​r​S​t​r​i​n​g​]​;​
}​ ​/​/​ ​d​i​d​N​o​t​R​e​s​o​l​v​e​

We’re not connecting to a hostname anymore, but we have gotten an Internet address encoded as an NSData. Change -connectToHost:asUser: to -connectToData:asUser:, such that the method signature takes the NSData, and removing the unnecessary hostname check.

-​ ​(​v​o​i​d​)​ ​c​o​n​n​e​c​t​T​o​A​d​d​r​e​s​s​:​ ​(​N​S​D​a​t​a​ ​*​)​ ​a​d​d​r​e​s​s​ ​a​s​U​s​e​r​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​u​s​e​r​n​a​m​e​ ​{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​e​r​r​o​r​M​e​s​s​a​g​e​ ​=​ ​n​i​l​;​
 ​ ​ ​ ​i​n​t​ ​s​y​s​E​r​r​o​r​ ​=​ ​n​o​E​r​r​;​

 ​ ​ ​ ​i​f​ ​(​_​s​o​c​k​f​d​ ​!​=​ ​k​I​n​v​a​l​i​d​S​o​c​k​e​t​)​ ​[​s​e​l​f​ ​c​l​o​s​e​C​o​n​n​e​c​t​i​o​n​]​;​

 ​ ​ ​ ​/​/​ ​s​a​n​i​t​y​-​c​h​e​c​k​ ​o​u​r​ ​n​i​c​k​n​a​m​e​ ​b​e​f​o​r​e​ ​t​r​y​i​n​g​ ​t​o​ ​c​o​n​n​e​c​t​
 ​ ​ ​ ​i​f​ ​(​u​s​e​r​n​a​m​e​.​l​e​n​g​t​h​ ​=​=​ ​0​ ​|​|​ ​u​s​e​r​n​a​m​e​.​l​e​n​g​t​h​ ​>​ ​8​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​M​e​s​s​a​g​e​ ​=​ ​@​"​U​s​e​r​n​a​m​e​ ​m​u​s​t​ ​b​e​ ​b​e​t​w​e​e​n​ ​1​ ​a​n​d​ ​8​ ​c​h​a​r​a​c​t​e​r​s​ ​l​o​n​g​.​"​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​_​s​o​c​k​f​d​ ​=​ ​S​o​c​k​e​t​C​o​n​n​e​c​t​e​d​T​o​A​d​d​r​e​s​s​ ​(​a​d​d​r​e​s​s​)​;​
 ​ ​ ​ ​i​f​ ​(​_​s​o​c​k​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​M​e​s​s​a​g​e​ ​=​ ​@​"​C​o​u​l​d​ ​n​o​t​ ​c​o​n​n​e​c​t​.​"​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​y​s​E​r​r​o​r​ ​=​ ​e​r​r​n​o​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​U​T​F​-​8​ ​l​e​n​g​t​h​ ​c​o​u​l​d​ ​b​e​ ​g​r​e​a​t​e​r​ ​t​h​a​n​ ​t​h​e​ ​n​u​m​b​e​r​ ​o​f​ ​c​h​a​r​a​c​t​e​r​s​.​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​n​a​m​e​ ​=​ ​[​u​s​e​r​n​a​m​e​ ​U​T​F​8​S​t​r​i​n​g​]​;​
 ​ ​ ​ ​.​.​.​

And finally, add the new SocketConnectedToAddress function. It works like SocketConnectedToHostNamed, but instead of looking up a host by name, it takes the address data given to us by Bonjour. You’ll want to put a forward reference at the top of the file, and then implement the function:

s​t​a​t​i​c​ ​i​n​t​ ​S​o​c​k​e​t​C​o​n​n​e​c​t​e​d​T​o​A​d​d​r​e​s​s​ ​(​N​S​D​a​t​a​ ​*​d​a​t​a​)​ ​{​
 ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​s​t​o​r​a​g​e​ ​s​e​r​v​e​r​_​a​d​d​r​;​
 ​ ​ ​ ​[​d​a​t​a​ ​g​e​t​B​y​t​e​s​:​ ​&​s​e​r​v​e​r​_​a​d​d​r​ ​l​e​n​g​t​h​:​ ​d​a​t​a​.​l​e​n​g​t​h​]​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​a​ ​s​o​c​k​e​t​.​
 ​ ​ ​ ​i​n​t​ ​s​o​c​k​f​d​ ​=​ ​s​o​c​k​e​t​ ​(​s​e​r​v​e​r​_​a​d​d​r​.​s​s​_​f​a​m​i​l​y​,​ ​S​O​C​K​_​S​T​R​E​A​M​,​ ​0​)​;​

 ​ ​ ​ ​i​f​ ​(​s​o​c​k​f​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​ ​ ​ ​ ​ ​ ​ ​ ​s​o​c​k​e​t​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​i​n​t​ ​e​r​r​ ​=​ ​c​o​n​n​e​c​t​ ​(​s​o​c​k​f​d​,​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​ ​*​)​ ​&​s​e​r​v​e​r​_​a​d​d​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​e​r​v​e​r​_​a​d​d​r​.​s​s​_​l​e​n​)​;​
 ​ ​ ​ ​i​f​ ​(​e​r​r​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​n​e​c​t​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​l​o​s​e​ ​(​s​o​c​k​f​d​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​o​c​k​f​d​ ​=​ ​-​1​;​
 ​ ​ ​ ​}​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​o​c​k​f​d​;​

}​ ​/​/​ ​S​o​c​k​e​t​C​o​n​n​e​c​t​e​d​T​o​A​d​d​r​e​s​s​

That’s it – build and run it. If your server is running, its name should appear in the combo box. If you have several servers running, you should be able to choose among them.

For the More Curious: TXT Records

Besides the standard information for a service (host, port, name), the DNS record used to publish the services has a TXT record. This is a place where you can put any sort of data that clients might want to know about. For example, your chatter server might want to advertise the number of users already connected. It could use the TXT record to do this. NSNetService has a method that would be used on the server side:

- (BOOL) setTXTRecordData: (NSData *) data;

On the client side, you would ask the NSNetService for the data using:

- (NSData *)TXTRecordData;

Of course, the server might change the TXT record. (For example, if more people subscribe, your chatterd server would want to change the advertised number of subscribers.) On the client, you would become a delegate of the NSNetService. When the TXT record is changed, you will be sent:

- (void)netService:(NSNetService *)sender
        didUpdateTXTRecordData:(NSData *)data;

Exercises

Using the TXT record, have the chatterd server advertise the number of subscribers. On the client, display the number of subscribers for each server in the server list pop-up.

18
Multiprocessing

All modern operating systems are multiprocessing, meaning that multiple independent programs are running simultaneously and sharing the system’s resources. The OS time-slices among the runnable programs, dividing the available CPU time among runnable processes, that is, those processes that are not blocked waiting for some event such as I/O completion.

Process Scheduling

The scheduler is the part of the OS that figures out which process should get the CPU next. It often uses information such as process priority, how much CPU time a process has previously gotten, and whether it has just completed an I/O operation in making this decision.

Each process has an associated niceness that influences its priority. Niceness is specified by an integer in the range from -20 through +20. By default, processes have a niceness of 0. The higher the niceness number, the lower the process’ priority: nicer processes are less demanding about how much CPU time they get.

You can start a process with a specific niceness number using the nice command. You can change the niceness of an already running process using the renice command. You can “nice down” (“down” in terms of priority; you are in fact increasing the niceness number) your own programs, but you cannot “nice up” a program unless you have superuser privileges.

You can think of the scheduler as having a list of processes with their individual priorities, as shown in Figure 18.1. Those processes that are not blocked are put into a run queue in order of their priority. When it is time to run a new process, the scheduler pulls a process off the front of the queue, lets it run until it blocks for some reason or its time slice expires, and then sticks it back in the queue.

Figure 18.1  The scheduler

You can examine the average length of the run queue with the uptime command:

1​8​:​2​8​ ​ ​u​p​ ​3​1​ ​d​a​y​s​,​ ​2​0​:​4​0​,​ ​3​ ​u​s​e​r​s​,​ ​l​o​a​d​ ​a​v​e​r​a​g​e​s​:​ ​0​.​2​9​ ​0​.​2​9​ ​0​.​2​7​

The three load averages at the end of the output represent the number of runnable processes (the depth of the run queue) over the last 1, 5, and 15 minutes. Here, you can see that the average system load over the last minute is about half what it was over the preceding five and fifteen minutes. A general rule of thumb is that a healthy machine, CPU-wise, has a load average at or less than twice the number of processors.

Mac OS X’s load average just reports the depth of the run queue. It does not include the time blocked in disk I/O, which some Unix-like OSs (including Linux) report.

Convenience Functions

Starting new processes can be a powerful tool in your programming arsenal. Unix comes with a lot of little command-line utilities that perform useful functions. Sometimes it makes more sense to take advantage of these utilities rather than reimplementing their behavior. For example, you may have a Perl script that strips tags from HTML. If your application has a need for that kind of tag-stripping feature, it can be easier to start the Perl script and feed it your HTML rather than playing around with regular expression code in Objective-C.

The easiest way to start another program is to use the system() function. This will start a program, wait until it finishes, and then return the result code. system() actually passes control to a new shell, /bin/sh, so you can use shell features like redirection.

int system (const char *string);

The return value from system() is the result value returned from the program. Otherwise, you get -1, and errno is set if there was an error before starting the shell. A return value of 127 means the shell failed for some reason, probably due to bad syntax in the command string. system() invokes the shell with the -c argument, passing in the command line as the next argument after the -c. You can do the same from your shell if you get this error and want to experiment with your command line from the Terminal.

If you want to actually read or write to the program you start, use popen() to start the program and open a pipe to it. pclose() closes the connection:

FILE *popen (const char *command, const char *type);

int pclose (FILE *stream);

Like system(), popen() invokes a shell to run the command. Example 18.1 is a little program that popen()s the cal program to get a current calendar. Just for fun, the output from cal gets run through rev to reverse the lines, which shows that pipelines work in popen(). The program does the equivalent of a head -9 by only reading the first nine lines.

Example 18.1. pcal.m

/​/​ ​p​c​a​l​.​m​ ​-​-​ ​d​i​s​p​l​a​y​ ​a​ ​c​a​l​e​n​d​a​r​ ​u​s​i​n​g​ ​p​o​p​e​n​

/​/​ ​g​c​c​ ​-​s​t​d​=​c​9​9​ ​-​g​ ​-​W​a​l​l​ ​-​W​e​x​t​r​a​ ​-​o​ ​p​c​a​l​ ​p​c​a​l​.​m​

#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​/​/​ ​p​e​r​r​o​r​(​)​,​ ​p​o​p​e​n​(​)​,​ ​p​r​i​n​t​f​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​/​/​ ​E​X​I​T​_​S​U​C​C​E​S​S​

#​d​e​f​i​n​e​ ​B​U​F​F​E​R​_​S​I​Z​E​ ​(​4​0​9​6​)​
s​t​a​t​i​c​ ​c​o​n​s​t​ ​i​n​t​ ​k​L​i​n​e​s​T​o​P​r​i​n​t​ ​=​ ​9​;​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​

 ​ ​ ​ ​/​/​ ​R​e​v​e​r​s​e​ ​t​h​e​ ​l​i​n​e​s​ ​j​u​s​t​ ​f​o​r​ ​f​u​n​.​
 ​ ​ ​ ​F​I​L​E​ ​*​p​i​p​e​l​i​n​e​ ​=​ ​p​o​p​e​n​(​"​c​a​l​ ​2​0​1​1​ ​|​ ​r​e​v​"​,​ ​"​r​"​)​;​
 ​ ​ ​ ​i​f​ ​(​!​p​i​p​e​l​i​n​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​(​"​p​o​p​e​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​r​e​s​u​l​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​B​U​F​F​E​R​_​S​I​Z​E​]​;​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​k​L​i​n​e​s​T​o​P​r​i​n​t​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​*​l​i​n​e​ ​=​ ​f​g​e​t​s​ ​(​b​u​f​f​e​r​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​,​ ​p​i​p​e​l​i​n​e​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​l​i​n​e​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​(​"​%​s​"​,​ ​b​u​f​f​e​r​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​f​e​o​f​(​p​i​p​e​l​i​n​e​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​A​l​l​ ​d​o​n​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​r​e​a​k​;​

 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​f​e​r​r​o​r​(​p​i​p​e​l​i​n​e​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​f​g​e​t​s​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​

 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​S​h​o​u​l​d​n​'​t​ ​h​a​p​p​e​n​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​u​t​s​ ​(​"​f​g​e​t​s​ ​r​e​t​u​r​n​e​d​ ​N​U​L​L​ ​w​i​t​h​o​u​t​ ​E​O​F​ ​o​r​ ​e​r​r​o​r​\​n​"​,​ ​s​t​d​e​r​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​p​c​l​o​s​e​ ​(​p​i​p​e​l​i​n​e​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​r​e​s​u​l​t​;​
}​ ​ ​/​/​ ​m​a​i​n​

Here is a sample run of pcal:

$​ ​.​/​p​c​a​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​1​0​2​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​

 ​ ​ ​ ​ ​ ​ ​ ​h​c​r​a​M​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​y​r​a​u​r​b​e​F​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​y​r​a​u​n​a​J​ ​ ​ ​ ​ ​ ​
S​ ​ ​F​ ​ ​h​T​ ​W​ ​ ​u​T​ ​M​ ​ ​S​ ​ ​ ​S​ ​ ​F​ ​ ​h​T​ ​W​ ​ ​u​T​ ​M​ ​ ​S​ ​ ​ ​S​ ​ ​F​ ​ ​h​T​ ​W​ ​ ​u​T​ ​M​ ​ ​S​ ​
5​ ​ ​4​ ​ ​3​ ​ ​2​ ​ ​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​5​ ​ ​4​ ​ ​3​ ​ ​2​ ​ ​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​
2​1​ ​1​1​ ​0​1​ ​9​ ​ ​8​ ​ ​7​ ​ ​6​ ​ ​ ​2​1​ ​1​1​ ​0​1​ ​9​ ​ ​8​ ​ ​7​ ​ ​6​ ​ ​ ​8​ ​ ​7​ ​ ​6​ ​ ​5​ ​ ​4​ ​ ​3​ ​ ​2​ ​
9​1​ ​8​1​ ​7​1​ ​6​1​ ​5​1​ ​4​1​ ​3​1​ ​ ​9​1​ ​8​1​ ​7​1​ ​6​1​ ​5​1​ ​4​1​ ​3​1​ ​ ​5​1​ ​4​1​ ​3​1​ ​2​1​ ​1​1​ ​0​1​ ​9​ ​
6​2​ ​5​2​ ​4​2​ ​3​2​ ​2​2​ ​1​2​ ​0​2​ ​ ​6​2​ ​5​2​ ​4​2​ ​3​2​ ​2​2​ ​1​2​ ​0​2​ ​ ​2​2​ ​1​2​ ​0​2​ ​9​1​ ​8​1​ ​7​1​ ​6​1​
 ​ ​ ​ ​ ​ ​1​3​ ​0​3​ ​9​2​ ​8​2​ ​7​2​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​8​2​ ​7​2​ ​9​2​ ​8​2​ ​ ​ ​ ​ ​ ​ ​ ​7​2​ ​6​2​ ​5​2​ ​4​2​ ​3​2​

The manpage for popen() points out that it uses a bidirectional pipe, so you can specify a mode of read and write (r+) instead of just one (r) or the other (w). Unfortunately, using the returned stream both for reading and writing is unlikely to work in practice, as it requires the cooperation of the popen()ed process. Use of a bidirectional stream can easily run afoul of the buffering done under the hood by the stream I/O functions. It also raises the specter of deadlock: your process could block writing data to the child process while it blocks writing already-processed data back. Neither process will ever get a chance to drain those full buffers and unblock the other. Your best bet if you need both to read and write to the child process is to either redirect its output to a file and handle that afterwards or, if you must interleave reads and writes, create the child process yourself and use two pipes, one for reading and one for writing.

fork

To create a new process in Unix, you must first make a copy of an existing process. That copy can continue to execute the code of the original process, or it can begin executing another program. The fork() system call performs the process copying:

pid_t fork (void);

fork() makes a copy of the running process, and it is one of the few functions that can return twice. In the original process, called the parent process, fork() returns the process ID of the new process. In the new process, called the child process, fork() returns zero. When an error occurs, there is no child process, and fork() returns -1 in the parent and sets errno.

As a copy of the parent process, the child inherits the parent process’ memory, open files, real and effective user and group IDs, current working directory, signal mask, file mode creation mask (umask), environment, resource limits, and any attached shared memory segments. This copying behavior can be seen in Figure 18.2:

Figure 18.2  Fork

Copying that much data sounds time consuming, but Mac OS X takes shortcuts. Rather than duplicate the parent’s memory space immediately, it shares the original copy between both parent and child and delays copying until necessary using a technique called copy on write, also called COW (see Figure 18.3). All the physical pages that relate to the parent process are marked read-only. When a process tries to write to a page of the “duplicated” memory, the page is first copied for its private use, and each copy becomes the separate, read- and writeable property of one process. The modifying process gets to make its changes, the other process is none the wiser, and no more copying than strictly necessary occurs. This greatly reduces the amount of work the OS does on a fork().

Figure 18.3  Copy on write

The convenience functions in the previous section – system() and popen() – call fork() under the hood, but, for most purposes, they use more resources than a careful use of fork() would, particularly in their invocation of a shell to run the new processes.

Example 18.2 shows a minimal program that forks a child:

Example 18.2. fork.m

/​/​ ​f​o​r​k​.​m​ ​-​-​ ​s​h​o​w​ ​s​i​m​p​l​e​ ​u​s​e​ ​o​f​ ​f​o​r​k​(​)​

/​/​ ​g​c​c​ ​-​s​t​d​=​c​9​9​ ​-​g​ ​-​W​a​l​l​ ​-​W​e​x​t​r​a​ ​-​o​ ​f​o​r​k​ ​f​o​r​k​.​m​

#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​k​(​)​,​ ​p​i​d​_​t​,​ ​s​l​e​e​p​(​)​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​E​X​I​T​_​S​U​C​C​E​S​S​,​ ​E​X​I​T​_​F​A​I​L​U​R​E​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​p​r​i​n​t​f​(​)​

i​n​t​ ​m​a​i​n​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​f​p​u​t​s​ ​(​"​H​e​l​l​o​ ​t​h​e​r​e​!​"​,​ ​s​t​d​o​u​t​)​;​ ​ ​/​/​ ​n​o​ ​n​e​w​l​i​n​e​

 ​ ​ ​ ​p​i​d​_​t​ ​c​h​i​l​d​ ​=​ ​f​o​r​k​(​)​;​

 ​ ​ ​ ​s​w​i​t​c​h​ ​(​c​h​i​l​d​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​-​1​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​e​r​r​o​r​ ​(​"​f​o​r​k​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​x​i​t​ ​(​E​X​I​T​_​F​A​I​L​U​R​E​)​;​

 ​ ​ ​ ​ ​ ​ ​c​a​s​e​ ​0​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​n​C​h​i​l​d​:​ ​M​y​ ​p​a​r​e​n​t​ ​i​s​ ​%​l​u​.​\​n​"​,​ ​(​u​n​s​i​g​n​e​d​ ​l​o​n​g​)​g​e​t​p​p​i​d​(​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​_​e​x​i​t​ ​(​E​X​I​T​_​S​U​C​C​E​S​S​)​;​

 ​ ​ ​ ​ ​ ​ ​d​e​f​a​u​l​t​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​n​P​a​r​e​n​t​:​ ​M​y​ ​c​h​i​l​d​ ​i​s​ ​%​l​u​.​\​n​"​,​ ​(​u​n​s​i​g​n​e​d​ ​l​o​n​g​)​c​h​i​l​d​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​D​e​l​a​y​ ​a​ ​b​i​t​ ​t​o​ ​i​m​p​r​o​v​e​ ​t​h​e​ ​o​d​d​s​ ​t​h​a​t​ ​t​h​e​ ​c​h​i​l​d​ ​w​i​l​l​ ​l​o​g​ ​b​e​f​o​r​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​t​h​e​ ​p​a​r​e​n​t​ ​e​x​i​t​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​l​e​e​p​ ​(​2​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​x​i​t​ ​(​E​X​I​T​_​S​U​C​C​E​S​S​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​n​o​t​ ​r​e​a​c​h​e​d​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
}​ ​ ​/​/​ ​m​a​i​n​

and a sample run:

$​ ​.​/​f​o​r​k​
H​e​l​l​o​ ​t​h​e​r​e​!​
P​a​r​e​n​t​:​ ​M​y​ ​c​h​i​l​d​ ​i​s​ ​7​8​7​3​3​.​
H​e​l​l​o​ ​t​h​e​r​e​!​
C​h​i​l​d​:​ ​M​y​ ​p​a​r​e​n​t​ ​i​s​ ​7​8​7​3​2​.​

As with just about everything in Unix-land, there are some gotchas that can catch the unwary. The first involves race conditions between the parent and the child: you are not guaranteed which will run first. You cannot depend on some code being run after the fork() in the parent before the child gets scheduled.

The other gotcha relates to how open files are shared between the two processes. Both the parent and child share the same file table entry in the kernel, as shown in Figure 18.4. This means that all of the attributes of the open files are shared, such as the current offset. This is commonly the desired behavior – when you want both child and parent to print to the same standard out, each process will increment the offset in the file when they print, so they’ll avoid writing over each other. However, it can also be confusing when your file offsets move from underneath you and you were not expecting them to.

Related to the file table issue is the state of the buffers for buffered I/O. The buffered I/O buffers in the parent’s address space get duplicated across the fork (Figure 18.4). If there is data in the buffer before the fork, both the child and the parent could print out the buffered data twice (as you can see in Example 18.2), which is likely not what you want.

Figure 18.4  Files after fork

Notice the use of _exit() in Example 18.2. It behaves like exit(), closing file descriptors and generally cleaning up, but it does not flush the file stream buffers, so you do not have to worry about _exit() flushing a buffer for you that has duplicate data. In the example, the newlines in the subsequent printf()s flushed the buffer for us.

Notice too that the parent sleeps a bit before exiting. This improves the odds of its still being the child’s parent when the child logs its message. If the parent exits before the child writes its message, the child’s parent will be process 1. You can see this behavior by deleting the sleep() line, recompiling, and rerunning the program. As the output depends on a race condition, it might take a few runs.

Parent and Child Lifetimes

Due to the nature of fork(), every process has exactly one parent process, while a parent can have multiple child processes. Unix enforces the rule that, while any process might or might not have children, every process always has a parent process. If a child process’ parent exits before the child does, then the child process is adopted by process 1. Under most Unixes, this is the init process; under Mac OS X, it is the versatile launchd.

When a child process exits, that is not the end of it. After a process terminates, the operating system keeps some information associated with it alive, such as its exit code and system resource usage statistics. Only after this information has been delivered to the child’s parent is the process finally allowed to die. Requesting this postmortem information is called reaping the child process. You reap the process using one of the wait() system calls.

pid_t wait (int *status);

pid_t waitpid (pid_t wpid, int *status, int options);

pid_t wait3 (int *status, int options, struct rusage *rusage);

pid_t wait4 (pid_t wpid, int *status, int options, struct rusage *rusage);

Calling any one of the wait functions will collect the result code from the child. wait() will block until there is a child waiting to be reaped. If multiple child processes are waiting, the wait() will return an arbitrary one. If you want to wait for a specific child process, use waitpid(). The options that waitpid(), wait3(), and wait4() use are:

WNOHANG | Do not block waiting for a child. Return immediately if there are no exited children.
WUNTRACED | Report job-control actions on children (like being stopped or backgrounded).

wait3() is like wait(), and wait4() is like waitpid(). The one waits on an arbitrary child, while the other waits on a specific one. wait3() and wait4() also fill in a structure describing the resources consumed by the child. You can find the full struct rusage in /usr/include/sys/resource.h. The more interesting elements of this structure are:

s​t​r​u​c​t​ ​r​u​s​a​g​e​ ​{​
 ​ ​ ​ ​s​t​r​u​c​t​ ​t​i​m​e​v​a​l​ ​r​u​_​u​t​i​m​e​;​ ​ ​ ​/​*​ ​u​s​e​r​ ​t​i​m​e​ ​u​s​e​d​ ​*​/​
 ​ ​ ​ ​s​t​r​u​c​t​ ​t​i​m​e​v​a​l​ ​r​u​_​s​t​i​m​e​;​ ​ ​ ​/​*​ ​s​y​s​t​e​m​ ​t​i​m​e​ ​u​s​e​d​ ​*​/​
 ​ ​ ​ ​l​o​n​g​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​u​_​m​a​x​r​s​s​;​ ​ ​/​*​ ​m​a​x​ ​r​e​s​i​d​e​n​t​ ​s​e​t​ ​s​i​z​e​ ​*​/​
}​

These tell you how much CPU time was consumed by the child and the high water mark of its memory usage.

The return value from the child, along with several other useful bits of information, is encoded in the status result returned by all wait() functions. Use these macros to pull out the items you are interested in:

WIFEXITED() | Returns true if the process terminated normally via a call to exit() or _exit().
WEXITSTATUS() | If WIFEXITED(status) is true, returns the low-order byte of the argument the child passed to exit() or _exit(). The return value from main() is used as the argument to exit().
WIFSIGNALED() | Returns true if the process terminated due to receipt of a signal.
WTERMSIG | If WIFSIGNALED(status) is true, returns the number of the signal that caused the termination of the process.
WCOREDUMP | If WIFSIGNALED(status) is true, returns true if the termination of the process was accompanied by the creation of a core dump.
WIFSTOPPED | Returns true if the process has not terminated, but was just stopped, such as by job control in a shell (for example, Control-Z will suspend a process).
WSTOPSIG | If WIFSTOPPED(status) is true, returns the number of the signal that caused the process to stop.

Example 18.3 shows these macros in action:

Example 18.3. status.m

/​/​ ​s​t​a​t​u​s​.​m​ ​-​-​ ​p​l​a​y​ ​w​i​t​h​ ​v​a​r​i​o​u​s​ ​c​h​i​l​d​ ​e​x​i​t​i​n​g​ ​s​t​a​t​u​s​ ​v​a​l​u​e​s​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​s​t​a​t​u​s​ ​s​t​a​t​u​s​.​m​

#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​
#​i​m​p​o​r​t​ ​<​s​y​s​/​r​e​s​o​u​r​c​e​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​r​l​i​m​i​t​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​i​m​e​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​r​u​_​u​t​i​m​e​ ​a​n​d​ ​r​u​_​s​t​i​m​e​ ​i​n​ ​r​l​i​m​i​t​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​i​d​_​t​
#​i​m​p​o​r​t​ ​<​s​y​s​/​w​a​i​t​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​w​a​i​t​(​)​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​f​o​r​k​

v​o​i​d​ ​p​r​i​n​t​S​t​a​t​u​s​ ​(​i​n​t​ ​s​t​a​t​u​s​)​ ​{​
 ​ ​ ​ ​i​f​ ​(​W​I​F​E​X​I​T​E​D​(​s​t​a​t​u​s​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​p​r​o​g​r​a​m​ ​e​x​i​t​e​d​ ​n​o​r​m​a​l​l​y​.​ ​ ​R​e​t​u​r​n​ ​v​a​l​u​e​ ​i​s​ ​%​d​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​W​E​X​I​T​S​T​A​T​U​S​(​s​t​a​t​u​s​)​)​;​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​W​I​F​S​I​G​N​A​L​E​D​(​s​t​a​t​u​s​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​p​r​o​g​r​a​m​ ​e​x​i​t​e​d​ ​o​n​ ​s​i​g​n​a​l​ ​%​d​"​,​ ​W​T​E​R​M​S​I​G​(​s​t​a​t​u​s​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​W​C​O​R​E​D​U​M​P​(​s​t​a​t​u​s​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​(​c​o​r​e​ ​d​u​m​p​e​d​)​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​o​t​h​e​r​ ​e​x​i​t​ ​v​a​l​u​e​"​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​n​"​)​;​

}​ ​/​/​ ​p​r​i​n​t​S​t​a​t​u​s​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​s​t​a​t​u​s​;​

 ​ ​ ​ ​/​/​ ​n​o​r​m​a​l​ ​e​x​i​t​
 ​ ​ ​ ​i​f​ ​(​f​o​r​k​(​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​_​e​x​i​t​ ​(​2​3​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​w​a​i​t​ ​(​&​s​t​a​t​u​s​)​;​
 ​ ​ ​ ​p​r​i​n​t​S​t​a​t​u​s​ ​(​s​t​a​t​u​s​)​;​

 ​ ​ ​ ​/​/​ ​d​i​e​ ​b​y​ ​a​ ​s​i​g​n​a​l​ ​(​S​I​G​A​B​R​T​)​
 ​ ​ ​ ​i​f​ ​(​f​o​r​k​(​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​a​b​o​r​t​ ​(​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​w​a​i​t​ ​(​&​s​t​a​t​u​s​)​;​
 ​ ​ ​ ​p​r​i​n​t​S​t​a​t​u​s​ ​(​s​t​a​t​u​s​)​;​

 ​ ​ ​ ​/​/​ ​d​i​e​ ​b​y​ ​c​r​a​s​h​i​n​g​
 ​ ​ ​ ​i​f​ ​(​f​o​r​k​(​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​*​b​l​a​h​ ​=​ ​(​i​n​t​ ​*​)​0​x​F​e​e​d​F​a​c​e​;​ ​ ​/​/​ ​a​ ​b​a​d​ ​a​d​d​r​e​s​s​
 ​ ​ ​ ​ ​ ​ ​ ​*​b​l​a​h​ ​=​ ​1​2​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​w​a​i​t​ ​(​&​s​t​a​t​u​s​)​;​
 ​ ​ ​ ​p​r​i​n​t​S​t​a​t​u​s​ ​(​s​t​a​t​u​s​)​;​

 ​ ​ ​ ​/​/​ ​d​r​o​p​ ​c​o​r​e​
 ​ ​ ​ ​i​f​ ​(​f​o​r​k​(​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​r​l​i​m​i​t​ ​r​l​;​

 ​ ​ ​ ​ ​ ​ ​ ​r​l​.​r​l​i​m​_​c​u​r​ ​=​ ​R​L​I​M​_​I​N​F​I​N​I​T​Y​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​l​.​r​l​i​m​_​m​a​x​ ​=​ ​R​L​I​M​_​I​N​F​I​N​I​T​Y​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​s​e​t​r​l​i​m​i​t​ ​(​R​L​I​M​I​T​_​C​O​R​E​,​ ​&​r​l​)​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​ ​i​n​ ​s​e​t​r​l​i​m​i​t​ ​f​o​r​ ​R​L​I​M​I​T​_​_​C​O​R​:​ ​%​d​ ​(​%​s​)​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​a​b​o​r​t​ ​(​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​w​a​i​t​ ​(​&​s​t​a​t​u​s​)​;​
 ​ ​ ​ ​p​r​i​n​t​S​t​a​t​u​s​ ​(​s​t​a​t​u​s​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

A sample run looks like this:

$​ ​.​/​s​t​a​t​u​s​
p​r​o​g​r​a​m​ ​e​x​i​t​e​d​ ​n​o​r​m​a​l​l​y​.​ ​ ​R​e​t​u​r​n​ ​v​a​l​u​e​ ​i​s​ ​2​3​
p​r​o​g​r​a​m​ ​e​x​i​t​e​d​ ​o​n​ ​s​i​g​n​a​l​ ​6​
p​r​o​g​r​a​m​ ​e​x​i​t​e​d​ ​o​n​ ​s​i​g​n​a​l​ ​1​1​
p​r​o​g​r​a​m​ ​e​x​i​t​e​d​ ​o​n​ ​s​i​g​n​a​l​ ​6​ ​(​c​o​r​e​ ​d​u​m​p​e​d​)​

In between the child process’ _exit() and the parent’s wait(), the kernel needs to store the child’s status and resource information somewhere. It disposes of most of the child’s resources (memory, files, etc.) immediately but preserves its process table entry, which includes the information provided to the parent by wait(). The process is dead, but its process table entry lives on: such an undead child process is known as a zombie, not to be confused with the Objective-C NSZombieEnabled memory debugging feature. Zombies show up in parentheses in the output of the ps command:

$​ ​p​s​
 ​ ​P​I​D​ ​T​T​Y​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​T​I​M​E​ ​C​M​D​
 ​ ​2​3​7​ ​t​t​y​s​0​0​0​ ​ ​ ​ ​0​:​0​3​.​6​0​ ​-​b​a​s​h​
7​9​8​5​5​ ​t​t​y​s​0​0​0​ ​ ​ ​ ​0​:​0​0​.​0​0​ ​.​/​s​p​a​w​n​_​z​o​m​b​i​e​
7​9​8​5​6​ ​t​t​y​s​0​0​0​ ​ ​ ​ ​0​:​0​0​.​0​0​ ​(​z​o​m​b​i​e​)​

The occasional zombie is nothing to worry about. When its parent exits, it will be reparented to process 1, which will reap it. Only en masse do zombies become dangerous: if a program continually creates zombies and does not exit, it can fill up the process table and render the machine useless. More likely is that it runs up against the per-user process limit, rendering the account of the process’ user id useless but not affecting other users.

How do you know when to call wait()? When a child exits, the system sends a SIGCHLD signal to the parent. This signal is ignored by default, but you can set a handler and use it to set a flag indicating that a child process needs waiting on. Note that the parent cannot count on receiving one SIGCHLD for each child process that exits, as several might exit while the parent is waiting in the kernel’s run queue.

You will likely want to continue to ignore SIGCHILD and instead wait() periodically, such as during idle processing.

Alternatively, you can use a kqueue (see Chapter 16: kqueue and FSEvents) to monitor for process termination and SIGCHLD signals.

You can see the parent process ID by passing the ppid keyword to the -o option of ps (more stuff added to make the output more useful):

$​ ​p​s​ ​-​a​x​o​ ​u​s​e​r​,​p​i​d​,​p​p​i​d​,​v​s​z​,​t​t​,​s​t​a​t​e​,​s​t​a​r​t​,​t​i​m​e​,​c​o​m​m​a​n​d​
U​S​E​R​ ​ ​ ​ ​P​I​D​ ​ ​P​P​I​D​ ​ ​ ​ ​V​S​Z​ ​ ​T​T​ ​ ​S​T​A​T​ ​S​T​A​R​T​E​D​ ​ ​ ​ ​ ​T​I​M​E​ ​C​O​M​M​A​N​D​
r​o​o​t​ ​ ​ ​ ​ ​ ​1​ ​ ​ ​ ​ ​0​ ​ ​ ​1​3​0​8​ ​ ​?​?​ ​ ​S​s​ ​ ​ ​2​0​S​e​p​0​2​ ​ ​0​:​0​0​.​0​0​ ​/​s​b​i​n​/​l​a​u​n​c​h​d​ ​
r​o​o​t​ ​ ​ ​ ​ ​5​1​ ​ ​ ​ ​ ​1​ ​ ​1​5​9​1​2​ ​ ​?​?​ ​ ​S​s​ ​ ​ ​2​0​S​e​p​0​2​ ​ ​0​:​0​2​.​4​5​ ​k​e​x​t​d​
r​o​o​t​ ​ ​ ​ ​ ​7​3​ ​ ​ ​ ​ ​1​ ​ ​ ​1​2​9​2​ ​ ​?​?​ ​ ​S​s​ ​ ​ ​2​0​S​e​p​0​2​ ​ ​0​:​2​0​.​9​8​ ​u​p​d​a​t​e​
.​.​.​
r​o​o​t​ ​ ​ ​ ​4​2​9​ ​ ​ ​3​9​7​ ​ ​1​4​0​4​8​ ​ ​p​2​ ​ ​S​s​ ​ ​ ​2​1​S​e​p​0​2​ ​ ​0​:​0​0​.​8​4​ ​l​o​g​i​n​ ​-​p​f​ ​m​a​r​k​d​
m​a​r​k​d​ ​ ​ ​4​3​0​ ​ ​ ​4​2​9​ ​ ​ ​5​8​7​2​ ​ ​p​2​ ​ ​S​ ​ ​ ​ ​2​1​S​e​p​0​2​ ​ ​0​:​0​0​.​0​5​ ​-​t​c​s​h​ ​(​t​c​s​h​)​
m​a​r​k​d​ ​ ​ ​4​3​1​ ​ ​ ​4​3​0​ ​ ​1​5​8​4​0​ ​ ​p​2​ ​ ​S​+​ ​ ​ ​2​1​S​e​p​0​2​ ​ ​2​:​3​4​.​6​3​ ​e​m​a​c​s​
m​a​r​k​d​ ​ ​ ​4​3​2​ ​ ​ ​4​3​1​ ​ ​ ​9​9​5​2​ ​s​t​d​ ​ ​S​s​ ​ ​ ​2​1​S​e​p​0​2​ ​ ​0​:​0​1​.​1​0​ ​-​b​i​n​/​t​c​s​h​ ​-​i​ ​(​t​c​s​h​)​
r​o​o​t​ ​ ​ ​2​8​9​4​ ​ ​ ​4​3​2​ ​ ​ ​5​1​9​2​ ​s​t​d​ ​ ​R​+​ ​ ​ ​1​1​:​1​8​A​M​ ​ ​0​:​0​0​.​0​0​ ​p​s​ ​-​a​x​o​ ​u​s​e​r​ ​p​i​d​ ​p​p​
.​.​.​

Here you can see some daemon processes with /sbin/launchd as the parent. There is also the login process (the parent pid 397 is Terminal.app), as well as some other programs like the shell and emacs.

exec

Most often after a fork, you just want to run some other program. The exec() family of functions replaces the current running process with a new one. You will typically hear of fork() and exec() spoken of together, since they are rarely used apart from each other.

There are several variants of exec() depending on how you specify the file to run, how you specify the program arguments, and how you specify the environment variables for the new program.

Table 18.1. exec() Variants

| Finding the Executable | Program Arguments | Environment
execl | given path | NULL-terminated argument list | environment inherited
execlp | PATH search | NULL-terminated argument list | environment inherited
execle | given path | list of arguments | explicit environment (NULL-terminated string array)
execv | given path | NULL-terminated string array | environment inherited
execvp | PATH search | NULL-terminated array of strings | environment inherited
execvP | search using specified path | NULL-terminated array of strings | environment inherited
execve | given path | NULL-terminated string array | explicit environment (NULL-terminated string array)

How to decipher the names:

p | If the given file name contains a slash, it is treated as a path to use. Otherwise, the call uses the PATH environment variable to do shell-style program lookup.
P | As p, but the provided path string, such as "/sbin:/bin:/usr/sbin:/usr/bin", is used instead of the PATH environment variable.
v | Program arguments are an array (“vector”) of strings.
l | Program arguments are a list of separate (varargs) arguments in the exec() command.
e | Environment variables are an array of strings of the form "VARIABLE=value".
No e | Environment variables are inherited using the environ variable.

If you do not use an e version of exec(), the global variable environ will be used to construct the environment for the new process. The execve() function is the actual system call that all the other functions are based on.

You are expected to pass the executable name or path as the first argument. The argument list, argument vector, and environment vector must be terminated by a NULL pointer:

c​h​a​r​ ​*​e​n​v​p​[​]​ ​=​ ​{​"​P​A​T​H​=​/​u​s​r​/​b​i​n​"​,​ ​"​E​D​I​T​O​R​=​/​u​s​r​/​b​i​n​/​v​i​m​"​,​ ​N​U​L​L​}​;​
c​h​a​r​ ​*​a​r​g​v​[​]​ ​=​ ​{​"​/​u​s​r​/​b​i​n​/​t​r​u​e​"​,​ ​N​U​L​L​}​;​

A number of attributes are inherited across the exec() call, including:

open files

process ID, parent process ID, process group ID

access groups, controlling terminal, resource usages

current working directory

umask, signal mask

Pipes

Open files are inherited across an exec() unless you explicitly tell the file descriptors to “close on exec.” This behavior forms the basis of building pipelines between programs. A process calls pipe() to create a communications channel before fork()ing:

int pipe (int fildes[2]);

pipe() fills the filedes array with two file descriptors that are connected in such a way that data written to filedes[1] can be read from filedes[0]. If you wanted to fork() and exec() a command and read that command’s output, you would do something like this, as illustrated in Figure 18.5:

Create the pipe

fork()

The child uses dup2() to move filedes[1] to standard out.

The child exec()s a program.

The parent reads the program’s output from filedes[0]. After the child exits, its file descriptors are closed, and reads from filedes[0] will return end of file once any buffered data is drained from the pipe.

Finally, the parent wait()s on the child.

Figure 18.5  Pipe and fork

You can chain the input and output of multiple programs together using multiple pipes.

Example 18.4 is a program that builds a pipeline equivalent to

$​ ​g​r​e​p​ ​-​i​ ​m​a​i​l​ ​/​u​s​r​/​s​h​a​r​e​/​d​i​c​t​/​w​o​r​d​s​ ​|​ ​t​r​ ​'​[​:​l​o​w​e​r​:​]​'​ ​'​[​:​u​p​p​e​r​:​]​'​

That is, get words from /usr/share/dict/words that contain “mail” and translate them to uppercase.

Example 18.4. pipeline.m

/​/​ ​p​i​p​e​l​i​n​e​.​m​ ​-​-​ ​m​a​n​u​a​l​l​y​ ​c​r​e​a​t​e​ ​a​ ​p​i​p​e​l​i​n​e​ ​t​o​ ​r​u​n​ ​t​h​e​ ​c​o​m​m​a​n​d​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​r​e​p​ ​-​i​ ​m​a​i​l​ ​/​u​s​r​/​s​h​a​r​e​/​d​i​c​t​/​w​o​r​d​s​ ​|​ ​t​r​ ​'​[​:​l​o​w​e​r​:​]​'​ ​'​[​:​u​p​p​e​r​:​]​'​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​o​ ​p​i​p​e​l​i​n​e​ ​p​i​p​e​l​i​n​e​.​m​

#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​ ​/​/​ ​f​o​r​ ​p​i​d​_​t​
#​i​m​p​o​r​t​ ​<​s​y​s​/​w​a​i​t​.​h​>​ ​ ​ ​ ​/​/​ ​f​o​r​ ​w​a​i​t​p​i​d​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​f​o​r​k​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​,​ ​p​i​p​e​,​ ​e​x​e​c​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​

#​d​e​f​i​n​e​ ​B​U​F​S​I​Z​E​ ​4​0​9​6​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​s​t​a​t​u​s​ ​=​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​

 ​ ​ ​ ​i​n​t​ ​p​i​p​e​l​i​n​e​1​[​2​]​;​ ​ ​ ​ ​/​/​ ​w​r​i​t​e​ ​o​n​ ​1​,​ ​r​e​a​d​ ​o​n​ ​z​e​r​o​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​p​i​p​e​ ​(​p​i​p​e​l​i​n​e​1​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​o​p​e​n​ ​p​i​p​e​ ​1​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​s​t​a​r​t​ ​t​h​e​ ​g​r​e​p​

 ​ ​ ​ ​p​i​d​_​t​ ​g​r​e​p​_​p​i​d​ ​=​ ​0​;​
 ​ ​ ​ ​i​f​ ​(​(​g​r​e​p​_​p​i​d​ ​=​ ​f​o​r​k​(​)​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​p​a​r​e​n​t​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​(​g​r​e​p​_​p​i​d​ ​=​=​ ​-​1​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​f​o​r​k​ ​f​a​i​l​e​d​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​c​l​o​s​e​ ​(​p​i​p​e​l​i​n​e​1​[​1​]​)​;​ ​/​/​ ​w​e​'​r​e​ ​n​o​t​ ​p​l​a​n​n​i​n​g​ ​o​n​ ​w​r​i​t​i​n​g​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​c​h​i​l​d​

 ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​*​a​r​g​u​m​e​n​t​s​[​]​ ​=​ ​{​ ​"​g​r​e​p​"​,​ ​"​-​i​"​,​ ​"​m​a​i​l​"​,​ ​"​/​u​s​r​/​s​h​a​r​e​/​d​i​c​t​/​w​o​r​d​s​"​,​ ​N​U​L​L​ ​}​;​

 ​ ​ ​ ​ ​ ​ ​ ​c​l​o​s​e​ ​(​p​i​p​e​l​i​n​e​1​[​0​]​)​;​ ​/​/​ ​w​e​'​r​e​ ​n​o​t​ ​p​l​a​n​n​i​n​g​ ​o​n​ ​r​e​a​d​i​n​g​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​s​e​t​ ​t​h​e​ ​s​t​a​n​d​a​r​d​ ​o​u​t​ ​t​o​ ​b​e​ ​t​h​e​ ​w​r​i​t​e​-​s​i​d​e​ ​o​f​ ​t​h​e​ ​p​i​p​e​l​i​n​e​1​

 ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​d​u​p​2​ ​(​p​i​p​e​l​i​n​e​1​[​1​]​,​ ​S​T​D​O​U​T​_​F​I​L​E​N​O​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​d​u​p​2​ ​f​a​i​l​e​d​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​T​h​e​ ​f​i​l​e​ ​i​s​ ​o​p​e​n​ ​o​n​ ​S​T​D​O​U​T​_​F​I​L​E​N​O​,​ ​s​o​ ​w​e​ ​d​o​n​'​t​ ​n​e​e​d​ ​t​h​i​s​ ​f​d​ ​a​n​y​m​o​r​e​.​
 ​ ​ ​ ​ ​ ​ ​ ​c​l​o​s​e​ ​(​p​i​p​e​l​i​n​e​1​[​1​]​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​e​x​e​c​ ​t​h​e​ ​c​h​i​l​d​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​e​x​e​c​v​p​ ​(​"​g​r​e​p​"​,​ ​a​r​g​u​m​e​n​t​s​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​e​x​e​c​ ​g​r​e​p​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​s​t​a​r​t​ ​t​h​e​ ​t​r​

 ​ ​ ​ ​i​n​t​ ​p​i​p​e​l​i​n​e​2​[​2​]​;​
 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​p​i​p​e​ ​(​p​i​p​e​l​i​n​e​2​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​o​p​e​n​ ​p​i​p​e​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​p​i​d​_​t​ ​t​r​_​p​i​d​ ​=​ ​0​;​
 ​ ​ ​ ​i​f​ ​(​(​t​r​_​p​i​d​ ​=​ ​f​o​r​k​(​)​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​p​a​r​e​n​t​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​(​t​r​_​p​i​d​ ​=​=​ ​-​1​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​f​o​r​k​ ​f​a​i​l​e​d​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​c​l​o​s​e​ ​(​p​i​p​e​l​i​n​e​2​[​1​]​)​;​ ​/​/​ ​w​e​'​r​e​ ​n​o​t​ ​p​l​a​n​n​i​n​g​ ​o​n​ ​w​r​i​t​i​n​g​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​c​h​i​l​d​

 ​ ​ ​ ​ ​ ​ ​ ​c​l​o​s​e​ ​(​p​i​p​e​l​i​n​e​2​[​0​]​)​;​ ​/​/​ ​w​e​'​r​e​ ​n​o​t​ ​p​l​a​n​n​i​n​g​ ​o​n​ ​r​e​a​d​i​n​g​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​s​e​t​ ​t​h​e​ ​s​t​a​n​d​a​r​d​ ​o​u​t​ ​t​o​ ​b​e​ ​t​h​e​ ​w​r​i​t​e​-​s​i​d​e​ ​o​f​ ​t​h​e​ ​p​i​p​e​l​i​n​e​2​

 ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​d​u​p​2​ ​(​p​i​p​e​l​i​n​e​1​[​0​]​,​ ​S​T​D​I​N​_​F​I​L​E​N​O​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​d​u​p​2​ ​f​a​i​l​e​d​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​c​l​o​s​e​ ​(​p​i​p​e​l​i​n​e​1​[​1​]​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​d​u​p​2​ ​(​p​i​p​e​l​i​n​e​2​[​1​]​,​ ​S​T​D​O​U​T​_​F​I​L​E​N​O​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​d​u​p​2​ ​f​a​i​l​e​d​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​c​l​o​s​e​ ​(​p​i​p​e​l​i​n​e​2​[​1​]​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​e​x​e​c​ ​t​h​e​ ​c​h​i​l​d​

 ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​e​x​e​c​l​p​ ​(​"​t​r​"​,​ ​"​t​r​"​,​ ​"​[​:​l​o​w​e​r​:​]​"​,​ ​"​[​:​u​p​p​e​r​:​]​"​,​ ​N​U​L​L​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​e​x​e​c​ ​t​r​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​t​h​i​s​ ​i​s​ ​o​n​l​y​ ​i​n​ ​t​h​e​ ​p​a​r​e​n​t​.​ ​ ​r​e​a​d​ ​t​h​e​ ​r​e​s​u​l​t​s​
 ​ ​ ​ ​F​I​L​E​ ​*​b​l​a​r​g​;​
 ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​B​U​F​S​I​Z​E​]​;​

 ​ ​ ​ ​b​l​a​r​g​ ​=​ ​f​d​o​p​e​n​ ​(​p​i​p​e​l​i​n​e​2​[​0​]​,​ ​"​r​"​)​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​f​g​e​t​s​(​b​u​f​f​e​r​,​ ​B​U​F​S​I​Z​E​,​ ​b​l​a​r​g​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​s​"​,​ ​b​u​f​f​e​r​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​a​n​d​ ​w​a​i​t​
 ​ ​ ​ ​i​n​t​ ​c​h​i​l​d​S​t​a​t​u​s​;​
 ​ ​ ​ ​w​a​i​t​p​i​d​ ​(​g​r​e​p​_​p​i​d​,​ ​&​c​h​i​l​d​S​t​a​t​u​s​,​ ​0​)​;​
 ​ ​ ​ ​w​a​i​t​p​i​d​ ​(​t​r​_​p​i​d​,​ ​&​c​h​i​l​d​S​t​a​t​u​s​,​ ​0​)​;​

 ​ ​ ​ ​/​/​ ​w​h​e​w​!​ ​ ​A​l​l​ ​d​o​n​e​.​

 ​ ​ ​ ​s​t​a​t​u​s​ ​=​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

b​a​i​l​o​u​t​:​

 ​ ​ ​ ​r​e​t​u​r​n​ ​(​s​t​a​t​u​s​)​;​

}​ ​/​/​ ​m​a​i​n​

A sample run:

$​ ​.​/​p​i​p​e​l​i​n​e​
A​I​R​M​A​I​L​
A​U​M​A​I​L​
B​E​M​A​I​L​
B​L​A​C​K​M​A​I​L​
B​L​A​C​K​M​A​I​L​E​R​
C​A​M​A​I​L​
.​.​.​
U​N​M​A​I​L​
U​N​M​A​I​L​A​B​L​E​
U​N​M​A​I​L​A​B​L​E​N​E​S​S​
U​N​M​A​I​L​E​D​

fork() Gotchas

While Unix file descriptors are inherited across fork(), most Mach ports are not. Cocoa relies on Mach ports for much of its private interprocess communication, including communication with the window server. Expect problems if you try to use Cocoa after a fork().

When you are using threads (which Cocoa and Core Foundation programs use implicitly), only the thread that calls fork() is running in the child. Unfortunately, all of the other thread stuff (mutexes and other data structures) still exists, and data structures that are protected by mutexes are potentially still in an indeterminate state, which can lead to total mayhem.

If you are using threads (or if a framework or library is using them on your behalf), the only safe functions to call after a fork() are the exec() functions and any of the async-signal-safe functions (the ones you can call in a signal handler). Under Mac OS X 10.6 and later, if you are using run loops, they are spinning up the Grand Central Dispatch workqueue threads on your behalf. The Foundation URL loading system uses threads, as do certain services in Core Foundation. Many other Apple frameworks use threads under the hood, as well.

If you attempt to use various Core Foundation functions from the child before exec()ing, the framework will scold you roundly via the aptly named function __THE_PROCESS_HAS_FORKED_AND_ YOU_CANNOT_USE_THIS_ COREFOUNDATION_FUNCTIONALITY ___YOU_MUST_EXEC__. When you fork, it will also helpfully update the Crash Reporter information to include whether it was a single- or multi-threaded process that forked.

In short: if you are using multiprocessing as an alternative to multithreading by fork()ing to continue your current program, you should look into using multithreading or some other, more modern approach to concurrency. And, unless you are writing such a single-threaded, BSD-style program and not touching any Apple frameworks or Mach functions, you should only fork() in order to exec().

Summary

Mac OS X is a multiprocessing operating system. Independent processes are given slices of CPU time, giving the illusion that all of the programs on the system are running concurrently.

Sometimes your programs need to create new processes to do their work. You can use one of the convenience functions like system() or popen() to run pipelines in a shell. You can also use fork() and exec() directly to create a new child process and execute a new program in that processes. You can use pipe() to establish a communications channel between related processes.

Exercises

In pipeline.m, if the parent does not close tr_pipe[WRITE], the program will hang in the loop that reads results from the children. Why does it behave like that?

19
Using NSTask

In this section, you will learn:

how to create new processes using NSTask,

how to send data to the new process' standard in and read data from its standard out and standard error using NSPipe and NSFileHandle, and

how NSProcessInfo supplies the program with information about itself.

NSProcessInfo

Your application can access its own process information using the NSProcessInfo object. Here are some of the commonly used methods on NSProcessInfo:

+​ ​(​N​S​P​r​o​c​e​s​s​I​n​f​o​ ​*​)​p​r​o​c​e​s​s​I​n​f​o​

You will use this class method to get hold of the shared instance of NSProcessInfo for the current process.

-​ ​(​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​)​e​n​v​i​r​o​n​m​e​n​t​

Returns a dictionary containing all the environment variables as keys and their values.

-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​h​o​s​t​N​a​m​e​

The name of the computer upon which the program is running.

-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​p​r​o​c​e​s​s​N​a​m​e​

The name of the program. This is used by the user defaults system.

-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​g​l​o​b​a​l​l​y​U​n​i​q​u​e​S​t​r​i​n​g​

This method uses the host name, process ID, and a timestamp to create a string that will be unique for the network. It uses a counter to ensure that each time this method is invoked it will create a different string.

NSTask

The NSTask object is used to create and control new processes. When the process ends, the object will post an NSTaskDidTerminateNotification notification. Before creating (or launching) the new process, you will set the attributes of the new process with these methods:

-​ ​(​v​o​i​d​)​s​e​t​L​a​u​n​c​h​P​a​t​h​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​p​a​t​h​

Sets the path to the code that will be executed when the process is created.

-​ ​(​v​o​i​d​)​s​e​t​A​r​g​u​m​e​n​t​s​:​ ​(​N​S​A​r​r​a​y​ ​*​)​a​r​g​u​m​e​n​t​s​

Takes an array of strings that will be the arguments to the program.

-​ ​(​v​o​i​d​)​s​e​t​E​n​v​i​r​o​n​m​e​n​t​:​ ​(​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​)​d​i​c​t​

You can use this to set the environment variables. If unset, the environment variables of the parent process will be used.

-​ ​(​v​o​i​d​)​s​e​t​C​u​r​r​e​n​t​D​i​r​e​c​t​o​r​y​P​a​t​h​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​p​a​t​h​

Every process has a directory from which all relative paths are resolved. This is known as the current directory. If unset, the current directory of the parent process is used.

-​ ​(​v​o​i​d​)​s​e​t​S​t​a​n​d​a​r​d​I​n​p​u​t​:​ ​(​i​d​)​i​n​p​u​t​

You can provide an object (either an NSPipe or an NSFileHandle) to act as a conduit to the new process’ standard input.

-​ ​(​v​o​i​d​)​s​e​t​S​t​a​n​d​a​r​d​O​u​t​p​u​t​:​ ​(​i​d​)​o​u​t​p​u​t​

You can provide an object (either an NSPipe or an NSFileHandle) to act as a conduit from the new process’ standard output.

-​ ​(​v​o​i​d​)​s​e​t​S​t​a​n​d​a​r​d​E​r​r​o​r​:​ ​(​i​d​)​e​r​r​o​r​

You can provide an object (either an NSPipe or an NSFileHandle) to act as a conduit from the new process’ standard error.

There are also methods you’ll use once the new process is running. Here are the most commonly used:

- (void)launch

Creates the new process.

- (void)terminate

Kills the new process by sending it a SIGTERM signal.

- (int)processIdentifier

Returns the new process’ process ID.

- (BOOL)isRunning

Returns YES if the new process is running.

NSFileHandle

When reading a file, Cocoa programmers often read in an entire file and pack it into an NSData or NSString before parsing it. When writing a file, Cocoa programmers usually create a complete NSData or NSString which is then written to the file system. Sometimes you want more control over reading from and writing to files. For example, you might read a file just until you find what you want and then close it. For more control over reading and writing from files, you use an NSFileHandle object.

An NSFileHandle is used for reading and writing files. Some of the reading methods are blocking (that is, the application stops and waits for the data to become available), and others are non-blocking. We’ll discuss the non-blocking methods later in the chapter. Here are some commonly used methods for reading, writing, and seeking:

- (NSData *) readDataToEndOfFile
- (NSData *) readDataOfLength: (unsigned int) length

These methods read data from the file handle.

- (void) writeData: (NSData *) data;

is a method for writing data to a file handle.

- (unsigned long long) offsetInFile
- (void) seekToFileOffset: (unsigned long long) offset

are methods for finding and changing your current location in a file.

- (void)closeFile

closes the file.

NSPipe

The class NSPipe has two instances of NSFileHandle – one for input and the other for output.

- (NSFileHandle *) fileHandleForReading

- (NSFileHandle *) fileHandleForWriting

Creating an App that Creates a New Process

Unix systems have a program called sort that reads data from standard input, sorts it, and outputs it to standard output. You are going to write a program that invokes sort as a new process, writes data to its input, and reads data from its output. The user will type in an NSTextView, click a button to trigger the sort, and read the result in another NSTextView. It will look like Figure 19.1:

Figure 19.1  SortThem application running

For the record, this is not how we would do a sort in a real application. The NSArray class has a couple of elegant ways to do sorting. This is just a simple example of using other processes.

Figure 19.2 is an object diagram of the nib file:

Figure 19.2  SortThem nib file

style="text-align: center">SortThem nib file

Create a new project of type Cocoa Application and name it SortThem.

Edit SortThemAppDelegate.h and set up the outlets and action:

Example 19.1. SortThemAppDelegate.h

#​i​m​p​o​r​t​ ​<​C​o​c​o​a​/​C​o​c​o​a​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​S​o​r​t​T​h​e​m​A​p​p​D​e​l​e​g​a​t​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​<​N​S​A​p​p​l​i​c​a​t​i​o​n​D​e​l​e​g​a​t​e​>​ ​{​
 ​ ​ ​ ​N​S​W​i​n​d​o​w​ ​*​_​w​i​n​d​o​w​;​
 ​ ​ ​ ​N​S​T​e​x​t​V​i​e​w​ ​*​_​i​n​T​e​x​t​;​
 ​ ​ ​ ​N​S​T​e​x​t​V​i​e​w​ ​*​_​o​u​t​T​e​x​t​;​
}​

@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​W​i​n​d​o​w​ ​*​w​i​n​d​o​w​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​V​i​e​w​ ​*​i​n​T​e​x​t​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​V​i​e​w​ ​*​o​u​t​T​e​x​t​;​

-​ ​(​I​B​A​c​t​i​o​n​)​ ​s​o​r​t​:​ ​(​i​d​)​ ​s​e​n​d​e​r​;​

@​e​n​d​ ​/​/​ ​S​o​r​t​T​h​e​m​A​p​p​D​e​l​e​g​a​t​e​

Edit MainMenu.xib. Drop two NSTextView objects and an NSButton on the window. Make the text view on the right non-editable:

Figure 19.3  Laying out the SortThem window

Before creating the code, take a look at the object diagram shown in Figure 19.4.

Figure 19.4  SortThem object diagram

In Xcode, add a -sort: method in SortThemAppDelegate.m:

Example 19.2. SortThemAppDelegate.m

#​i​m​p​o​r​t​ ​"​S​o​r​t​T​h​e​m​A​p​p​D​e​l​e​g​a​t​e​.​h​"​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​S​o​r​t​T​h​e​m​A​p​p​D​e​l​e​g​a​t​e​
@​s​y​n​t​h​e​s​i​z​e​ ​w​i​n​d​o​w​ ​=​ ​_​w​i​n​d​o​w​;​
@​s​y​n​t​h​e​s​i​z​e​ ​i​n​T​e​x​t​ ​=​ ​_​i​n​T​e​x​t​;​
@​s​y​n​t​h​e​s​i​z​e​ ​o​u​t​T​e​x​t​ ​=​ ​_​o​u​t​T​e​x​t​;​

-​ ​(​I​B​A​c​t​i​o​n​)​ ​s​o​r​t​:​ ​(​i​d​)​ ​s​e​n​d​e​r​ ​{​
 ​ ​ ​ ​N​S​T​a​s​k​ ​*​t​a​s​k​ ​=​ ​[​[​N​S​T​a​s​k​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​N​S​P​i​p​e​ ​*​i​n​P​i​p​e​ ​=​ ​[​[​N​S​P​i​p​e​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​N​S​P​i​p​e​ ​*​o​u​t​P​i​p​e​ ​=​ ​[​[​N​S​P​i​p​e​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​[​t​a​s​k​ ​s​e​t​L​a​u​n​c​h​P​a​t​h​:​ ​@​"​/​u​s​r​/​b​i​n​/​s​o​r​t​"​]​;​
 ​ ​ ​ ​[​t​a​s​k​ ​s​e​t​S​t​a​n​d​a​r​d​O​u​t​p​u​t​:​ ​o​u​t​P​i​p​e​]​;​
 ​ ​ ​ ​[​t​a​s​k​ ​s​e​t​S​t​a​n​d​a​r​d​I​n​p​u​t​:​ ​i​n​P​i​p​e​]​;​
 ​ ​ ​ ​[​t​a​s​k​ ​s​e​t​A​r​g​u​m​e​n​t​s​:​ ​[​N​S​A​r​r​a​y​ ​a​r​r​a​y​W​i​t​h​O​b​j​e​c​t​:​ ​@​"​-​f​"​]​]​;​

 ​ ​ ​ ​[​t​a​s​k​ ​l​a​u​n​c​h​]​;​

 ​ ​ ​ ​N​S​F​i​l​e​H​a​n​d​l​e​ ​*​w​r​i​t​i​n​g​H​a​n​d​l​e​ ​=​ ​[​i​n​P​i​p​e​ ​f​i​l​e​H​a​n​d​l​e​F​o​r​W​r​i​t​i​n​g​]​;​
 ​ ​ ​ ​N​S​D​a​t​a​ ​*​o​u​t​D​a​t​a​ ​=​ ​[​[​s​e​l​f​.​i​n​T​e​x​t​ ​s​t​r​i​n​g​]​ ​d​a​t​a​U​s​i​n​g​E​n​c​o​d​i​n​g​:​ ​N​S​U​T​F​8​S​t​r​i​n​g​E​n​c​o​d​i​n​g​]​;​
 ​ ​ ​ ​[​w​r​i​t​i​n​g​H​a​n​d​l​e​ ​w​r​i​t​e​D​a​t​a​:​ ​o​u​t​D​a​t​a​]​;​
 ​ ​ ​ ​[​w​r​i​t​i​n​g​H​a​n​d​l​e​ ​c​l​o​s​e​F​i​l​e​]​;​

 ​ ​ ​ ​N​S​D​a​t​a​ ​*​i​n​D​a​t​a​ ​=​ ​[​[​o​u​t​P​i​p​e​ ​f​i​l​e​H​a​n​d​l​e​F​o​r​R​e​a​d​i​n​g​]​ ​r​e​a​d​D​a​t​a​T​o​E​n​d​O​f​F​i​l​e​]​;​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​s​t​r​i​n​g​ ​=​ ​[​[​N​S​S​t​r​i​n​g​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​D​a​t​a​:​ ​i​n​D​a​t​a​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​c​o​d​i​n​g​:​ ​N​S​U​T​F​8​S​t​r​i​n​g​E​n​c​o​d​i​n​g​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​o​u​t​T​e​x​t​ ​s​e​t​S​t​r​i​n​g​:​ ​s​t​r​i​n​g​]​;​
 ​ ​ ​ ​[​s​t​r​i​n​g​ ​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​[​t​a​s​k​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​i​n​P​i​p​e​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​o​u​t​P​i​p​e​ ​r​e​l​e​a​s​e​]​;​
}​ ​/​/​ ​s​o​r​t​

@​e​n​d​ ​/​/​ ​S​o​r​t​T​h​e​m​A​p​p​D​e​l​e​g​a​t​e​

Build and run your application.

Non-blocking reads

If a process takes a long time to return output, you don’t want your application to stop while waiting for output from the program. To avoid this case, you create a file handle that does non-blocking reading. In particular, you set up the file handle so that it posts a notification when there is data to be processed.

In this section, you are going to create a task which runs traceroute. traceroute sends out packets to discover the routers between your machine and another host. The responses from the routers sometimes take a while to get back. You will read the data in the background and append it to the text view.

The notification created will be an NSFileHandleReadCompletionNotification. To start the file handle waiting for the data, you will send it the message -readInBackgroundAndNotify. Each time you receive this notification, you will read the data using the -availableData method of NSFileHandle. You will also need to call -readInBackgroundAndNotify again to restart the wait for data.

The running application is shown in Figure 19.5.

Figure 19.5  TraceRoute application running

Create the header and edit the xib file

Create a new project of type Cocoa Application. Name it TraceRoute. Edit TraceRouteAppDelegate.h to add references to the moving pieces:

Example 19.3. 

#​i​m​p​o​r​t​ ​<​C​o​c​o​a​/​C​o​c​o​a​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​T​r​a​c​e​R​o​u​t​e​A​p​p​D​e​l​e​g​a​t​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​<​N​S​A​p​p​l​i​c​a​t​i​o​n​D​e​l​e​g​a​t​e​>​ ​{​
 ​ ​ ​ ​N​S​W​i​n​d​o​w​ ​*​_​w​i​n​d​o​w​;​
 ​ ​ ​ ​N​S​B​u​t​t​o​n​ ​*​_​b​u​t​t​o​n​;​
 ​ ​ ​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​_​h​o​s​t​F​i​e​l​d​;​
 ​ ​ ​ ​N​S​T​e​x​t​V​i​e​w​ ​*​_​t​e​x​t​V​i​e​w​;​
 ​ ​ ​ ​N​S​P​i​p​e​ ​*​_​p​i​p​e​;​
 ​ ​ ​ ​N​S​T​a​s​k​ ​*​_​t​a​s​k​;​
}​

@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​W​i​n​d​o​w​ ​*​w​i​n​d​o​w​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​B​u​t​t​o​n​ ​*​b​u​t​t​o​n​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​h​o​s​t​F​i​e​l​d​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​V​i​e​w​ ​*​t​e​x​t​V​i​e​w​;​

-​ ​(​v​o​i​d​)​ ​d​a​t​a​R​e​a​d​y​:​ ​(​N​S​N​o​t​i​f​i​c​a​t​i​o​n​ ​*​)​ ​n​o​t​i​f​i​c​a​t​i​o​n​;​
-​ ​(​v​o​i​d​)​ ​t​a​s​k​T​e​r​m​i​n​a​t​e​d​:​ ​(​N​S​N​o​t​i​f​i​c​a​t​i​o​n​ ​*​)​ ​n​o​t​i​f​i​c​a​t​i​o​n​;​
-​ ​(​v​o​i​d​)​ ​a​p​p​e​n​d​D​a​t​a​:​ ​(​N​S​D​a​t​a​ ​*​)​ ​d​d​a​t​a​;​
-​ ​(​v​o​i​d​)​ ​c​l​e​a​n​u​p​;​

-​ ​(​I​B​A​c​t​i​o​n​)​ ​s​t​a​r​t​S​t​o​p​:​ ​(​i​d​)​ ​s​e​n​d​e​r​;​

@​e​n​d​ ​/​/​ ​T​r​a​c​e​R​o​u​t​e​A​p​p​D​e​l​e​g​a​t​e​x​

Edit MainMenu.xib. Drag in an NSTextView, NSTextField, and an NSButton. Hook up the connections from the app delegate to the objects in the window, and also connect the button’s action to startStop:.

Here are the moving pieces for when the NSTask is running:

Edit the code

Edit TraceRouteAppDelegate.m and make it look like this:

Example 19.4. TraceRouteAppDelegate.m

#​i​m​p​o​r​t​ ​"​T​r​a​c​e​R​o​u​t​e​A​p​p​D​e​l​e​g​a​t​e​.​h​"​
#​i​m​p​o​r​t​ ​<​d​i​s​p​a​t​c​h​/​d​i​s​p​a​t​c​h​.​h​>​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​T​r​a​c​e​R​o​u​t​e​A​p​p​D​e​l​e​g​a​t​e​

@​s​y​n​t​h​e​s​i​z​e​ ​w​i​n​d​o​w​ ​=​ ​_​w​i​n​d​o​w​;​
@​s​y​n​t​h​e​s​i​z​e​ ​b​u​t​t​o​n​ ​=​ ​_​b​u​t​t​o​n​;​
@​s​y​n​t​h​e​s​i​z​e​ ​h​o​s​t​F​i​e​l​d​ ​=​ ​_​h​o​s​t​F​i​e​l​d​;​
@​s​y​n​t​h​e​s​i​z​e​ ​t​e​x​t​V​i​e​w​ ​=​ ​_​t​e​x​t​V​i​e​w​;​

/​/​ ​A​p​p​e​n​d​ ​t​h​e​ ​d​a​t​a​ ​t​o​ ​t​h​e​ ​s​t​r​i​n​g​ ​i​n​ ​t​h​e​ ​t​e​x​t​ ​v​i​e​w​

-​ ​(​v​o​i​d​)​ ​a​p​p​e​n​d​D​a​t​a​:​ ​(​N​S​D​a​t​a​ ​*​)​ ​d​a​t​a​ ​{​
 ​ ​ ​ ​N​S​R​a​n​g​e​ ​e​n​d​R​a​n​g​e​ ​=​ ​N​S​M​a​k​e​R​a​n​g​e​ ​(​s​e​l​f​.​t​e​x​t​V​i​e​w​.​s​t​r​i​n​g​.​l​e​n​g​t​h​,​ ​0​)​;​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​s​t​r​i​n​g​ ​=​ ​[​[​N​S​S​t​r​i​n​g​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​D​a​t​a​:​ ​d​a​t​a​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​c​o​d​i​n​g​:​ ​N​S​U​T​F​8​S​t​r​i​n​g​E​n​c​o​d​i​n​g​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​t​e​x​t​V​i​e​w​ ​r​e​p​l​a​c​e​C​h​a​r​a​c​t​e​r​s​I​n​R​a​n​g​e​:​ ​e​n​d​R​a​n​g​e​ ​ ​w​i​t​h​S​t​r​i​n​g​:​ ​s​t​r​i​n​g​]​;​
 ​ ​ ​ ​[​s​t​r​i​n​g​ ​r​e​l​e​a​s​e​]​;​
}​ ​/​/​ ​a​p​p​e​n​d​D​a​t​a​

-​ ​(​v​o​i​d​)​ ​c​l​e​a​n​u​p​ ​ ​{​
 ​ ​ ​ ​/​/​ ​R​e​l​e​a​s​e​ ​t​h​e​ ​o​l​d​ ​t​a​s​k​
 ​ ​ ​ ​[​_​t​a​s​k​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​_​t​a​s​k​ ​=​ ​n​i​l​;​

 ​ ​ ​ ​/​/​ ​R​e​l​e​a​s​e​ ​t​h​e​ ​p​i​p​e​
 ​ ​ ​ ​[​_​p​i​p​e​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​_​p​i​p​e​ ​=​ ​n​i​l​;​

 ​ ​ ​ ​/​/​ ​C​h​a​n​g​e​ ​t​h​e​ ​t​i​t​l​e​ ​o​n​ ​t​h​e​ ​b​u​t​t​o​n​
 ​ ​ ​ ​[​s​e​l​f​.​b​u​t​t​o​n​ ​s​e​t​T​i​t​l​e​:​ ​@​"​T​r​a​c​e​ ​t​h​e​ ​r​o​u​t​e​"​]​;​

 ​ ​ ​ ​/​/​ ​N​o​ ​l​o​n​g​e​r​ ​a​n​ ​o​b​s​e​r​v​e​r​
 ​ ​ ​ ​[​[​N​S​N​o​t​i​f​i​c​a​t​i​o​n​C​e​n​t​e​r​ ​d​e​f​a​u​l​t​C​e​n​t​e​r​]​ ​r​e​m​o​v​e​O​b​s​e​r​v​e​r​:​ ​s​e​l​f​]​;​
}​ ​/​/​ ​c​l​e​a​n​u​p​

-​ ​(​v​o​i​d​)​ ​t​a​s​k​T​e​r​m​i​n​a​t​e​d​:​ ​(​N​S​N​o​t​i​f​i​c​a​t​i​o​n​ ​*​)​ ​n​o​t​i​f​i​c​a​t​i​o​n​ ​{​
 ​ ​ ​ ​N​S​D​a​t​a​ ​*​l​e​f​t​I​n​P​i​p​e​;​

 ​ ​ ​ ​/​/​ ​F​l​u​s​h​ ​d​a​t​a​ ​s​t​i​l​l​ ​i​n​ ​p​i​p​e​.​
 ​ ​ ​ ​l​e​f​t​I​n​P​i​p​e​ ​=​ ​[​[​_​p​i​p​e​ ​f​i​l​e​H​a​n​d​l​e​F​o​r​R​e​a​d​i​n​g​]​ ​r​e​a​d​D​a​t​a​T​o​E​n​d​O​f​F​i​l​e​]​;​

 ​ ​ ​ ​i​f​ ​(​l​e​f​t​I​n​P​i​p​e​)​ ​[​s​e​l​f​ ​a​p​p​e​n​d​D​a​t​a​:​ ​l​e​f​t​I​n​P​i​p​e​]​;​
 ​ ​ ​ ​[​s​e​l​f​ ​c​l​e​a​n​u​p​]​;​
}​ ​/​/​ ​t​a​s​k​T​e​r​m​i​n​a​t​e​d​

-​ ​(​I​B​A​c​t​i​o​n​)​ ​s​t​a​r​t​S​t​o​p​:​ ​(​i​d​)​ ​s​e​n​d​e​r​ ​{​
 ​ ​ ​ ​/​/​ ​I​s​ ​t​h​e​ ​t​a​s​k​ ​a​l​r​e​a​d​y​ ​r​u​n​n​i​n​g​?​
 ​ ​ ​ ​i​f​ ​(​[​_​t​a​s​k​ ​i​s​R​u​n​n​i​n​g​]​)​ ​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​S​t​o​p​ ​i​t​ ​a​n​d​ ​t​i​d​y​ ​u​p​
 ​ ​ ​ ​ ​ ​ ​ ​[​_​t​a​s​k​ ​t​e​r​m​i​n​a​t​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​c​l​e​a​n​u​p​]​;​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​a​ ​t​a​s​k​ ​a​n​d​ ​p​i​p​e​
 ​ ​ ​ ​ ​ ​ ​ ​_​t​a​s​k​ ​=​ ​[​[​N​S​T​a​s​k​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​p​i​p​e​ ​=​ ​[​[​N​S​P​i​p​e​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​S​e​t​ ​t​h​e​ ​a​t​t​r​i​b​u​t​e​s​ ​o​f​ ​t​h​e​ ​t​a​s​k​
 ​ ​ ​ ​ ​ ​ ​ ​[​_​t​a​s​k​ ​s​e​t​L​a​u​n​c​h​P​a​t​h​:​ ​@​"​/​u​s​r​/​s​b​i​n​/​t​r​a​c​e​r​o​u​t​e​"​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​_​t​a​s​k​ ​s​e​t​A​r​g​u​m​e​n​t​s​:​ ​[​N​S​A​r​r​a​y​ ​a​r​r​a​y​W​i​t​h​O​b​j​e​c​t​:​ ​s​e​l​f​.​h​o​s​t​F​i​e​l​d​.​s​t​r​i​n​g​V​a​l​u​e​]​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​_​t​a​s​k​ ​s​e​t​S​t​a​n​d​a​r​d​O​u​t​p​u​t​:​ ​_​p​i​p​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​_​t​a​s​k​ ​s​e​t​S​t​a​n​d​a​r​d​E​r​r​o​r​:​ ​_​p​i​p​e​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​R​e​g​i​s​t​e​r​ ​f​o​r​ ​n​o​t​i​f​i​c​a​t​i​o​n​s​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​N​S​N​o​t​i​f​i​c​a​t​i​o​n​C​e​n​t​e​r​ ​d​e​f​a​u​l​t​C​e​n​t​e​r​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​d​d​O​b​s​e​r​v​e​r​:​ ​s​e​l​f​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​e​l​e​c​t​o​r​:​ ​@​s​e​l​e​c​t​o​r​(​d​a​t​a​R​e​a​d​y​:​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​a​m​e​:​ ​N​S​F​i​l​e​H​a​n​d​l​e​R​e​a​d​C​o​m​p​l​e​t​i​o​n​N​o​t​i​f​i​c​a​t​i​o​n​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​b​j​e​c​t​:​ ​[​_​p​i​p​e​ ​f​i​l​e​H​a​n​d​l​e​F​o​r​R​e​a​d​i​n​g​]​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​[​[​N​S​N​o​t​i​f​i​c​a​t​i​o​n​C​e​n​t​e​r​ ​d​e​f​a​u​l​t​C​e​n​t​e​r​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​d​d​O​b​s​e​r​v​e​r​:​ ​ ​s​e​l​f​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​e​l​e​c​t​o​r​:​ ​@​s​e​l​e​c​t​o​r​(​t​a​s​k​T​e​r​m​i​n​a​t​e​d​:​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​a​m​e​:​ ​N​S​T​a​s​k​D​i​d​T​e​r​m​i​n​a​t​e​N​o​t​i​f​i​c​a​t​i​o​n​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​b​j​e​c​t​:​ ​_​t​a​s​k​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​L​a​u​n​c​h​ ​t​h​e​ ​t​a​s​k​
 ​ ​ ​ ​ ​ ​ ​ ​[​_​t​a​s​k​ ​l​a​u​n​c​h​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​.​b​u​t​t​o​n​ ​s​e​t​T​i​t​l​e​:​ ​@​"​T​e​r​m​i​n​a​t​e​"​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​.​t​e​x​t​V​i​e​w​ ​s​e​t​S​t​r​i​n​g​:​ ​@​"​"​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​p​i​p​e​ ​r​e​a​d​i​n​g​ ​i​n​ ​t​h​e​ ​b​a​c​k​g​r​o​u​n​d​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​_​p​i​p​e​ ​f​i​l​e​H​a​n​d​l​e​F​o​r​R​e​a​d​i​n​g​]​ ​r​e​a​d​I​n​B​a​c​k​g​r​o​u​n​d​A​n​d​N​o​t​i​f​y​]​;​
 ​ ​ ​ ​}​
}​

-​ ​(​v​o​i​d​)​ ​d​a​t​a​R​e​a​d​y​:​ ​(​N​S​N​o​t​i​f​i​c​a​t​i​o​n​ ​*​)​ ​n​o​t​i​f​i​c​a​t​i​o​n​ ​{​
 ​ ​ ​ ​N​S​D​a​t​a​ ​*​d​a​t​a​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​n​o​t​i​f​i​c​a​t​i​o​n​ ​u​s​e​r​I​n​f​o​]​ ​ ​v​a​l​u​e​F​o​r​K​e​y​:​ ​N​S​F​i​l​e​H​a​n​d​l​e​N​o​t​i​f​i​c​a​t​i​o​n​D​a​t​a​I​t​e​m​]​;​
 ​ ​ ​ ​i​f​ ​(​d​a​t​a​ ​!​=​ ​n​i​l​)​ ​[​s​e​l​f​ ​a​p​p​e​n​d​D​a​t​a​:​ ​d​a​t​a​]​;​

 ​ ​ ​ ​/​/​ ​M​u​s​t​ ​r​e​s​t​a​r​t​ ​r​e​a​d​i​n​g​ ​i​n​ ​b​a​c​k​g​r​o​u​n​d​ ​a​f​t​e​r​ ​e​a​c​h​ ​n​o​t​i​f​i​c​a​t​i​o​n​
 ​ ​ ​ ​[​[​_​p​i​p​e​ ​f​i​l​e​H​a​n​d​l​e​F​o​r​R​e​a​d​i​n​g​]​ ​r​e​a​d​I​n​B​a​c​k​g​r​o​u​n​d​A​n​d​N​o​t​i​f​y​]​;​

}​ ​/​/​ ​d​a​t​a​R​e​a​d​y​

@​e​n​d​ ​/​/​ ​T​r​a​c​e​R​o​u​t​e​A​p​p​D​e​l​e​g​a​t​e​

Build and run the application.

Exercises

The traceroute command still runs if you quit TraceRoute before the route tracing finishes. This is OK, since you might want to kick off longer-running commands that will finish their processing at a later time. How would you clean up the task on application exit? Fix TraceRoute to tidy up things when it finishes.

20
Multithreading

Multithreading is another method for achieving concurrency in your application. While multiprocessing uses multiple independent processes with their own address spaces and resources, threads are multiple execution streams that all run within a single application, with a single address space, all sharing the available resources.

Multithreading, like multiprocessing, can take advantage of multiple CPUs. You can also use it to simplify some kinds of programming. Each thread can go on its merry way, computing values and calling functions that block, while other threads can run independently and are unaffected. One very common use of threads is handling requests in a network server (like a web server). A new connection is accept()ed and a thread is created to handle the request. This thread can then use read() to get the request and write() to send data back. It can also open files and perform loops, so there is no need to multiplex the I/O using select(), and there is no need to go through contortions to do computations piecemeal.

Posix Threads

Mac OS X uses the Posix thread API, more commonly known as “pthreads,” for its native threading model. Unfortunately, pthreads have a different convention for reporting error conditions than the rest of the Unix API. While it returns zero on success like you would expect, it returns the error code on error, rather than returning -1 and setting errno.

Creating threads

pthread_create() is used to create a new thread:

int pthread_create (pthread_t *threadID, const pthread_attr_t *attr,
                    void *(*startRoutine)(void *), void *arg);

This function returns zero on success and an error value on failure. These are the arguments it takes:

threadID | A pointer to a pthread_t. The thread ID for the new thread will be written here.
attr | A set of attributes. Pass NULL to use the default attributes. Specific attributes are not discussed; they tend to confuse discussions about the basics of threaded programming.

startRoutine

A pointer to a function with a signature of

void *someFunction (void *someArg);

This is where execution in the thread will start. The thread will terminate when this function returns. The someArg parameter is the value of the arg parameter passed to pthread_create(). The return value is some pointer to return status. You can pass whatever data structure you want for these two values.

arg | The argument given to the startRoutine.

The system allocates a private stack, similar to the main function call stack, for a thread when it gets created, as shown in Figure 20.1. The thread uses this stack for function call housekeeping and local variable storage.

Figure 20.1  Thread stacks

Threads are like processes because they have a return value that could be of interest to whoever created the thread. Using a mechanism similar to waitpid() for processes you can use pthread_join() to rendezvous with a particular thread:

int pthread_join (pthread_t threadID, void **valuePtr);

pthread_join() will block until the indicated thread exits. The return value will be written into the valuePtr.

To determine your own threadID, use pthread_self();

Sometimes you want to detach a thread so that you do not have to pthread_join() it. The thread will run to completion, exit, and be deleted. To do that, use pthread_detach():

int pthread_detach (pthread_t threadID);

Detached threads are sometimes called “daemon threads” because they run independently of their parents like daemons.

A common idiom is for a thread function to call

p​t​h​r​e​a​d​_​d​e​t​a​c​h​ ​(​p​t​h​r​e​a​d​_​s​e​l​f​(​)​)​;​

to turn itself into a daemon thread.

Example 20.1 is a little program that spins off a couple of threads that all count from zero to some value. Some are detached, and some aren’t and should be waited on:

Example 20.1. basics.m

/​/​ ​b​a​s​i​c​s​.​m​ ​-​-​ ​b​a​s​i​c​ ​t​h​r​e​a​d​ ​c​r​e​a​t​i​o​n​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​s​t​d​=​c​9​9​ ​-​W​a​l​l​ ​-​o​ ​b​a​s​i​c​s​ ​b​a​s​i​c​s​.​m​

#​i​m​p​o​r​t​ ​<​p​t​h​r​e​a​d​.​h​>​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​t​h​r​e​a​d​_​*​ ​c​a​l​l​s​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​x​i​t​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​(​)​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​u​s​l​e​e​p​(​)​

#​d​e​f​i​n​e​ ​T​H​R​E​A​D​_​C​O​U​N​T​ ​6​

/​/​ ​i​n​f​o​r​m​a​t​i​o​n​ ​t​o​ ​t​e​l​l​ ​t​h​e​ ​t​h​r​e​a​d​ ​h​o​w​ ​t​o​ ​b​e​h​a​v​e​

t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​T​h​r​e​a​d​I​n​f​o​ ​{​
 ​ ​ ​ ​p​t​h​r​e​a​d​_​t​ ​ ​ ​t​h​r​e​a​d​I​D​;​
 ​ ​ ​ ​i​n​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​d​e​x​;​
 ​ ​ ​ ​i​n​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​u​m​b​e​r​T​o​C​o​u​n​t​T​o​;​
 ​ ​ ​ ​i​n​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​t​a​c​h​Y​o​u​r​s​e​l​f​;​
 ​ ​ ​ ​i​n​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​l​e​e​p​T​i​m​e​;​ ​ ​ ​ ​ ​ ​/​/​ ​i​n​ ​m​i​c​r​o​s​e​c​o​n​d​s​ ​(​1​/​1​0​0​,​0​0​0​,​0​0​0​)​
}​ ​T​h​r​e​a​d​I​n​f​o​;​

v​o​i​d​ ​*​t​h​r​e​a​d​F​u​n​c​t​i​o​n​ ​(​v​o​i​d​ ​*​a​r​g​u​m​e​n​t​)​ ​{​
 ​ ​ ​ ​T​h​r​e​a​d​I​n​f​o​ ​*​i​n​f​o​ ​=​ ​(​T​h​r​e​a​d​I​n​f​o​ ​*​)​ ​a​r​g​u​m​e​n​t​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​t​h​r​e​a​d​ ​%​d​,​ ​c​o​u​n​t​i​n​g​ ​t​o​ ​%​d​,​ ​d​e​t​a​c​h​i​n​g​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​f​o​-​>​i​n​d​e​x​,​ ​i​n​f​o​-​>​n​u​m​b​e​r​T​o​C​o​u​n​t​T​o​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​i​n​f​o​-​>​d​e​t​a​c​h​Y​o​u​r​s​e​l​f​)​ ​?​ ​"​y​e​s​"​ ​:​ ​"​n​o​"​)​;​

 ​ ​ ​ ​i​f​ ​(​i​n​f​o​-​>​d​e​t​a​c​h​Y​o​u​r​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​p​t​h​r​e​a​d​_​d​e​t​a​c​h​ ​(​p​t​h​r​e​a​d​_​s​e​l​f​(​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​!​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​d​e​t​a​c​h​ ​t​h​r​e​a​d​ ​%​d​.​ ​E​r​r​o​r​:​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​f​o​-​>​i​n​d​e​x​,​ ​r​e​s​u​l​t​,​ ​s​t​r​e​r​r​o​r​(​r​e​s​u​l​t​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​n​o​w​ ​t​o​ ​d​o​ ​t​h​e​ ​a​c​t​u​a​l​ ​"​w​o​r​k​"​ ​o​f​ ​t​h​e​ ​t​h​r​e​a​d​

 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​i​n​f​o​-​>​n​u​m​b​e​r​T​o​C​o​u​n​t​T​o​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​t​h​r​e​a​d​ ​%​d​ ​c​o​u​n​t​i​n​g​ ​%​d​\​n​"​,​ ​i​n​f​o​-​>​i​n​d​e​x​,​ ​i​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​u​s​l​e​e​p​ ​(​i​n​f​o​-​>​s​l​e​e​p​T​i​m​e​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​t​h​r​e​a​d​ ​%​d​ ​d​o​n​e​\​n​"​,​ ​i​n​f​o​-​>​i​n​d​e​x​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​(​N​U​L​L​)​;​

}​ ​/​/​ ​t​h​r​e​a​d​F​u​n​c​t​i​o​n​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​T​h​r​e​a​d​I​n​f​o​ ​t​h​r​e​a​d​s​[​T​H​R​E​A​D​_​C​O​U​N​T​]​;​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​;​

 ​ ​ ​ ​/​/​ ​i​n​i​t​i​a​l​i​z​e​ ​t​h​e​ ​T​h​r​e​a​d​I​n​f​o​s​:​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​T​H​R​E​A​D​_​C​O​U​N​T​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​t​h​r​e​a​d​s​[​i​]​.​i​n​d​e​x​ ​=​ ​i​;​
 ​ ​ ​ ​ ​ ​ ​ ​t​h​r​e​a​d​s​[​i​]​.​n​u​m​b​e​r​T​o​C​o​u​n​t​T​o​ ​=​ ​(​i​ ​+​ ​1​)​ ​*​ ​2​;​
 ​ ​ ​ ​ ​ ​ ​ ​t​h​r​e​a​d​s​[​i​]​.​d​e​t​a​c​h​Y​o​u​r​s​e​l​f​ ​=​ ​(​i​ ​%​ ​2​)​;​ ​/​/​ ​d​e​t​a​c​h​ ​o​d​d​ ​t​h​r​e​a​d​s​
 ​ ​ ​ ​ ​ ​ ​ ​t​h​r​e​a​d​s​[​i​]​.​s​l​e​e​p​T​i​m​e​ ​=​ ​5​0​0​0​0​0​ ​+​ ​2​0​0​0​0​0​ ​*​ ​i​;​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​(​m​a​k​e​ ​s​u​b​s​e​u​q​e​n​t​ ​t​h​r​e​a​d​s​ ​w​a​i​t​ ​l​o​n​g​e​r​ ​b​e​t​w​e​e​n​ ​c​o​u​n​t​s​)​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​c​r​e​a​t​e​ ​t​h​e​ ​t​h​r​e​a​d​s​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​T​H​R​E​A​D​_​C​O​U​N​T​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​p​t​h​r​e​a​d​_​c​r​e​a​t​e​ ​(​&​t​h​r​e​a​d​s​[​i​]​.​t​h​r​e​a​d​I​D​,​ ​N​U​L​L​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​h​r​e​a​d​F​u​n​c​t​i​o​n​,​ ​&​t​h​r​e​a​d​s​[​i​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​!​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​c​o​u​l​d​ ​n​o​t​ ​p​t​h​r​e​a​d​_​c​r​e​a​t​e​ ​t​h​r​e​a​d​ ​%​d​.​ ​ ​E​r​r​o​r​:​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​,​ ​r​e​s​u​l​t​,​ ​s​t​r​e​r​r​o​r​(​r​e​s​u​l​t​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​x​i​t​ ​(​E​X​I​T​_​F​A​I​L​U​R​E​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​n​o​w​ ​r​e​n​d​e​z​v​o​u​s​ ​w​i​t​h​ ​a​l​l​ ​t​h​e​ ​n​o​n​-​d​e​t​a​c​h​e​d​ ​t​h​r​e​a​d​s​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​T​H​R​E​A​D​_​C​O​U​N​T​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​v​o​i​d​ ​*​r​e​t​V​a​l​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​!​t​h​r​e​a​d​s​[​i​]​.​d​e​t​a​c​h​Y​o​u​r​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​p​t​h​r​e​a​d​_​j​o​i​n​ ​(​t​h​r​e​a​d​s​[​i​]​.​t​h​r​e​a​d​I​D​,​ ​&​r​e​t​V​a​l​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​!​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​e​r​r​o​r​ ​j​o​i​n​i​n​g​ ​t​h​r​e​a​d​ ​%​d​.​ ​ ​E​r​r​o​r​:​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​,​ ​r​e​s​u​l​t​,​ ​s​t​r​e​r​r​o​r​(​r​e​s​u​l​t​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​j​o​i​n​e​d​ ​w​i​t​h​ ​t​h​r​e​a​d​ ​%​d​\​n​"​,​ ​i​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

The sample run is much more interesting in real life. Here is a part of it:

$​ ​.​/​b​a​s​i​c​s​
t​h​r​e​a​d​ ​0​,​ ​c​o​u​n​t​i​n​g​ ​t​o​ ​2​,​ ​d​e​t​a​c​h​i​n​g​ ​n​o​
 ​ ​t​h​r​e​a​d​ ​0​ ​c​o​u​n​t​i​n​g​ ​0​
t​h​r​e​a​d​ ​1​,​ ​c​o​u​n​t​i​n​g​ ​t​o​ ​4​,​ ​d​e​t​a​c​h​i​n​g​ ​y​e​s​
 ​ ​t​h​r​e​a​d​ ​1​ ​c​o​u​n​t​i​n​g​ ​0​
t​h​r​e​a​d​ ​2​,​ ​c​o​u​n​t​i​n​g​ ​t​o​ ​6​,​ ​d​e​t​a​c​h​i​n​g​ ​n​o​
 ​ ​t​h​r​e​a​d​ ​2​ ​c​o​u​n​t​i​n​g​ ​0​
.​.​.​
 ​ ​t​h​r​e​a​d​ ​2​ ​c​o​u​n​t​i​n​g​ ​1​
t​h​r​e​a​d​ ​0​ ​d​o​n​e​
j​o​i​n​e​d​ ​w​i​t​h​ ​t​h​r​e​a​d​ ​0​
 ​ ​t​h​r​e​a​d​ ​3​ ​c​o​u​n​t​i​n​g​ ​1​
 ​ ​t​h​r​e​a​d​ ​4​ ​c​o​u​n​t​i​n​g​ ​1​
.​.​.​
 ​ ​t​h​r​e​a​d​ ​4​ ​c​o​u​n​t​i​n​g​ ​9​
 ​ ​t​h​r​e​a​d​ ​5​ ​c​o​u​n​t​i​n​g​ ​8​
t​h​r​e​a​d​ ​4​ ​d​o​n​e​
j​o​i​n​e​d​ ​w​i​t​h​ ​t​h​r​e​a​d​ ​4​

There are a couple of things of interest. The first is there is no predefined order in which the threads will run. They are at the mercy of the OS scheduler. The second is that the main thread (where main() runs) is special. In all other threads, return is equivalent to pthread_exit(). When main() returns, however, it is equivalent to exit(), which terminates the process. If main() instead calls pthread_exit(), the application will not terminate until all threads have exited. This is why thread five sometimes does not finish its work by the time the program exits.

Synchronization

Remember the discussion about race conditions and concurrency when talking about signals? Threads have the same kinds of problems. There is something mentioned above that bears repeating: there is no predefined order in which the threads will run; they are at the mercy of the OS scheduler.

This can cause a lot of problems and introduces a lot of complexity to make sure that this (possibly) random order of execution will not corrupt data.

For example, in basics.m above, an array of ThreadInfo structures was first initialized with all of the information every thread would need, and after that, each thread received a pointer to its own array element.

T​h​r​e​a​d​I​n​f​o​ ​i​n​f​o​;​

f​o​r​ ​(​i​ ​=​ ​0​;​ ​i​ ​<​ ​T​H​R​E​A​D​_​C​O​U​N​T​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​i​n​f​o​-​>​i​n​d​e​x​ ​=​ ​i​;​
 ​ ​ ​ ​i​n​f​o​-​>​n​u​m​b​e​r​T​o​C​o​u​n​t​T​o​ ​=​ ​(​i​ ​+​ ​1​)​ ​*​ ​2​;​
 ​ ​ ​ ​.​.​.​
 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​p​t​h​r​e​a​d​_​c​r​e​a​t​e​ ​(​&​t​h​r​e​a​d​s​[​i​]​.​t​h​r​e​a​d​I​D​,​ ​N​U​L​L​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​h​r​e​a​d​F​u​n​c​t​i​o​n​,​ ​&​i​n​f​o​)​;​
}​

Then threadFunction would copy the data it wanted.

There are three cases to consider:

threadFunction starts executing immediately. The thread copies the data out of its argument pointer and goes on its merry way. Things work OK in this mode.

threadFunction starts executing a little later, like at the top of the loop. The thread gets created when the i loop variable is 2. The loop then goes to index 3 and creates a thread and now is about to do loop number 4. The “2 thread” finally gets scheduled and starts executing, looks at the memory for its control information, and uses the same configuration information intended for thread four.

threadFunction starts executing a little later, in the middle of the loop. This is the worst-case scenario: corrupted data. If the “2 thread” wakes up while index and numberToCountTo have been updated for i = 4, but detachYourself and sleepTime still have i = 3’s values, it will get half the data of the “3 thread” and half from the “4 thread” info.

Along the same lines, unprotected manipulations to data structures in a threaded environment can lead to corruption. Imagine a linked list that is in the middle of the pointer manipulations for adding a new node. This thread gets preempted by another thread that tries to add something of its own to that list. In the best case, you will crash because a pointer being modified is pointing to a bad address. The worst case is that one or the other insertions gets lost and you have slightly corrupted data, an error that will only manifest itself later, far away from the race condition that caused it.

Getting synchronization right is hard to do, and problems can be very hard to debug. There is a fine line between safe data access and efficient data access, and this is the primary reason threaded programming is much harder than people think it is.

To help address these problems, the pthread API provides some synchronization mechanisms, specifically mutexes (mutual exclusion locks) and condition variables.

Mutexes

Mutexes are used to serialize access to critical sections of code, meaning that when mutexes are used properly, only one thread of execution can be executing that section of code, as shown in Figure 20.2. All other threads wanting to run there will be blocked until the original thread finishes. After that, an arbitrary thread will be picked to run that piece of code.

Figure 20.2  A mutex

The use of a mutex over a section of code eliminates any concurrency that code may have, which is the general motivation for using threads in the first place, so you want the duration of a mutex lock to be as short as possible. Be aware that although mutexes control access to code, you are just using that to control access to data.

The datatype for a mutex is pthread_mutex_t. You can declare them as local variables, global variables, or malloc() memory for them. There are two ways to initialize a mutex. The first way is to use a static initializer, which is handy for a singleton mutex that you want to stick outside of a function:

s​t​a​t​i​c​ ​p​t​h​r​e​a​d​_​m​u​t​e​x​_​t​ ​m​y​M​u​t​e​x​ ​=​ ​P​T​H​R​E​A​D​_​M​U​T​E​X​_​I​N​I​T​I​A​L​I​Z​E​R​;​

The other is to get a chunk of memory the size of pthread_mutex_t and use pthread_mutex_init() on that memory:

int pthread_mutex_init (pthread_mutex_t *mutex,
                        const pthread_mutexattr_t *attr);

Like with pthread_create(), specific attributes aren’t discussed.

You would use pthread_mutex_init() when you have a mutex per data structure (like you create a new tree, and create a mutex just for that tree).

When you are done with a mutex you initialized with pthread_mutex_init(), use pthread_mutex_destroy() to release its resources:

int pthread_mutex_destroy (pthread_mutex_t *mutex);

To acquire a mutex use pthread_mutex_lock():

int pthread_mutex_lock (pthread_mutex_t *mutex);

If the mutex is unavailable, this call will block until it becomes free. When execution resumes after this call (with a zero return value), you know you have sole possession of the mutex.

To release a mutex, use pthread_mutex_unlock():

int pthread_mutex_unlock (pthread_mutex_t *mutex);

If you do not want to block when acquiring a mutex, use pthread_mutex_trylock():

int pthread_mutex_trylock (pthread_mutex_t *mutex);

If this returns with zero, you have locked the mutex. If it returns EBUSY, the mutex is locked by another party, and you need to try again.

Example 20.2 shows mutexes in action.

Example 20.2. mutex.m

/​/​ ​c​o​p​y​ ​b​a​s​i​c​.​m​ ​t​o​ ​m​u​t​e​x​.​m​ ​f​i​r​s​t​,​ ​t​h​e​n​ ​c​h​a​n​g​e​ ​t​h​r​e​a​d​F​u​n​c​t​i​o​n​ ​t​o​ ​t​h​i​s​:​

p​t​h​r​e​a​d​_​m​u​t​e​x​_​t​ ​g​_​m​u​t​e​x​ ​=​ ​P​T​H​R​E​A​D​_​M​U​T​E​X​_​I​N​I​T​I​A​L​I​Z​E​R​;​

v​o​i​d​ ​*​t​h​r​e​a​d​F​u​n​c​t​i​o​n​ ​(​v​o​i​d​ ​*​a​r​g​u​m​e​n​t​)​ ​{​
 ​ ​ ​ ​T​h​r​e​a​d​I​n​f​o​ ​*​i​n​f​o​ ​=​ ​(​T​h​r​e​a​d​I​n​f​o​ ​*​)​ ​a​r​g​u​m​e​n​t​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​t​h​r​e​a​d​ ​%​d​,​ ​c​o​u​n​t​i​n​g​ ​t​o​ ​%​d​,​ ​d​e​t​a​c​h​i​n​g​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​f​o​-​>​i​n​d​e​x​,​ ​i​n​f​o​-​>​n​u​m​b​e​r​T​o​C​o​u​n​t​T​o​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​i​n​f​o​-​>​d​e​t​a​c​h​Y​o​u​r​s​e​l​f​)​ ​?​ ​"​y​e​s​"​ ​:​ ​"​n​o​"​)​;​

 ​ ​ ​ ​i​f​ ​(​i​n​f​o​-​>​d​e​t​a​c​h​Y​o​u​r​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​p​t​h​r​e​a​d​_​d​e​t​a​c​h​ ​(​p​t​h​r​e​a​d​_​s​e​l​f​(​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​!​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​d​e​t​a​c​h​ ​t​h​r​e​a​d​ ​%​d​.​ ​E​r​r​o​r​:​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​f​o​-​>​i​n​d​e​x​,​ ​r​e​s​u​l​t​,​ ​s​t​r​e​r​r​o​r​(​r​e​s​u​l​t​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​n​o​w​ ​t​o​ ​d​o​ ​t​h​e​ ​a​c​t​u​a​l​ ​"​w​o​r​k​"​ ​o​f​ ​t​h​e​ ​t​h​r​e​a​d​

 ​ ​ ​ ​p​t​h​r​e​a​d​_​m​u​t​e​x​_​l​o​c​k​ ​(​&​g​_​m​u​t​e​x​)​;​

 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​i​n​f​o​-​>​n​u​m​b​e​r​T​o​C​o​u​n​t​T​o​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​ ​ ​t​h​r​e​a​d​ ​%​d​ ​c​o​u​n​t​i​n​g​ ​%​d​\​n​"​,​ ​i​n​f​o​-​>​i​n​d​e​x​,​ ​i​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​u​s​l​e​e​p​ ​(​i​n​f​o​-​>​s​l​e​e​p​T​i​m​e​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​p​t​h​r​e​a​d​_​m​u​t​e​x​_​u​n​l​o​c​k​ ​(​&​g​_​m​u​t​e​x​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​t​h​r​e​a​d​ ​%​d​ ​d​o​n​e​\​n​"​,​ ​i​n​f​o​-​>​i​n​d​e​x​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​N​U​L​L​;​

}​ ​/​/​ ​t​h​r​e​a​d​F​u​n​c​t​i​o​n​

Now see that execution has been serialized:

$​ ​.​/​m​u​t​e​x​
 ​ ​t​h​r​e​a​d​ ​0​ ​c​o​u​n​t​i​n​g​ ​1​
t​h​r​e​a​d​ ​0​ ​d​o​n​e​
 ​ ​t​h​r​e​a​d​ ​1​ ​c​o​u​n​t​i​n​g​ ​0​
j​o​i​n​e​d​ ​w​i​t​h​ ​t​h​r​e​a​d​ ​0​
 ​ ​t​h​r​e​a​d​ ​1​ ​c​o​u​n​t​i​n​g​ ​1​
 ​ ​t​h​r​e​a​d​ ​1​ ​c​o​u​n​t​i​n​g​ ​2​
 ​ ​t​h​r​e​a​d​ ​1​ ​c​o​u​n​t​i​n​g​ ​3​
t​h​r​e​a​d​ ​1​ ​d​o​n​e​
.​.​.​

Also notice how much slower the entire program runs now that the critical section (the counting loop) is serialized.

Deadlocks

If you are dealing with multiple mutexes for a single operation (such as locking two data structures before manipulating them together) and you are not careful about acquiring the mutexes in the same order every time you use them, you could be open to a deadlock situation.

Suppose thread one has

 ​ ​ ​ ​p​t​h​r​e​a​d​_​m​u​t​e​x​_​l​o​c​k​ ​(​m​u​t​e​x​A​)​;​
 ​ ​ ​ ​p​t​h​r​e​a​d​_​m​u​t​e​x​_​l​o​c​k​ ​(​m​u​t​e​x​B​)​;​

and thread two has

 ​ ​ ​ ​p​t​h​r​e​a​d​_​m​u​t​e​x​_​l​o​c​k​ ​(​m​u​t​e​x​B​)​;​
 ​ ​ ​ ​p​t​h​r​e​a​d​_​m​u​t​e​x​_​l​o​c​k​ ​(​m​u​t​e​x​A​)​;​

Figure 20.3 shows an execution path like this:

Thread one locks A. It gets pre-empted.

Thread two locks B. It gets pre-empted.

Thread one attempts to lock B. It blocks.

Thread two attempts to lock A. It blocks.

Figure 20.3  Deadlock

Both threads are now deadlocked, each waiting on the other to release its resource, and there is no way to break it. Each thread instead should do something like:

w​h​i​l​e​ ​(​1​)​ ​{​
 ​ ​ ​ ​p​t​h​r​e​a​d​_​m​u​t​e​x​_​l​o​c​k​ ​(​m​u​t​e​x​A​)​;​
 ​ ​ ​ ​i​f​ ​(​p​t​h​r​e​a​d​_​m​u​t​e​x​_​t​r​y​l​o​c​k​(​m​u​t​e​x​B​)​ ​=​=​ ​E​B​U​S​Y​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​t​h​r​e​a​d​_​m​u​t​e​x​_​u​n​l​o​c​k​ ​(​m​u​t​e​x​A​)​;​
 ​ ​ ​ ​}​
}​

That is, lock the first mutex and try locking the second. If it is locked, someone else has it, so release the first lock and try all over again, just in case someone has mutexB held and is waiting for mutexA.

Condition variables

Mutexes are great for what they do – protecting critical regions of code. Sometimes, though, you have situations where you want to wait until some condition is true before locking your mutex, like checking if a queue has an item in it before you process a request. If you use mutexes for this, you will end up writing loops to test the condition and then release the mutex. In other words, this is a polling operation, which is wasteful of CPU time.

Condition variables (pthread_cond_t) address this problem. Condition variables let interested parties block on the variable. The blocking will stop via a signal from another thread. Signal is an unfortunate choice of words, because this signaling has no relation to the Unix signals that were discussed earlier.

Like the mutex, you can initialize conditional variables statically with PTHREAD_COND_INITIALIZER, or you can use

int pthread_cond_init (pthread_cond_t *cond,
                       const pthread_condattr_t *attr);

Similarly, if you initialize a condition variable, destroy it with

int pthread_cond_destroy (pthread_cond_t *cond);

A mutex and a condition variable are associated. To use a condition variable, you lock the associated mutex, then while the condition you are interested in is false, call pthread_cond_wait():

int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);

The mutex is automatically unlocked, and the call blocks. When another thread calls pthread_cond_signal():

int pthread_cond_signal (pthread_cond_t *cond);

a single thread that is currently blocked on pthread_cond_wait() will wake up (use pthread_cond_broadcast() to wake up all blocked threads). Be aware that pthread_cond_wait() can spuriously return. You should always check the value of your condition before moving on.

OK, so what is this “value of your condition”? Consider Figure 20.4, the request queue for a web server:

Figure 20.4  Server request queue

A single thread blocks on the accept() call waiting for new connections. When a new connection comes in, it gets put at the end of the request queue. At the same time, connection threads are hanging around, pulling the topmost entry off of the queue and processing them. This is a version of the classic “producer/consumer” problem that just about every concurrent programming book talks about.

For both the “accept” and “connection” threads, there are two states they can be in with respect to the queue. Accept thread accepts a new connection:

The queue has free space to put a new request

Put request on queue

Signal a connection thread to wake up

The queue is full:

block on a condition variable until there is space in the queue

when it wakes up, put the request in the queue

In pseudo code:

p​t​h​r​e​a​d​_​m​u​t​e​x​_​l​o​c​k​ ​(​q​u​e​u​e​L​o​c​k​)​;​
w​h​i​l​e​ ​(​q​u​e​u​e​ ​i​s​ ​f​u​l​l​)​ ​{​
 ​ ​ ​ ​p​t​h​r​e​a​d​_​c​o​n​d​_​w​a​i​t​ ​(​g​_​q​u​e​u​e​C​o​n​d​,​ ​q​u​e​u​e​L​o​c​k​)​;​
}​
p​u​t​ ​i​t​e​m​ ​o​n​ ​t​h​e​ ​q​u​e​u​e​;​
p​t​h​r​e​a​d​_​m​u​t​e​x​_​u​n​l​o​c​k​ ​(​q​u​e​u​e​L​o​c​k​)​;​
s​i​g​n​a​l​ ​a​ ​c​o​n​n​e​c​t​i​o​n​ ​t​h​r​e​a​d​
g​o​ ​b​a​c​k​ ​t​o​ ​a​c​c​e​p​t​(​)​

The pthread_cond_wait() will only happen when the queue is full, and you hang around in that loop until there is free space in the queue. The while loop is to protect against spurious returns.

Connection thread:

The queue has a request on it:

get the request from the queue

if the queue had been completely full, signal the accept() thread that there is now space available, in case it is blocked waiting or free space in the queue

The queue is empty:

block on a condition variable until there is space in the queue

when it wakes up, get the request from the queue

Likewise in pseudo code:

p​t​h​r​e​a​d​_​m​u​t​e​x​_​l​o​c​k​ ​(​q​u​e​u​e​L​o​c​k​)​;​
w​h​i​l​e​ ​(​q​u​e​u​e​ ​i​s​ ​e​m​p​t​y​)​ ​{​
 ​ ​ ​ ​p​t​h​r​e​a​d​_​c​o​n​d​_​w​a​i​t​ ​(​g​_​q​u​e​u​e​C​o​n​d​,​ ​q​u​e​u​e​L​o​c​k​)​;​
}​
g​e​t​ ​i​t​e​m​ ​f​r​o​m​ ​t​h​e​ ​q​u​e​u​e​;​
p​t​h​r​e​a​d​_​m​u​t​e​x​_​u​n​l​o​c​k​ ​(​q​u​e​u​e​L​o​c​k​)​;​
s​i​g​n​a​l​ ​t​h​e​ ​a​c​c​e​p​t​ ​t​h​r​e​a​d​
p​r​o​c​e​s​s​ ​t​h​e​ ​r​e​q​u​e​s​t​.​

If you do not want to block indefinitely, you can specify a timeout for waiting on a condition variable by using pthread_cond_timedwait():

int pthread_cond_timedwait (pthread_cond_t *cond,
                            pthread_mutex_t *mutex,
                            const struct timespec *abstime);

The timeout is specified by filling out this structure:

s​t​r​u​c​t​ ​t​i​m​e​s​p​e​c​ ​{​
 ​ ​ ​ ​t​i​m​e​_​t​ ​ ​t​v​_​s​e​c​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​s​e​c​o​n​d​s​
 ​ ​ ​ ​l​o​n​g​ ​ ​ ​ ​t​v​_​n​s​e​c​;​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​n​a​n​o​s​e​c​o​n​d​s​
}​;​

pthread_timed_condwait() differs from similar calls like select() in that the timeout is an absolute time, not a relative time. This makes it easier to handle the case of spurious wake-ups because you do not have to recalculate the wait interval each time.

Example 20.3 is a simple webserver which has a request queue that uses condition variables.

Example 20.3. webserve-thread.m

/​/​ ​w​e​b​s​e​r​v​e​-​t​h​r​e​a​d​.​m​ ​-​-​ ​a​ ​v​e​r​y​ ​s​i​m​p​l​e​ ​w​e​b​ ​s​e​r​v​e​r​ ​u​s​i​n​g​ ​t​h​r​e​a​d​s​ ​t​o​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​a​n​d​l​e​ ​r​e​q​u​e​s​t​s​

/​/​ ​g​c​c​ ​-​g​ ​-​s​t​d​=​c​9​9​ ​-​W​a​l​l​ ​-​o​ ​w​e​b​s​e​r​v​e​-​t​h​r​e​a​d​ ​w​e​b​s​e​r​v​e​-​t​h​r​e​a​d​.​m​

#​i​m​p​o​r​t​ ​<​a​r​p​a​/​i​n​e​t​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​i​n​e​t​_​n​t​o​a​
#​i​m​p​o​r​t​ ​<​a​r​p​a​/​i​n​e​t​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​i​n​e​t​_​n​t​o​a​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​a​s​s​e​r​t​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​a​s​s​e​r​t​
#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
#​i​m​p​o​r​t​ ​<​n​e​t​i​n​e​t​/​i​n​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​o​c​k​a​d​d​r​_​i​n​
#​i​m​p​o​r​t​ ​<​p​t​h​r​e​a​d​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​t​h​r​e​a​d​_​*​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​
#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​,​ ​p​i​p​e​,​ ​e​x​e​c​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​
#​i​m​p​o​r​t​ ​<​s​y​s​/​p​a​r​a​m​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​M​I​N​/​M​A​X​
#​i​m​p​o​r​t​ ​<​s​y​s​/​r​e​s​o​u​r​c​e​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​u​c​t​ ​r​u​s​a​g​e​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​o​c​k​e​t​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​o​c​k​e​t​(​)​,​ ​A​F​_​I​N​E​T​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​i​m​e​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​u​c​t​ ​t​i​m​e​v​a​l​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​i​d​_​t​,​ ​a​m​o​n​g​s​t​ ​o​t​h​e​r​s​
#​i​m​p​o​r​t​ ​<​s​y​s​/​w​a​i​t​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​w​a​i​t​3​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​f​o​r​k​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​c​l​o​s​e​

#​d​e​f​i​n​e​ ​P​O​R​T​_​N​U​M​B​E​R​ ​8​0​8​0​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​s​e​t​ ​t​o​ ​8​0​ ​t​o​ ​l​i​s​t​e​n​ ​o​n​ ​t​h​e​ ​H​T​T​P​ ​p​o​r​t​
#​d​e​f​i​n​e​ ​M​A​X​_​T​H​R​E​A​D​S​ ​5​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​m​a​x​i​m​u​m​ ​n​u​m​b​e​r​ ​o​f​ ​c​o​n​n​e​c​t​i​o​n​ ​t​h​r​e​a​d​s​

/​/​ ​-​-​-​-​-​ ​q​u​e​u​e​ ​f​o​r​ ​h​a​n​d​l​i​n​g​ ​r​e​q​u​e​s​t​s​

#​d​e​f​i​n​e​ ​Q​U​E​U​E​_​D​E​P​T​H​ ​1​0​

t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​R​e​q​u​e​s​t​ ​{​
 ​ ​ ​ ​i​n​t​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​d​;​ ​/​/​ ​f​i​l​e​ ​d​e​s​c​r​i​p​t​o​r​ ​o​f​ ​t​h​e​ ​i​n​c​o​m​i​n​g​ ​r​e​q​u​e​s​t​
 ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​ ​a​d​d​r​e​s​s​;​
}​ ​R​e​q​u​e​s​t​;​

s​t​a​t​i​c​ ​R​e​q​u​e​s​t​ ​g​_​r​e​q​u​e​s​t​Q​u​e​u​e​[​Q​U​E​U​E​_​D​E​P​T​H​]​;​
s​t​a​t​i​c​ ​i​n​t​ ​g​_​q​u​e​u​e​E​n​d​ ​=​ ​-​1​;​ ​/​/​ ​0​ ​i​s​ ​t​h​e​ ​h​e​a​d​.​ ​ ​e​n​d​ ​=​=​ ​-​1​ ​f​o​r​ ​e​m​p​t​y​ ​q​u​e​u​e​

s​t​a​t​i​c​ ​p​t​h​r​e​a​d​_​m​u​t​e​x​_​t​ ​g​_​q​u​e​u​e​M​u​t​e​x​ ​=​ ​P​T​H​R​E​A​D​_​M​U​T​E​X​_​I​N​I​T​I​A​L​I​Z​E​R​;​
s​t​a​t​i​c​ ​p​t​h​r​e​a​d​_​c​o​n​d​_​t​ ​g​_​q​u​e​u​e​C​o​n​d​ ​=​ ​P​T​H​R​E​A​D​_​C​O​N​D​_​I​N​I​T​I​A​L​I​Z​E​R​;​

v​o​i​d​ ​d​u​m​p​Q​u​e​u​e​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​d​ ​i​t​e​m​s​ ​i​n​ ​q​u​e​u​e​:​\​n​"​,​ ​g​_​q​u​e​u​e​E​n​d​ ​+​ ​1​)​;​

 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​=​ ​g​_​q​u​e​u​e​E​n​d​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​d​:​ ​ ​f​d​ ​i​s​ ​%​d​\​n​"​,​ ​i​,​ ​g​_​r​e​q​u​e​s​t​Q​u​e​u​e​[​i​]​.​f​d​)​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​d​u​m​p​Q​u​e​u​e​

v​o​i​d​ ​g​e​t​R​e​q​u​e​s​t​ ​(​i​n​t​ ​*​f​d​,​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​*​a​d​d​r​e​s​s​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​d​o​S​i​g​n​a​l​ ​=​ ​0​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​b​e​f​o​r​e​ ​g​e​t​R​e​q​u​e​s​t​\​n​"​)​;​
 ​ ​ ​ ​d​u​m​p​Q​u​e​u​e​ ​(​)​;​

 ​ ​ ​ ​p​t​h​r​e​a​d​_​m​u​t​e​x​_​l​o​c​k​ ​(​&​g​_​q​u​e​u​e​M​u​t​e​x​)​;​
 ​ ​ ​ ​w​h​i​l​e​ ​(​g​_​q​u​e​u​e​E​n​d​ ​=​=​ ​-​1​)​ ​{​ ​/​/​ ​q​u​e​u​e​ ​i​s​ ​e​m​p​t​y​
 ​ ​ ​ ​ ​ ​ ​ ​p​t​h​r​e​a​d​_​c​o​n​d​_​w​a​i​t​ ​(​&​g​_​q​u​e​u​e​C​o​n​d​,​ ​&​g​_​q​u​e​u​e​M​u​t​e​x​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​a​f​t​e​r​ ​w​a​k​e​u​p​\​n​"​)​;​
 ​ ​ ​ ​d​u​m​p​Q​u​e​u​e​ ​(​)​;​

 ​ ​ ​ ​/​/​ ​c​o​p​y​ ​t​h​e​ ​r​e​q​u​e​s​t​ ​t​o​ ​t​h​e​ ​c​a​l​l​e​r​
 ​ ​ ​ ​*​f​d​ ​=​ ​g​_​r​e​q​u​e​s​t​Q​u​e​u​e​[​0​]​.​f​d​;​
 ​ ​ ​ ​m​e​m​c​p​y​ ​(​a​d​d​r​e​s​s​,​ ​&​g​_​r​e​q​u​e​s​t​Q​u​e​u​e​[​0​]​.​a​d​d​r​e​s​s​,​ ​s​i​z​e​o​f​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​)​)​;​

 ​ ​ ​ ​i​f​ ​(​g​_​q​u​e​u​e​E​n​d​ ​=​=​ ​Q​U​E​U​E​_​D​E​P​T​H​ ​-​ ​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​g​o​i​n​g​ ​f​r​o​m​ ​f​u​l​l​ ​t​o​ ​n​o​t​ ​q​u​i​t​e​ ​s​o​ ​f​u​l​l​
 ​ ​ ​ ​ ​ ​ ​ ​d​o​S​i​g​n​a​l​ ​=​ ​1​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​s​h​i​f​t​ ​u​p​ ​t​h​e​ ​q​u​e​u​e​
 ​ ​ ​ ​i​f​ ​(​g​_​q​u​e​u​e​E​n​d​ ​>​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​m​e​m​m​o​v​e​ ​(​g​_​r​e​q​u​e​s​t​Q​u​e​u​e​,​ ​g​_​r​e​q​u​e​s​t​Q​u​e​u​e​ ​+​ ​1​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​o​f​(​R​e​q​u​e​s​t​)​ ​*​ ​g​_​q​u​e​u​e​E​n​d​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​g​_​q​u​e​u​e​E​n​d​-​-​;​

 ​ ​ ​ ​p​t​h​r​e​a​d​_​m​u​t​e​x​_​u​n​l​o​c​k​ ​(​&​g​_​q​u​e​u​e​M​u​t​e​x​)​;​

 ​ ​ ​ ​i​f​ ​(​d​o​S​i​g​n​a​l​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​t​h​r​e​a​d​_​c​o​n​d​_​s​i​g​n​a​l​ ​(​&​g​_​q​u​e​u​e​C​o​n​d​)​;​
 ​ ​ ​ ​}​

}​ ​/​/​ ​g​e​t​R​e​q​u​e​s​t​

v​o​i​d​ ​q​u​e​u​e​R​e​q​u​e​s​t​ ​(​i​n​t​ ​f​d​,​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​*​a​d​d​r​e​s​s​)​ ​{​
 ​ ​ ​ ​p​t​h​r​e​a​d​_​m​u​t​e​x​_​l​o​c​k​ ​(​&​g​_​q​u​e​u​e​M​u​t​e​x​)​;​

 ​ ​ ​ ​a​s​s​e​r​t​ ​(​g​_​q​u​e​u​e​E​n​d​ ​<​=​ ​Q​U​E​U​E​_​D​E​P​T​H​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​b​e​f​o​r​e​ ​q​u​e​u​e​\​n​"​)​;​
 ​ ​ ​ ​w​h​i​l​e​ ​(​g​_​q​u​e​u​e​E​n​d​ ​=​=​ ​Q​U​E​U​E​_​D​E​P​T​H​ ​-​ ​1​)​ ​{​ ​/​/​ ​q​u​e​u​e​ ​i​s​ ​f​u​l​l​
 ​ ​ ​ ​ ​ ​ ​ ​p​t​h​r​e​a​d​_​c​o​n​d​_​w​a​i​t​ ​(​&​g​_​q​u​e​u​e​C​o​n​d​,​ ​&​g​_​q​u​e​u​e​M​u​t​e​x​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​a​f​t​e​r​ ​q​u​e​u​e​\​n​"​)​;​

 ​ ​ ​ ​a​s​s​e​r​t​ ​(​g​_​q​u​e​u​e​E​n​d​ ​<​ ​Q​U​E​U​E​_​D​E​P​T​H​ ​-​ ​1​)​;​

 ​ ​ ​ ​g​_​q​u​e​u​e​E​n​d​+​+​;​
 ​ ​ ​ ​g​_​r​e​q​u​e​s​t​Q​u​e​u​e​[​g​_​q​u​e​u​e​E​n​d​]​.​f​d​ ​=​ ​f​d​;​
 ​ ​ ​ ​m​e​m​c​p​y​ ​(​&​g​_​r​e​q​u​e​s​t​Q​u​e​u​e​[​g​_​q​u​e​u​e​E​n​d​]​.​a​d​d​r​e​s​s​,​ ​a​d​d​r​e​s​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​o​f​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​)​)​;​

 ​ ​ ​ ​p​t​h​r​e​a​d​_​m​u​t​e​x​_​u​n​l​o​c​k​ ​(​&​g​_​q​u​e​u​e​M​u​t​e​x​)​;​
 ​ ​ ​ ​p​t​h​r​e​a​d​_​c​o​n​d​_​s​i​g​n​a​l​ ​(​&​g​_​q​u​e​u​e​C​o​n​d​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​e​n​q​u​e​u​e​d​ ​a​n​o​t​h​e​r​ ​o​n​e​\​n​"​)​;​
 ​ ​ ​ ​d​u​m​p​Q​u​e​u​e​ ​(​)​;​

}​ ​/​/​ ​q​u​e​u​e​R​e​q​u​e​s​t​

/​/​ ​H​T​T​P​ ​r​e​q​u​e​s​t​ ​h​a​n​d​l​i​n​g​

/​/​ ​t​h​e​s​e​ ​a​r​e​ ​s​o​m​e​ ​o​f​ ​t​h​e​ ​c​o​m​m​o​n​ ​H​T​T​P​ ​r​e​s​p​o​n​s​e​ ​c​o​d​e​s​

#​d​e​f​i​n​e​ ​H​T​T​P​_​O​K​ ​ ​ ​ ​ ​ ​ ​ ​ ​2​0​0​
#​d​e​f​i​n​e​ ​H​T​T​P​_​N​O​T​_​F​O​U​N​D​ ​ ​4​0​4​
#​d​e​f​i​n​e​ ​H​T​T​P​_​E​R​R​O​R​ ​ ​ ​ ​ ​ ​5​0​0​

/​/​ ​r​e​t​u​r​n​ ​a​ ​s​t​r​i​n​g​ ​t​o​ ​t​h​e​ ​b​r​o​w​s​e​r​

#​d​e​f​i​n​e​ ​r​e​t​u​r​n​S​t​r​i​n​g​(​h​t​t​p​R​e​s​u​l​t​,​ ​s​t​r​i​n​g​,​ ​c​h​a​n​n​e​l​)​ ​\​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​B​u​f​f​e​r​(​(​h​t​t​p​R​e​s​u​l​t​)​,​ ​(​s​t​r​i​n​g​)​,​ ​(​s​t​r​l​e​n​(​s​t​r​i​n​g​)​)​,​ ​(​c​h​a​n​n​e​l​)​)​

/​/​ ​r​e​t​u​r​n​ ​a​ ​c​h​a​r​a​c​t​e​r​ ​b​u​f​f​e​r​ ​(​n​o​t​ ​n​e​c​e​s​s​a​r​i​l​y​ ​z​e​r​o​-​t​e​r​m​i​n​a​t​e​d​)​ ​t​o​ ​t​h​e​ ​b​r​o​w​s​e​r​

v​o​i​d​ ​r​e​t​u​r​n​B​u​f​f​e​r​ ​(​i​n​t​ ​h​t​t​p​R​e​s​u​l​t​,​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​c​o​n​t​e​n​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​c​o​n​t​e​n​t​L​e​n​g​t​h​,​ ​F​I​L​E​ ​*​c​o​m​m​C​h​a​n​n​e​l​)​ ​{​
 ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​c​o​m​m​C​h​a​n​n​e​l​,​ ​"​H​T​T​P​/​1​.​0​ ​%​d​ ​b​l​a​h​\​n​"​,​ ​h​t​t​p​R​e​s​u​l​t​)​;​
 ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​c​o​m​m​C​h​a​n​n​e​l​,​ ​"​C​o​n​t​e​n​t​-​T​y​p​e​:​ ​t​e​x​t​/​h​t​m​l​\​n​"​)​;​
 ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​c​o​m​m​C​h​a​n​n​e​l​,​ ​"​C​o​n​t​e​n​t​-​L​e​n​g​t​h​:​ ​%​d​\​n​"​,​ ​c​o​n​t​e​n​t​L​e​n​g​t​h​)​;​
 ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​c​o​m​m​C​h​a​n​n​e​l​,​ ​"​\​n​"​)​;​
 ​ ​ ​ ​f​w​r​i​t​e​ ​(​c​o​n​t​e​n​t​,​ ​c​o​n​t​e​n​t​L​e​n​g​t​h​,​ ​1​,​ ​c​o​m​m​C​h​a​n​n​e​l​ ​)​;​
}​ ​/​/​ ​r​e​t​u​r​n​B​u​f​f​e​r​

/​/​ ​s​t​r​e​a​m​ ​b​a​c​k​ ​t​o​ ​t​h​e​ ​b​r​o​w​s​e​r​ ​n​u​m​b​e​r​s​ ​b​e​i​n​g​ ​c​o​u​n​t​e​d​,​ ​w​i​t​h​ ​a​ ​p​a​u​s​e​ ​b​e​t​w​e​e​n​
/​/​ ​t​h​e​m​.​ ​ ​T​h​e​ ​u​s​e​r​ ​s​h​o​u​l​d​ ​s​e​e​ ​t​h​e​ ​n​u​m​b​e​r​s​ ​a​p​p​e​a​r​ ​e​v​e​r​y​ ​c​o​u​p​l​e​ ​o​f​ ​s​e​c​o​n​d​s​

v​o​i​d​ ​r​e​t​u​r​n​N​u​m​b​e​r​s​ ​(​i​n​t​ ​n​u​m​b​e​r​,​ ​F​I​L​E​ ​*​c​o​m​m​C​h​a​n​n​e​l​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​m​i​n​,​ ​m​a​x​,​ ​i​;​
 ​ ​ ​ ​m​i​n​ ​=​ ​M​I​N​ ​(​n​u​m​b​e​r​,​ ​1​)​;​
 ​ ​ ​ ​m​a​x​ ​=​ ​M​A​X​ ​(​n​u​m​b​e​r​,​ ​1​)​;​

 ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​c​o​m​m​C​h​a​n​n​e​l​,​ ​"​H​T​T​P​/​1​.​0​ ​%​d​ ​O​K​\​n​"​,​ ​H​T​T​P​_​O​K​)​;​
 ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​c​o​m​m​C​h​a​n​n​e​l​,​ ​"​C​o​n​t​e​n​t​-​T​y​p​e​:​ ​t​e​x​t​/​h​t​m​l​;​ ​c​h​a​r​s​e​t​=​I​S​O​-​8​8​5​9​-​1​\​n​"​)​;​
 ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​c​o​m​m​C​h​a​n​n​e​l​,​ ​"​\​n​"​)​;​ ​/​/​ ​n​o​ ​c​o​n​t​e​n​t​ ​l​e​n​g​t​h​ ​s​i​n​c​e​ ​t​h​i​s​ ​i​s​ ​d​y​n​a​m​i​c​

 ​ ​ ​ ​/​/​ ​S​e​n​d​ ​4​K​ ​o​f​ ​d​u​m​m​y​ ​d​a​t​a​ ​s​o​ ​t​h​a​t​ ​b​r​o​w​s​e​r​s​ ​w​i​l​l​ ​s​t​a​r​t​ ​s​h​o​w​i​n​g​ ​t​h​e​ ​i​n​c​r​e​m​e​n​t​a​l​
 ​ ​ ​ ​/​/​ ​t​e​x​t​.​
 ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​4​ ​*​ ​1​0​2​4​]​;​
 ​ ​ ​ ​m​e​m​s​e​t​ ​(​b​u​f​f​e​r​,​ ​'​ ​'​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​)​;​
 ​ ​ ​ ​f​w​r​i​t​e​ ​(​b​u​f​f​e​r​,​ ​1​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​,​ ​c​o​m​m​C​h​a​n​n​e​l​)​;​

 ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​c​o​m​m​C​h​a​n​n​e​l​,​ ​"​<​h​2​>​T​h​e​ ​n​u​m​b​e​r​s​ ​f​r​o​m​ ​%​d​ ​t​o​ ​%​d​<​/​h​2​>​\​n​"​,​ ​m​i​n​,​ ​m​a​x​)​;​

 ​ ​ ​ ​f​o​r​ ​(​i​ ​=​ ​m​i​n​;​ ​i​ ​<​=​ ​m​a​x​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​l​e​e​p​ ​(​2​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​c​o​m​m​C​h​a​n​n​e​l​,​ ​"​%​d​\​n​"​,​ ​i​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​f​l​u​s​h​ ​(​c​o​m​m​C​h​a​n​n​e​l​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​c​o​m​m​C​h​a​n​n​e​l​,​ ​"​<​h​r​>​D​o​n​e​\​n​"​)​;​

}​ ​/​/​ ​r​e​t​u​r​n​N​u​m​b​e​r​s​

/​/​ ​R​e​t​u​r​n​ ​a​ ​f​i​l​e​ ​f​r​o​m​ ​t​h​e​ ​f​i​l​e​ ​s​y​s​t​e​m​,​ ​r​e​l​a​t​i​v​e​ ​t​o​ ​w​h​e​r​e​ ​t​h​e​ ​w​e​b​s​e​r​v​e​r​
/​/​ ​i​s​ ​r​u​n​n​i​n​g​.​ ​ ​N​o​t​e​ ​t​h​a​t​ ​t​h​i​s​ ​d​o​e​s​n​'​t​ ​l​o​o​k​ ​f​o​r​ ​a​n​y​ ​n​a​s​t​y​ ​c​h​a​r​a​c​t​e​r​s​
/​/​ ​l​i​k​e​ ​'​.​.​'​,​ ​s​o​ ​t​h​i​s​ ​f​u​n​c​t​i​o​n​ ​i​s​ ​a​ ​p​r​e​t​t​y​ ​b​i​g​ ​s​e​c​u​r​i​t​y​ ​h​o​l​e​.​

v​o​i​d​ ​r​e​t​u​r​n​F​i​l​e​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​f​i​l​e​n​a​m​e​,​ ​F​I​L​E​ ​*​c​o​m​m​C​h​a​n​n​e​l​)​ ​{​
 ​ ​ ​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​m​i​m​e​t​y​p​e​ ​=​ ​N​U​L​L​;​

 ​ ​ ​ ​/​/​ ​t​r​y​ ​t​o​ ​g​u​e​s​s​ ​t​h​e​ ​m​i​m​e​ ​t​y​p​e​.​ ​ ​I​E​ ​a​s​s​u​m​e​s​ ​a​l​l​ ​n​o​n​-​g​r​a​p​h​i​c​ ​f​i​l​e​s​ ​a​r​e​ ​H​T​M​L​
 ​ ​ ​ ​i​f​ ​(​s​t​r​s​t​r​(​f​i​l​e​n​a​m​e​,​ ​"​.​m​"​)​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​m​i​m​e​t​y​p​e​ ​=​ ​"​t​e​x​t​/​p​l​a​i​n​"​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​s​t​r​s​t​r​(​f​i​l​e​n​a​m​e​,​ ​"​.​h​"​)​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​m​i​m​e​t​y​p​e​ ​=​ ​"​t​e​x​t​/​p​l​a​i​n​"​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​s​t​r​s​t​r​(​f​i​l​e​n​a​m​e​,​ ​"​.​t​x​t​"​)​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​m​i​m​e​t​y​p​e​ ​=​ ​"​t​e​x​t​/​p​l​a​i​n​"​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​s​t​r​s​t​r​(​f​i​l​e​n​a​m​e​,​ ​"​.​t​g​z​"​)​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​m​i​m​e​t​y​p​e​ ​=​ ​"​a​p​p​l​i​c​a​t​i​o​n​/​x​-​c​o​m​p​r​e​s​s​e​d​"​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​s​t​r​s​t​r​(​f​i​l​e​n​a​m​e​,​ ​"​.​h​t​m​l​"​)​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​m​i​m​e​t​y​p​e​ ​=​ ​"​t​e​x​t​/​h​t​m​l​"​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​s​t​r​s​t​r​(​f​i​l​e​n​a​m​e​,​ ​"​.​h​t​m​l​"​)​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​m​i​m​e​t​y​p​e​ ​=​ ​"​t​e​x​t​/​h​t​m​l​"​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​s​t​r​s​t​r​(​f​i​l​e​n​a​m​e​,​ ​"​.​x​y​z​"​)​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​m​i​m​e​t​y​p​e​ ​=​ ​"​a​u​d​i​o​/​m​p​e​g​"​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​F​I​L​E​ ​*​f​i​l​e​ ​=​ ​f​o​p​e​n​ ​(​f​i​l​e​n​a​m​e​,​ ​"​r​"​)​;​

 ​ ​ ​ ​i​f​ ​(​f​i​l​e​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​S​t​r​i​n​g​ ​(​H​T​T​P​_​N​O​T​_​F​O​U​N​D​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​f​i​n​d​ ​y​o​u​r​ ​f​i​l​e​.​ ​ ​S​o​r​r​y​\​n​.​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​m​m​C​h​a​n​n​e​l​)​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​*​b​u​f​f​e​r​[​8​ ​*​ ​1​0​2​4​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​c​o​m​m​C​h​a​n​n​e​l​,​ ​"​H​T​T​P​/​1​.​0​ ​%​d​ ​O​K​\​n​"​,​ ​H​T​T​P​_​O​K​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​m​i​m​e​t​y​p​e​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​c​o​m​m​C​h​a​n​n​e​l​,​ ​"​C​o​n​t​e​n​t​-​T​y​p​e​:​ ​%​s​\​n​"​,​ ​m​i​m​e​t​y​p​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​c​o​m​m​C​h​a​n​n​e​l​,​ ​"​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​w​h​i​l​e​ ​(​(​r​e​s​u​l​t​ ​=​ ​f​r​e​a​d​ ​(​b​u​f​f​e​r​,​ ​1​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​,​ ​f​i​l​e​)​)​ ​>​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​w​r​i​t​e​ ​(​b​u​f​f​e​r​,​ ​1​,​ ​r​e​s​u​l​t​,​ ​c​o​m​m​C​h​a​n​n​e​l​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

}​ ​/​/​ ​r​e​t​u​r​n​F​i​l​e​

/​/​ ​u​s​i​n​g​ ​t​h​e​ ​m​e​t​h​o​d​ ​a​n​d​ ​t​h​e​ ​r​e​q​u​e​s​t​ ​(​t​h​e​ ​p​a​t​h​ ​p​a​r​t​ ​o​f​ ​t​h​e​ ​u​r​l​)​,​ ​g​e​n​e​r​a​t​e​ ​t​h​e​ ​d​a​t​a​
/​/​ ​f​o​r​ ​t​h​e​ ​u​s​e​r​ ​a​n​d​ ​s​e​n​d​ ​i​t​ ​b​a​c​k​.​

v​o​i​d​ ​h​a​n​d​l​e​R​e​q​u​e​s​t​ ​(​c​o​n​s​t​ ​c​h​a​r​ ​*​m​e​t​h​o​d​,​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​o​r​i​g​i​n​a​l​R​e​q​u​e​s​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​F​I​L​E​ ​*​c​o​m​m​C​h​a​n​n​e​l​)​ ​{​
 ​ ​ ​ ​c​h​a​r​ ​*​r​e​q​u​e​s​t​ ​=​ ​s​t​r​d​u​p​ ​(​o​r​i​g​i​n​a​l​R​e​q​u​e​s​t​)​;​ ​/​/​ ​w​e​'​l​l​ ​u​s​e​ ​s​t​r​s​e​p​ ​t​o​ ​s​p​l​i​t​ ​t​h​i​s​

 ​ ​ ​ ​i​f​ ​(​s​t​r​c​m​p​(​m​e​t​h​o​d​,​ ​"​G​E​T​"​)​ ​!​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​S​t​r​i​n​g​ ​(​H​T​T​P​_​E​R​R​O​R​,​ ​"​o​n​l​y​ ​G​E​T​s​ ​a​r​e​ ​s​u​p​p​o​r​t​e​d​"​,​ ​c​o​m​m​C​h​a​n​n​e​l​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​c​h​a​r​ ​*​n​e​x​t​S​t​r​i​n​g​ ​=​ ​r​e​q​u​e​s​t​;​
 ​ ​ ​ ​c​h​a​r​ ​*​c​h​u​n​k​;​
 ​ ​ ​ ​c​h​u​n​k​ ​=​ ​s​t​r​s​e​p​ ​(​&​n​e​x​t​S​t​r​i​n​g​,​ ​"​/​"​)​;​ ​ ​/​/​ ​u​r​l​s​ ​s​t​a​r​t​ ​w​i​t​h​ ​s​l​a​s​h​e​s​,​ ​s​o​ ​c​h​u​n​k​ ​i​s​ ​"​"​
 ​ ​ ​ ​c​h​u​n​k​ ​=​ ​s​t​r​s​e​p​ ​(​&​n​e​x​t​S​t​r​i​n​g​,​ ​"​/​"​)​;​ ​ ​/​/​ ​t​h​e​ ​l​e​a​d​i​n​g​ ​p​a​r​t​ ​o​f​ ​t​h​e​ ​u​r​l​

 ​ ​ ​ ​i​f​ ​(​s​t​r​c​m​p​(​c​h​u​n​k​,​ ​"​n​u​m​b​e​r​s​"​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​u​r​l​ ​o​f​ ​t​h​e​ ​f​o​r​m​ ​/​n​u​m​b​e​r​s​/​5​ ​t​o​ ​p​r​i​n​t​ ​n​u​m​b​e​r​s​ ​f​r​o​m​ ​1​ ​t​o​ ​5​
 ​ ​ ​ ​ ​ ​ ​ ​c​h​u​n​k​ ​=​ ​s​t​r​s​e​p​ ​(​&​n​e​x​t​S​t​r​i​n​g​,​ ​"​/​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​c​h​u​n​k​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​S​t​r​i​n​g​ ​(​H​T​T​P​_​E​R​R​O​R​,​ ​"​F​o​r​m​ ​i​s​ ​/​n​u​m​b​e​r​s​/​2​3​ ​-​ ​S​o​r​r​y​\​n​.​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​m​m​C​h​a​n​n​e​l​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​n​u​m​b​e​r​ ​=​ ​a​t​o​i​(​c​h​u​n​k​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​N​u​m​b​e​r​s​ ​(​n​u​m​b​e​r​,​ ​c​o​m​m​C​h​a​n​n​e​l​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​s​t​r​c​m​p​(​c​h​u​n​k​,​ ​"​f​i​l​e​"​)​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​h​u​n​k​ ​=​ ​s​t​r​s​e​p​ ​(​&​n​e​x​t​S​t​r​i​n​g​,​ ​"​"​)​;​ ​/​/​ ​g​e​t​ ​t​h​e​ ​r​e​s​t​ ​o​f​ ​t​h​e​ ​s​t​r​i​n​g​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​F​i​l​e​ ​(​c​h​u​n​k​,​ ​c​o​m​m​C​h​a​n​n​e​l​)​;​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​S​t​r​i​n​g​ ​(​H​T​T​P​_​N​O​T​_​F​O​U​N​D​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​h​a​n​d​l​e​ ​y​o​u​r​ ​r​e​q​u​e​s​t​.​ ​ ​S​o​r​r​y​\​n​.​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​m​m​C​h​a​n​n​e​l​)​;​
 ​ ​ ​ ​}​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​h​i​l​d​ ​%​l​d​ ​h​a​n​d​l​e​d​ ​r​e​q​u​e​s​t​ ​'​%​s​'​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​l​o​n​g​)​p​t​h​r​e​a​d​_​s​e​l​f​(​)​,​ ​o​r​i​g​i​n​a​l​R​e​q​u​e​s​t​)​;​

 ​ ​ ​ ​f​r​e​e​ ​(​r​e​q​u​e​s​t​)​;​

}​ ​/​/​ ​h​a​n​d​l​e​R​e​q​u​e​s​t​

/​/​ ​r​e​a​d​ ​t​h​e​ ​r​e​q​u​e​s​t​ ​f​r​o​m​ ​t​h​e​ ​b​r​o​w​s​e​r​,​ ​p​u​l​l​ ​a​p​a​r​t​ ​t​h​e​ ​e​l​e​m​e​n​t​s​ ​o​f​ ​t​h​e​
/​/​ ​r​e​q​u​e​s​t​,​ ​a​n​d​ ​t​h​e​n​ ​d​i​s​p​a​t​c​h​ ​i​t​.​

v​o​i​d​ ​d​i​s​p​a​t​c​h​R​e​q​u​e​s​t​ ​(​i​n​t​ ​f​d​,​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​*​a​d​d​r​e​s​s​)​ ​{​
#​d​e​f​i​n​e​ ​L​I​N​E​B​U​F​F​E​R​_​S​I​Z​E​ ​8​1​9​2​
 ​ ​ ​ ​c​h​a​r​ ​l​i​n​e​b​u​f​f​e​r​[​L​I​N​E​B​U​F​F​E​R​_​S​I​Z​E​]​;​
 ​ ​ ​ ​F​I​L​E​ ​*​c​o​m​m​C​h​a​n​n​e​l​;​

 ​ ​ ​ ​c​o​m​m​C​h​a​n​n​e​l​ ​=​ ​f​d​o​p​e​n​ ​(​f​d​,​ ​"​r​+​"​)​;​
 ​ ​ ​ ​i​f​ ​(​c​o​m​m​C​h​a​n​n​e​l​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​o​p​e​n​ ​c​o​m​m​C​h​a​n​n​e​l​.​ ​ ​E​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​t​h​i​s​ ​i​s​ ​p​r​e​t​t​y​ ​l​a​m​e​ ​i​n​ ​t​h​a​t​ ​i​t​ ​o​n​l​y​ ​r​e​a​d​s​ ​t​h​e​ ​f​i​r​s​t​ ​l​i​n​e​ ​a​n​d​
 ​ ​ ​ ​/​/​ ​a​s​s​u​m​e​s​ ​t​h​a​t​'​s​ ​t​h​e​ ​r​e​q​u​e​s​t​,​ ​s​u​b​s​e​q​u​e​n​t​l​y​ ​i​g​n​o​r​i​n​g​ ​a​n​y​ ​h​e​a​d​e​r​s​
 ​ ​ ​ ​/​/​ ​t​h​a​t​ ​m​i​g​h​t​ ​b​e​ ​s​e​n​t​.​

 ​ ​ ​ ​i​f​ ​(​f​g​e​t​s​(​l​i​n​e​b​u​f​f​e​r​,​ ​L​I​N​E​B​U​F​F​E​R​_​S​I​Z​E​,​ ​c​o​m​m​C​h​a​n​n​e​l​)​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​o​k​,​ ​n​o​w​ ​f​i​g​u​r​e​ ​o​u​t​ ​w​h​a​t​ ​t​h​e​y​ ​w​a​n​t​e​d​
 ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​*​r​e​q​u​e​s​t​E​l​e​m​e​n​t​s​[​3​]​,​ ​*​n​e​x​t​S​t​r​i​n​g​,​ ​*​c​h​u​n​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​i​ ​=​ ​0​;​
 ​ ​ ​ ​ ​ ​ ​ ​n​e​x​t​S​t​r​i​n​g​ ​=​ ​l​i​n​e​b​u​f​f​e​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​w​h​i​l​e​ ​(​(​c​h​u​n​k​ ​=​ ​s​t​r​s​e​p​ ​(​&​n​e​x​t​S​t​r​i​n​g​,​ ​"​ ​"​)​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​q​u​e​s​t​E​l​e​m​e​n​t​s​[​i​]​ ​=​ ​c​h​u​n​k​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​+​+​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​i​ ​!​=​ ​3​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​S​t​r​i​n​g​ ​(​H​T​T​P​_​E​R​R​O​R​,​ ​"​m​a​l​f​o​r​m​e​d​ ​r​e​q​u​e​s​t​"​,​ ​c​o​m​m​C​h​a​n​n​e​l​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​h​a​n​d​l​e​R​e​q​u​e​s​t​ ​(​r​e​q​u​e​s​t​E​l​e​m​e​n​t​s​[​0​]​,​ ​r​e​q​u​e​s​t​E​l​e​m​e​n​t​s​[​1​]​,​ ​c​o​m​m​C​h​a​n​n​e​l​)​;​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​r​e​a​d​ ​a​n​ ​e​m​p​t​y​ ​r​e​q​u​e​s​t​.​ ​ ​e​x​i​t​i​n​g​\​n​"​)​;​
 ​ ​ ​ ​}​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​f​c​l​o​s​e​ ​(​c​o​m​m​C​h​a​n​n​e​l​)​;​

}​ ​/​/​ ​d​i​s​p​a​t​c​h​R​e​q​u​e​s​t​

/​/​ ​S​i​t​ ​b​l​o​c​k​i​n​g​ ​o​n​ ​a​c​c​e​p​t​ ​u​n​t​i​l​ ​a​ ​n​e​w​ ​c​o​n​n​e​c​t​i​o​n​ ​c​o​m​e​s​ ​i​n​.​ ​ ​Q​u​e​u​e​ ​t​h​e​
/​/​ ​c​o​n​n​e​c​t​i​o​n​ ​(​w​h​i​c​h​ ​s​h​o​u​l​d​ ​e​v​e​n​t​u​a​l​l​y​ ​w​a​k​e​ ​u​p​ ​a​ ​c​o​n​n​e​c​t​i​o​n​ ​t​h​r​e​a​d​ ​t​o​
/​/​ ​h​a​n​d​l​e​ ​i​t​

v​o​i​d​ ​a​c​c​e​p​t​R​e​q​u​e​s​t​ ​(​i​n​t​ ​l​i​s​t​e​n​S​o​c​k​e​t​)​ ​{​
 ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​a​d​d​r​e​s​s​;​
 ​ ​ ​ ​s​o​c​k​l​e​n​_​t​ ​a​d​d​r​e​s​s​L​e​n​g​t​h​ ​=​ ​s​i​z​e​o​f​(​a​d​d​r​e​s​s​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​b​e​f​o​r​e​ ​a​c​c​e​p​t​\​n​"​)​;​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​a​c​c​e​p​t​ ​(​l​i​s​t​e​n​S​o​c​k​e​t​,​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​ ​*​)​&​a​d​d​r​e​s​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​&​a​d​d​r​e​s​s​L​e​n​g​t​h​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​a​f​t​e​r​ ​a​c​c​e​p​t​\​n​"​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​a​c​c​e​p​t​ ​f​a​i​l​e​d​.​ ​ ​e​r​r​o​r​:​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​i​n​t​ ​f​d​ ​=​ ​r​e​s​u​l​t​;​

 ​ ​ ​ ​q​u​e​u​e​R​e​q​u​e​s​t​ ​(​f​d​,​ ​&​a​d​d​r​e​s​s​)​;​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​r​e​t​u​r​n​;​

}​ ​/​/​ ​a​c​c​e​p​t​R​e​q​u​e​s​t​

/​/​ ​-​-​-​-​-​ ​n​e​t​w​o​r​k​ ​s​t​u​f​f​

/​/​ ​s​t​a​r​t​ ​l​i​s​t​e​n​i​n​g​ ​o​n​ ​o​u​r​ ​s​e​r​v​e​r​ ​p​o​r​t​

i​n​t​ ​s​t​a​r​t​L​i​s​t​e​n​i​n​g​ ​(​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​f​d​ ​=​ ​-​1​,​ ​s​u​c​c​e​s​s​ ​=​ ​0​;​

 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​s​o​c​k​e​t​ ​(​A​F​_​I​N​E​T​,​ ​S​O​C​K​_​S​T​R​E​A​M​,​ ​0​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​m​a​k​e​ ​a​ ​s​c​o​k​e​t​.​ ​ ​e​r​r​o​r​:​ ​%​d​ ​/​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​f​d​ ​=​ ​r​e​s​u​l​t​;​

 ​ ​ ​ ​i​n​t​ ​y​e​s​ ​=​ ​1​;​
 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​s​e​t​s​o​c​k​o​p​t​ ​(​f​d​,​ ​S​O​L​_​S​O​C​K​E​T​,​ ​S​O​_​R​E​U​S​E​A​D​D​R​,​ ​&​y​e​s​,​ ​s​i​z​e​o​f​(​i​n​t​)​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​s​e​t​s​o​c​k​o​p​t​ ​t​o​ ​r​e​u​s​e​ ​a​d​d​r​e​s​s​.​ ​%​d​ ​/​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​b​i​n​d​ ​t​o​ ​a​n​ ​a​d​d​r​e​s​s​ ​a​n​d​ ​p​o​r​t​
 ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​a​d​d​r​e​s​s​;​
 ​ ​ ​ ​a​d​d​r​e​s​s​.​s​i​n​_​l​e​n​ ​=​ ​s​i​z​e​o​f​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​)​;​
 ​ ​ ​ ​a​d​d​r​e​s​s​.​s​i​n​_​f​a​m​i​l​y​ ​=​ ​A​F​_​I​N​E​T​;​
 ​ ​ ​ ​a​d​d​r​e​s​s​.​s​i​n​_​p​o​r​t​ ​=​ ​h​t​o​n​s​ ​(​P​O​R​T​_​N​U​M​B​E​R​)​;​
 ​ ​ ​ ​a​d​d​r​e​s​s​.​s​i​n​_​a​d​d​r​.​s​_​a​d​d​r​ ​=​ ​h​t​o​n​l​ ​(​I​N​A​D​D​R​_​A​N​Y​)​;​
 ​ ​ ​ ​m​e​m​s​e​t​ ​(​a​d​d​r​e​s​s​.​s​i​n​_​z​e​r​o​,​ ​0​,​ ​s​i​z​e​o​f​(​a​d​d​r​e​s​s​.​s​i​n​_​z​e​r​o​)​)​;​

 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​b​i​n​d​ ​(​f​d​,​ ​(​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​ ​*​)​&​a​d​d​r​e​s​s​,​ ​s​i​z​e​o​f​(​a​d​d​r​e​s​s​)​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​b​i​n​d​ ​s​o​c​k​e​t​.​ ​ ​e​r​r​o​r​:​ ​%​d​ ​/​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​l​i​s​t​e​n​ ​(​f​d​,​ ​8​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​l​i​s​t​e​n​ ​f​a​i​l​e​d​.​ ​ ​e​r​r​o​r​:​ ​%​d​ ​/​ ​ ​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​s​u​c​c​e​s​s​ ​=​ ​1​;​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​i​f​ ​(​!​s​u​c​c​e​s​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​l​o​s​e​ ​(​f​d​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​d​ ​=​ ​-​1​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​f​d​;​

}​ ​/​/​ ​s​t​a​r​t​L​i​s​t​e​n​i​n​g​

/​/​ ​-​-​-​-​-​ ​t​h​r​e​a​d​ ​f​u​n​c​t​i​o​n​s​

/​/​ ​t​h​e​r​e​'​s​ ​j​u​s​t​ ​o​n​e​ ​o​f​ ​t​h​e​s​e​.​ ​I​t​'​s​ ​t​h​e​ ​p​r​o​d​u​c​e​r​ ​o​f​ ​n​e​w​ ​r​e​q​u​e​s​t​s​
v​o​i​d​ ​*​a​c​c​e​p​t​T​h​r​e​a​d​ ​(​v​o​i​d​ ​*​a​r​g​u​m​e​n​t​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​l​i​s​t​e​n​S​o​c​k​e​t​ ​=​ ​*​(​(​i​n​t​ ​*​)​a​r​g​u​m​e​n​t​)​;​

 ​ ​ ​ ​w​h​i​l​e​ ​(​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​a​c​c​e​p​t​R​e​q​u​e​s​t​ ​(​l​i​s​t​e​n​S​o​c​k​e​t​)​;​
 ​ ​ ​ ​}​

}​ ​/​/​ ​a​c​c​e​p​t​T​h​r​e​a​d​

/​/​ ​T​h​e​r​e​'​s​ ​N​ ​o​f​ ​t​h​e​s​e​ ​t​o​ ​h​a​n​d​l​e​ ​r​e​q​u​e​s​t​s​.​
v​o​i​d​ ​*​r​e​q​u​e​s​t​T​h​r​e​a​d​ ​(​v​o​i​d​ ​*​a​r​g​u​m​e​n​t​)​ ​{​
 ​ ​ ​ ​/​/​ ​S​p​i​n​ ​o​u​t​ ​o​n​ ​o​u​r​ ​o​w​n​.​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​p​t​h​r​e​a​d​_​d​e​t​a​c​h​ ​(​p​t​h​r​e​a​d​_​s​e​l​f​(​)​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​!​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​d​e​t​a​c​h​ ​c​o​n​n​e​c​t​i​o​n​ ​t​h​r​e​a​d​.​ ​ ​e​r​r​o​r​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​,​ ​s​t​r​e​r​r​o​r​(​r​e​s​u​l​t​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​N​U​L​L​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​w​h​i​l​e​ ​(​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​f​d​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​u​c​t​ ​s​o​c​k​a​d​d​r​_​i​n​ ​a​d​d​r​e​s​s​;​

 ​ ​ ​ ​ ​ ​ ​ ​g​e​t​R​e​q​u​e​s​t​ ​(​&​f​d​,​ ​&​a​d​d​r​e​s​s​)​;​ ​/​/​ ​T​h​i​s​ ​w​i​l​l​ ​b​l​o​c​k​ ​u​n​t​i​l​ ​a​ ​r​e​q​u​e​s​t​ ​i​s​ ​q​u​e​u​e​d​
 ​ ​ ​ ​ ​ ​ ​ ​d​i​s​p​a​t​c​h​R​e​q​u​e​s​t​ ​(​f​d​,​ ​&​a​d​d​r​e​s​s​)​;​
 ​ ​ ​ ​}​

}​ ​/​/​ ​r​e​q​u​e​s​t​T​h​r​e​a​d​

/​/​ ​-​-​-​-​-​ ​g​e​t​ ​t​h​i​n​g​s​ ​s​t​a​r​t​e​d​ ​i​n​ ​m​a​i​n​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​p​t​h​r​e​a​d​_​t​ ​a​c​c​e​p​t​T​h​r​e​a​d​I​D​;​
 ​ ​ ​ ​i​n​t​ ​s​t​a​t​u​s​ ​=​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​

 ​ ​ ​ ​i​n​t​ ​l​i​s​t​e​n​S​o​c​k​e​t​ ​=​ ​s​t​a​r​t​L​i​s​t​e​n​i​n​g​ ​(​)​;​

 ​ ​ ​ ​i​f​ ​(​l​i​s​t​e​n​S​o​c​k​e​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​s​t​a​r​t​L​i​s​t​e​n​i​n​g​ ​f​a​i​l​e​d​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​B​l​o​c​k​ ​S​I​G​P​I​P​E​ ​s​o​ ​w​e​ ​d​o​n​'​t​ ​c​r​o​a​k​ ​i​f​ ​w​e​ ​t​r​y​ ​w​r​i​t​i​n​g​ ​t​o​ ​a​ ​c​l​o​s​e​d​ ​c​o​n​n​e​c​t​i​o​n​
 ​ ​ ​ ​i​f​ ​(​s​i​g​n​a​l​ ​(​S​I​G​P​I​P​E​,​ ​S​I​G​_​I​G​N​)​ ​=​=​ ​S​I​G​_​E​R​R​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​i​g​n​o​r​e​ ​S​I​G​P​I​P​E​.​ ​ ​e​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​S​t​a​r​t​ ​t​h​e​ ​a​c​c​e​p​t​ ​t​h​r​e​a​d​
 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​p​t​h​r​e​a​d​_​c​r​e​a​t​e​ ​(​&​a​c​c​e​p​t​T​h​r​e​a​d​I​D​,​ ​N​U​L​L​,​ ​a​c​c​e​p​t​T​h​r​e​a​d​,​ ​&​l​i​s​t​e​n​S​o​c​k​e​t​)​;​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​!​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​p​t​h​r​e​a​d​_​*​ ​d​o​e​s​n​'​t​ ​u​s​e​ ​e​r​r​n​o​ ​:​-​|​
 ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​c​r​e​a​t​e​ ​a​c​c​e​p​t​ ​t​h​r​e​a​d​.​ ​ ​e​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​,​ ​s​t​r​e​r​r​o​r​(​r​e​s​u​l​t​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​S​t​a​r​t​ ​t​h​e​ ​c​o​n​n​e​c​t​i​o​n​ ​t​h​r​e​a​d​s​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​M​A​X​_​T​H​R​E​A​D​S​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​t​h​r​e​a​d​_​t​ ​c​o​n​n​T​h​r​e​a​d​I​D​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​p​t​h​r​e​a​d​_​c​r​e​a​t​e​ ​(​&​c​o​n​n​T​h​r​e​a​d​I​D​,​ ​N​U​L​L​,​ ​r​e​q​u​e​s​t​T​h​r​e​a​d​,​ ​N​U​L​L​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​!​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​p​r​i​n​t​f​ ​(​s​t​d​e​r​r​,​ ​"​c​o​u​l​d​ ​n​o​t​ ​c​r​e​a​t​e​ ​c​o​n​n​e​c​t​i​o​n​ ​t​h​r​e​a​d​.​ ​ ​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​e​r​r​o​r​ ​i​s​ ​%​d​/​%​s​\​n​"​,​ ​r​e​s​u​l​t​,​ ​s​t​r​e​r​r​o​r​(​r​e​s​u​l​t​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​p​t​h​r​e​a​d​_​j​o​i​n​ ​(​a​c​c​e​p​t​T​h​r​e​a​d​I​D​,​ ​N​U​L​L​)​;​

 ​ ​ ​ ​s​t​a​t​u​s​ ​=​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​
 ​ ​b​a​i​l​o​u​t​:​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​t​a​t​u​s​;​

}​ ​/​/​ ​m​a​i​n​

Compile and run the program. You can use telnet to communicate with the server, or you can fire up a web browser and use URLs like http://localhost:8080/file/webserve-thread.m to return a file or http://localhost:8080/numbers/37 to see the numbers. Safari does some aggressive caching of data before displaying, so you may not see the individual numbers being displayed until they are all generated. Other browsers, like FireFox or Camino, will show the numbers as they are generated.

Cocoa and Threading

You can use multiple threads in a Cocoa program. As usual, Cocoa brings a nice set of clean APIs that provide the threading features. All of the caveats above regarding race conditions and performance apply when using Cocoa, as well as some additional gotchas.

NSThread

NSThread is the class that abstracts threads. To create a new thread, use the class method

+ (void)detachNewThreadSelector: (SEL) aSelector
                       toTarget: (id) aTarget
                     withObject: (id) anArgument;

aSelector is a selector that describes a method aTarget can receive. aSelector's signature is

- (void) aSelector: (id) anArgument;

anArgument is what gets passed to this method. This call works just like pthread_create() in that the thread starts executing with the first instruction of aSelector and the thread terminates when the method exits. This is a daemon thread, so there is no need to do any kind of waiting for it to finish.

When the first NSThread object is created, the NSThread class posts an NSWillBecomeMultiThreadedNotification. Afterward, calls to [NSThread isMultiThreaded] will return YES. You can use this call and the notification to decide whether you need to use synchronization for your data structures. If you are single threaded, using synchronization will just slow you down for zero benefit. pthread_create() does not post this notification nor does it cause -isMultiThreaded to return YES, so be aware of this if you mix pthreads and NSThread.

When you create an NSThread, you also get an NSRunLoop that you can use for event handling and Distributed Objects operations. If you are going to be using any Cocoa calls in the thread, you should create an NSAutoreleasePool. NSApplication’s +detachDrawingThread:toTarget:withObject: method is a convenience for making a new thread and setting up an autorelease pool. And you are not constrained to only doing drawing in that thread.

The NSLock class behaves very much like a pthread_mutex_t. You can lock and unlock it, try the lock, and have a lock timeout. Likewise, NSConditionLock fills the role of pthread_cond_t. The Cocoa condition lock API hides the loop that you had to use when using pthread_cond_wait(). Instead, it is a little state machine that keeps track of what state it is in (all programmer definable), and you can tell it to wait until a particular state occurs, as well as tell it what state to move to.

Prior to OS X 10.2, child threads could not draw into Cocoa views. Jaguar added this ability. You need to call -(BOOL)lockFocusIfCanDraw before doing your drawing. You also have to explicitly flush the window before the window server will show the updated contents:

i​f​ ​(​[​d​r​a​w​V​i​e​w​ ​l​o​c​k​F​o​c​u​s​I​f​C​a​n​D​r​a​w​]​)​ ​{​
 ​ ​ ​ ​/​/​ ​s​e​t​ ​c​o​l​o​r​s​,​ ​u​s​e​ ​N​S​B​e​z​i​e​r​P​a​t​h​ ​a​n​d​ ​N​S​S​t​r​i​n​g​ ​d​r​a​w​i​n​g​ ​f​u​n​c​t​i​o​n​s​
 ​ ​ ​ ​[​[​d​r​a​w​V​i​e​w​ ​w​i​n​d​o​w​]​ ​f​l​u​s​h​W​i​n​d​o​w​]​;​
 ​ ​ ​ ​[​d​r​a​w​V​i​e​w​ ​u​n​l​o​c​k​F​o​c​u​s​]​;​
}​

Cocoa and thread safety

Parts of the Foundation and AppKit frameworks are thread safe, and some are not. In general, immutable objects are thread safe and can be used by multiple threads. Mutable objects are not thread safe and should not be used by multiple threads. You have to be careful about how you create an object before making assumptions about the mutability of an object – for instance, consider a method that takes an NSString. NSStrings are generally immutable. Given inheritance though, a caller can legally create an NSMutableString and give it to a method that takes an NSString. In the same way, immutable containers are safe to share amongst threads, but mutable containers are not.

User interface objects are not thread safe and should only be used on the main thread. In general, you should assume that an object is not thread safe unless it is explicitly documented as such.

Objective-C @synchronized blocks

When you tell the compiler to use native Objective-C exceptions, whether with the checkbox in Xcode or by giving the compiler the -fobjc-exceptions flag, you also get to use the Objective-C thread synchronization operator.

The @synchronized() directive locks a section of code that operates on a particular object. Only a single thread can manipulate that object in this chunk of code at a time. Other threads using the same object will be blocked until the @synchronized block is exited.

@synchronized() takes a single argument, which can be any Objective-C object. You can use an object that is stored in an instance variable, you can use self, or you can even use a class object. If you are using @synchronized(self) inside of a class method, you will be @synchronized with the class object. @synchronized(someObject) acts like a recursive mutex, so a thread can use the same object in different blocks that are synchronized with the same object. Exceptions thrown from within a @synchronized() block will automatically release the lock.

For the More Curious: Thread Local Storage

Sometimes it is very handy to have storage that is private to a thread. In the basics.m program above, you could stash the contents of the ThreadInfo stack into thread local storage instead of on the function call stack. Thread local storage behaves like global variables but are private to the thread. errno is stored in thread local storage so that every thread has its own errno.

To use thread local storage, initialize a pthread_key_t using pthread_key_create():

int pthread_key_create (pthread_key_t *key, void (*destructor)(void *));

This creates an abstract key. Usually you name a variable with a descriptive name or associate this key with a descriptive string in a dictionary, as shown in Figure 20.5. The destructor function is called when the thread exits, so that dynamic memory (or other resources) can be cleaned up.

Set a thread-local value by using pthread_setspecific():

int pthread_setspecific (pthread_key_t key, const void *value);

and get the value by using pthread_getspecific():

void *pthread_getspecific (pthread_key_t key);

Figure 20.5  Thread local storage

If you are using NSThread, you can use

- (NSMutableDictionary *) threadDictionary

To put stuff into thread local storage, just store stuff in the dictionary. This is an NSThread instance method, so you need to find your current NSThread object by using [NSThread currentThread].

For the More Curious: Read/Write Locks

Both Cocoa and pthreads provide read/write locks. These allow a data structure to be read by a number of readers simultaneously but only allow one thread write access at a time.

pthread_rwlock_t works just like pthread_mutex_t but with a couple of extra calls. Here are the details:

pthread_rwlock_init (pthread_rwlock_t *lock,
                     const pthread_rwlockattr_t *attr);

OS X does not define PTHREAD_RWLOCK_INITIALIZER, so you cannot create them statically.

int pthread_rwlock_destroy (pthread_rwlock_t *lock);

Rather than use lock(), you specify which kind of locking you want:

int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock);

int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock);

int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock);

int pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock);

And unlock by using

int pthread_rwlock_unlock (pthread_rwlock_t *rwlock);

In general, using read/write locks can be a bad idea because it doubles the number of actual lock operations. Read/write locks can be implemented with a mutex and two condition variables. Unless you have some really expensive data structures, the overhead of the read/write lock operations will be more than the operation you are protecting. Given that caveat, there are some times when they are quite useful, especially when there are a lot of readers, not many modifications, and accessing the data structure is time consuming.

Exercises

Use conditions to implement the producer-consumer problem. One thread should generate integers in the Fibonacci sequence; another should print those numbers. Use sleep() and ifdefs to vary the rate at which the two threads work, so that, depending on defined constants, either the producer or consumer ends up having to wait for the other thread.

Tweak basics.m to use Thread Local Storage to store the index, number to count to, and the sleep time.

Make nodepool.m from Chapter 7: Memory thread safe so that multiple threads can allocate stuff out of the same pool.

21
Operations

As you saw in Chapter 10: Performance Tuning, multiple processors in a single computer are a fact of life today. CPU manufacturers cannot scale processor speed as they have in the past, so they are putting their transistors into more CPU cores. Getting the best performance out of today’s systems requires parallel programming, which usually means using threads. Threaded programming is difficult and error prone, making it harder and more expensive to take full advantage of the processing power of today’s systems

Mac OS X 10.5 introduced some classes that simplify some kinds of parallel programming. The NSOperation class represents a unit of parallelizable work, and the NSOperationQueue class takes NSOperations and executes them. Even though “queue” implies first-in, first-out execution of operations, NSOperationQueue is more flexible because you can specify dependencies between operations. One operation can wait until other operations are done. You can also specify operation priorities, allowing one kind of operation to always take precedence over other kinds of operations.

In Mac OS X 10.5, there are two main kinds of operations, concurrent and non-concurrent. Non-concurrent operations cause an NSOperationQueue to run the operation in its own thread, while multiple concurrent operations can be active in a single thread.

This nomenclature is a bit backwards from the point of view of someone creating a new operation, but it makes sense from the point of view of the NSOperationQueue. The queue runs NSOperations. If an operation wants a thread all to itself, it cannot run concurrently with any NSOperation on that same thread; hence, it is a non-concurrent operation. Similarly, if an operation is fine with having multiple operations running at the same time (perhaps they all are doing asynchronous I/O), then they are run concurrently all on the same thread.

In Mac OS X 10.6, all operations are non-concurrent. In fact, the concurrency flag is ignored by NSOperationQueue. Every operation will be started in its own thread. This can be a surprising gotcha if you have concurrent operations that you wrote under Leopard being compiled into a 10.6-and-beyond application. iOS 4 has the Mac OS X 10.6 behavior.

Aside from the concurrent and non-concurrent distinction, there are, for lack of a better term, simple-lifetime and complex-lifetime operations. Simple-lifetime operations override -main, do their work, and then fall out the bottom of the method. Once -main returns, the operation queue assumes that your operation has completed and will schedule the next one to run. For Mac OS X 10.5, all non-concurrent operations (each getting its own thread) are simple operations.

Complex-lifetime operations let you control the entire lifetime of the operation. Instead of overriding -main you override -start. You can exit from -start, but the operation will still be considered to be running. Operations have a KVO component, and with complex-lifetime operations you are responsible for maintaining the KVO contracts. Details of implementing complex-lifetime operations will be discussed later in this chapter. For Mac OS X 10.5, all concurrent operations (multiple operations sharing a thread) are complex-lifetime.

Simple-Lifetime Operations

If you have synchronous work to do, like a large computation, you will want to use a simple-lifetime operation. This is what you get by default when you subclass NSOperation and override -main.

- (void) main;

Do your work, and when you are done, just return from the method. That is enough to tell the NSOperationQueue that ran the operation that the operation has completed its work.

Operations can be cancelled at any time, so you should poll -isCancelled when convenient:

- (BOOL) isCancelled;

The NSOperation form of cancellation differs from the cancellation provided by the pthread API. pthread cancellation will terminate a thread, and is a tricky game to play because the thread may be in the middle of manipulating some global data structure, like in malloc(), when it is cancelled. NSOperation does not use thread cancellation, instead it relies on the operation to notice whether it has been cancelled and do any tear down of its environment. You should also check -isCancelled first thing when your -main method is run. NSOperationQueue will always run its operations, even if they have been cancelled.

You can add and remove dependencies between operations:

- (void) addDependency: (NSOperation *) op;
- (void) removeDependency: (NSOperation *) op;

Circular dependencies are not supported and are not detected. NSOperationQueue is the entity that actually honors the dependencies, so there is nothing preventing you from manually running an NSOperation before its dependencies run. If you want to see all the dependencies a particular operation has, you can look at its dependencies array:

- (NSArray *) dependencies;

You can set and get the relative priority of an operation by manipulating its queue priority:

- (NSOperationQueuePriority) queuePriority;
- (void) setQueuePriority: (NSOperationQueuePriority) p;

where NSOperationQueuePriority is one of:

e​n​u​m​ ​{​
 ​ ​ ​ ​N​S​O​p​e​r​a​t​i​o​n​Q​u​e​u​e​P​r​i​o​r​i​t​y​V​e​r​y​L​o​w​,​
 ​ ​ ​ ​N​S​O​p​e​r​a​t​i​o​n​Q​u​e​u​e​P​r​i​o​r​i​t​y​L​o​w​,​
 ​ ​ ​ ​N​S​O​p​e​r​a​t​i​o​n​Q​u​e​u​e​P​r​i​o​r​i​t​y​N​o​r​m​a​l​,​
 ​ ​ ​ ​N​S​O​p​e​r​a​t​i​o​n​Q​u​e​u​e​P​r​i​o​r​i​t​y​H​i​g​h​,​
 ​ ​ ​ ​N​S​O​p​e​r​a​t​i​o​n​Q​u​e​u​e​P​r​i​o​r​i​t​y​V​e​r​y​H​i​g​h​
}​;​

Changing an operation’s priority does not impact the operating system scheduler, so setting a Very High priority will not cause your operation to be allocated more CPU time. This value is only used by an NSOperationQueue to decide which operation to run next.

NSOperationQueue

NSOperationQueue is a class that accepts NSOperation instances and runs them. On Mac OS X 10.5, the queue will run the operation in the current thread if the operation says that it is a concurrent operation. If the operation is non-concurrent, or you are on 10.6, the queue will create and manage threads automatically.

Add an operation to an NSOperationQueue with -addOperation:

- (void) addOperation: (NSOperation *) op;

-operations returns the current set of operations that are currently running or that are waiting:

- (NSArray *) operations;

By default, an NSOperationQueue will choose the number of concurrent threads it manages based on the number of processors, as well as vague “other factors” mentioned in Apple’s documentation. You can manually tweak and query the concurrency factor with these calls:

- (NSInteger) maxConcurrentOperationCount;
- (void) setMaxConcurrentOperationCount: (NSInteger) count;

If you decide to abandon the work a queue is doing, you can tell it to cancel all of the operations:

- (void) cancelAllOperations;

You can wait synchronously for all operations to complete:

- (void) waitUntilAllOperationsAreFinished;

Threading issues

Because non-concurrent operations run on their own thread, you need to be aware of the thread safety issues of any API that you are using in your operation. For example, you cannot safely set an NSTextField’s value from another thread because user interface manipulations must be done on the main thread.

One subtle point to remember is that the current thread is used when posting an NSNotification or when handing a KVO observer notification. If you plan on using an NSNotification or KVO to notify other objects that an operation has completed, you need to be aware that the other object’s notification methods will run in the same thread that ran the operation and not the main thread, which unfortunately is often not what you want to happen. You can use one of the -performSelectorOnMainThread: family of methods to trigger a notification on the main thread.

MandelOpper

The Mandelbrot set is a favorite graphical pastime amongst many programmers. It is a fractal geometry construct that can generate really cool images. Generating the Mandelbrot set is a computationally intensive process, which lends itself to parallel processing. The MandelOpper application will calculate a Mandelbrot set and display it in a window, using instances of a simple-lifetime NSOperation class to do the actual calculation (Figure 21.1).

Figure 21.1  MandelOpper screen being filled in

Figure 21.2 shows the general class layout. MandelOpperAppDelegate is your typical Cocoa application controller, coordinating the user interface and initiating the Mandelbrot calculations. The Bitmap class holds a bucket of bytes that the CalcOperation operations will write their pixel colors into. Each CalcOperation will calculate one row of pixels. The BitmapView displays the contents of a Bitmap.

Figure 21.2  MandelOpper classes

Bitmap

Bitmap wraps an array of bytes, gives callers the starting address for a particular row of pixels, and generates an image representation suitable for drawing. The pixels are 4-byte values, in the form RGBA, one byte for the red, green, blue, and alpha (transparency) components.

Create a new Cocoa Application in Xcode and name it MandelOpper. Create new source files for Bitmap.h and Bitmap.m. Here is Bitmap.h:

/​/​ ​B​i​t​m​a​p​.​h​ ​-​-​ ​W​r​a​p​ ​a​ ​b​u​c​k​e​t​ ​o​f​ ​4​-​b​y​t​e​ ​p​i​x​e​l​s​,​ ​w​i​t​h​ ​r​e​a​d​y​ ​a​c​c​e​s​s​ ​t​o​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​h​e​ ​b​a​s​e​ ​a​d​d​r​e​s​s​ ​o​f​ ​i​n​d​i​v​i​d​u​a​l​ ​s​c​a​n​l​i​n​e​s​ ​a​n​d​ ​e​a​s​y​ ​i​m​a​g​e​ ​r​e​p​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​r​e​a​t​i​o​n​.​

#​i​m​p​o​r​t​ ​<​C​o​c​o​a​/​C​o​c​o​a​.​h​>​

/​/​ ​T​h​e​ ​b​u​f​f​e​r​ ​i​s​ ​c​o​m​p​o​s​e​d​ ​o​f​ ​a​ ​l​i​n​e​a​r​ ​a​r​r​a​y​ ​o​f​ ​3​2​-​b​i​t​ ​e​l​e​m​e​n​t​s​,​ ​o​r​d​e​r​e​d​ ​R​G​B​A​.​

@​i​n​t​e​r​f​a​c​e​ ​B​i​t​m​a​p​ ​:​ ​N​S​O​b​j​e​c​t​ ​{​
 ​ ​ ​ ​N​S​U​I​n​t​e​g​e​r​ ​_​w​i​d​t​h​;​ ​ ​ ​ ​ ​ ​ ​/​/​ ​n​u​m​b​e​r​ ​o​f​ ​p​i​x​e​l​s​ ​p​e​r​ ​l​i​n​e​.​
 ​ ​ ​ ​N​S​U​I​n​t​e​g​e​r​ ​_​h​e​i​g​h​t​;​ ​ ​ ​ ​ ​ ​/​/​ ​n​u​m​b​e​r​ ​o​f​ ​l​i​n​e​s​.​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​c​h​a​r​ ​*​_​b​u​f​f​e​r​;​ ​ ​/​/​ ​A​ ​b​u​c​k​e​t​ ​o​f​ ​b​y​t​e​s​.​
}​

@​p​r​o​p​e​r​t​y​ ​(​r​e​a​d​o​n​l​y​)​ ​N​S​U​I​n​t​e​g​e​r​ ​w​i​d​t​h​;​
@​p​r​o​p​e​r​t​y​ ​(​r​e​a​d​o​n​l​y​)​ ​N​S​U​I​n​t​e​g​e​r​ ​h​e​i​g​h​t​;​

/​/​ ​M​a​k​e​ ​a​ ​n​e​w​ ​b​i​t​m​a​p​ ​w​i​t​h​ ​t​h​e​ ​g​i​v​e​n​ ​w​i​d​t​h​ ​a​n​d​ ​h​e​i​g​h​t​.​
-​ ​(​i​d​)​ ​i​n​i​t​W​i​t​h​W​i​d​t​h​:​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​w​i​d​t​h​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​i​g​h​t​:​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​h​e​i​g​h​t​;​

/​/​ ​R​e​t​u​r​n​s​ ​a​n​ ​i​m​a​g​e​ ​r​e​p​r​e​s​e​n​t​a​t​i​o​n​ ​s​u​i​t​a​b​l​e​ ​f​o​r​ ​d​r​a​w​i​n​g​.​
-​ ​(​N​S​B​i​t​m​a​p​I​m​a​g​e​R​e​p​ ​*​)​ ​i​m​a​g​e​R​e​p​;​

/​/​ ​G​e​t​ ​t​h​e​ ​l​o​c​a​t​i​o​n​ ​i​n​ ​m​e​m​o​r​y​ ​f​o​r​ ​a​ ​p​a​r​t​i​c​u​l​a​r​ ​s​c​a​n​l​i​n​e​ ​i​n​ ​t​h​e​ ​b​i​t​m​a​p​.​
/​/​ ​Y​o​u​ ​c​a​n​ ​s​a​f​e​l​y​ ​t​r​a​v​e​l​ ​w​i​d​t​h​ ​*​ ​4​ ​b​y​t​e​s​ ​a​l​o​n​g​ ​i​t​s​ ​p​a​t​h​ ​w​i​t​h​o​u​t​ ​a​ ​l​o​c​k​.​
-​ ​(​u​n​s​i​g​n​e​d​ ​c​h​a​r​ ​*​)​ ​b​a​s​e​A​d​d​r​e​s​s​F​o​r​L​i​n​e​:​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​l​i​n​e​;​

@​e​n​d​ ​/​/​ ​B​i​t​m​a​p​

Now edit Bitmap.m and add the initializer, basic accessors, and cleanup methods. The bitmap’s memory is initialized to 0x55, giving all the pixels a semi-transparent gray color.

#​i​m​p​o​r​t​ ​"​B​i​t​m​a​p​.​h​"​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​B​i​t​m​a​p​

@​s​y​n​t​h​e​s​i​z​e​ ​w​i​d​t​h​ ​=​ ​_​w​i​d​t​h​;​
@​s​y​n​t​h​e​s​i​z​e​ ​h​e​i​g​h​t​ ​=​ ​_​h​e​i​g​h​t​;​

-​ ​(​i​d​)​ ​i​n​i​t​W​i​t​h​W​i​d​t​h​:​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​w​i​d​t​h​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​e​i​g​h​t​:​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​h​e​i​g​h​t​ ​{​

 ​ ​ ​ ​i​f​ ​(​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​_​w​i​d​t​h​ ​=​ ​w​i​d​t​h​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​h​e​i​g​h​t​ ​=​ ​h​e​i​g​h​t​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​G​e​t​ ​a​ ​c​h​u​n​k​ ​o​f​ ​m​e​m​o​r​y​,​ ​t​h​e​n​ ​f​i​l​l​ ​i​t​ ​w​i​t​h​ ​g​r​a​y​.​
 ​ ​ ​ ​ ​ ​ ​ ​_​b​u​f​f​e​r​ ​=​ ​m​a​l​l​o​c​ ​(​_​w​i​d​t​h​ ​*​ ​_​h​e​i​g​h​t​ ​*​ ​4​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​F​i​l​l​ ​w​i​t​h​ ​a​ ​s​e​m​i​t​r​a​n​s​p​a​r​e​n​t​ ​g​r​a​y​ ​c​o​l​o​r​.​
 ​ ​ ​ ​ ​ ​ ​ ​m​e​m​s​e​t​ ​(​_​b​u​f​f​e​r​,​ ​0​x​5​5​,​ ​_​w​i​d​t​h​ ​*​ ​_​h​e​i​g​h​t​ ​*​ ​4​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​

}​ ​/​/​ ​i​n​i​t​W​i​t​h​W​i​d​t​h​

-​ ​(​v​o​i​d​)​ ​d​e​a​l​l​o​c​ ​{​
 ​ ​ ​ ​f​r​e​e​ ​(​_​b​u​f​f​e​r​)​;​
 ​ ​ ​ ​[​s​u​p​e​r​ ​d​e​a​l​l​o​c​]​;​

}​ ​/​/​ ​d​e​a​l​l​o​c​

Next is -baseAddressForLine:, which returns the address in memory where a particular row of pixels live. This is just basic pointer math, assuming four bytes per pixel. CalcOperation will use this base address to know where it should start placing pixel values.

/​/​ ​P​e​r​f​o​r​m​ ​s​o​m​e​ ​p​o​i​n​t​e​r​ ​m​a​t​h​ ​t​o​ ​g​i​v​e​ ​t​h​e​ ​c​a​l​l​e​r​ ​t​h​e​ ​s​t​a​r​t​i​n​g​ ​a​d​d​r​e​s​s​ ​f​o​r​
/​/​ ​a​ ​p​a​r​t​i​c​u​l​a​r​ ​s​c​a​n​l​i​n​e​.​

-​ ​(​u​n​s​i​g​n​e​d​ ​c​h​a​r​ ​*​)​ ​b​a​s​e​A​d​d​r​e​s​s​F​o​r​L​i​n​e​:​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​l​i​n​e​ ​{​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​c​h​a​r​ ​*​a​d​d​r​ ​=​ ​_​b​u​f​f​e​r​ ​+​ ​(​l​i​n​e​ ​*​ ​s​e​l​f​.​w​i​d​t​h​ ​*​ ​4​)​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​a​d​d​r​;​

}​ ​/​/​ ​b​a​s​e​A​d​d​r​e​s​s​F​o​r​L​i​n​e​

And finally, -imageRep wraps the pixel buffer in an NSBitmapImageRep for drawing. It is also the last method for the class.

-​ ​(​N​S​B​i​t​m​a​p​I​m​a​g​e​R​e​p​ ​*​)​ ​i​m​a​g​e​R​e​p​ ​{​
 ​ ​ ​ ​N​S​B​i​t​m​a​p​I​m​a​g​e​R​e​p​ ​*​r​e​p​;​
 ​ ​ ​ ​r​e​p​ ​=​ ​[​[​N​S​B​i​t​m​a​p​I​m​a​g​e​R​e​p​ ​a​l​l​o​c​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​i​t​W​i​t​h​B​i​t​m​a​p​D​a​t​a​P​l​a​n​e​s​:​ ​&​_​b​u​f​f​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​i​x​e​l​s​W​i​d​e​:​ ​s​e​l​f​.​w​i​d​t​h​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​i​x​e​l​s​H​i​g​h​:​ ​s​e​l​f​.​h​e​i​g​h​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​i​t​s​P​e​r​S​a​m​p​l​e​:​ ​8​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​a​m​p​l​e​s​P​e​r​P​i​x​e​l​:​ ​4​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​h​a​s​A​l​p​h​a​:​ ​Y​E​S​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​s​P​l​a​n​a​r​:​ ​N​O​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​l​o​r​S​p​a​c​e​N​a​m​e​:​ ​N​S​D​e​v​i​c​e​R​G​B​C​o​l​o​r​S​p​a​c​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​y​t​e​s​P​e​r​R​o​w​:​ ​s​e​l​f​.​w​i​d​t​h​ ​*​ ​4​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​i​t​s​P​e​r​P​i​x​e​l​:​ ​3​2​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​[​r​e​p​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​

}​ ​/​/​ ​i​m​a​g​e​R​e​p​

@​e​n​d​ ​/​/​ ​B​i​t​m​a​p​

BitmapView

The pixels have a place to live. Now they need a place where they can show themselves off. Make a new NSView subclass called BitmapView. Edit BitmapView.h with the class interface, which just hangs onto the bitmap and gives callers a way to set the bitmap to use for drawing:

/​/​ ​B​i​t​m​a​p​V​i​e​w​.​h​ ​-​-​ ​V​i​e​w​ ​t​h​a​t​ ​d​i​s​p​l​a​y​s​ ​a​ ​B​i​t​m​a​p​,​ ​w​h​i​c​h​ ​u​s​u​a​l​l​y​ ​c​o​n​t​a​i​n​s​ ​a​
/​/​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​M​a​n​d​e​l​b​r​o​t​ ​s​e​t​.​

#​i​m​p​o​r​t​ ​<​C​o​c​o​a​/​C​o​c​o​a​.​h​>​

@​c​l​a​s​s​ ​B​i​t​m​a​p​;​

@​i​n​t​e​r​f​a​c​e​ ​B​i​t​m​a​p​V​i​e​w​ ​:​ ​N​S​V​i​e​w​ ​{​
 ​ ​ ​ ​B​i​t​m​a​p​ ​*​_​b​i​t​m​a​p​;​ ​/​/​ ​C​o​n​t​a​i​n​s​ ​t​h​e​ ​b​i​t​s​ ​t​o​ ​d​r​a​w​
}​

/​/​ ​T​h​e​ ​b​i​t​m​a​p​ ​t​o​ ​u​s​e​ ​f​o​r​ ​d​r​a​w​i​n​g​.​
@​p​r​o​p​e​r​t​y​ ​(​r​e​t​a​i​n​)​ ​B​i​t​m​a​p​ ​*​b​i​t​m​a​p​;​

/​/​ ​C​a​u​s​e​ ​a​ ​r​e​d​r​a​w​ ​f​r​o​m​ ​a​n​o​t​h​e​r​ ​t​h​r​e​a​d​ ​b​e​c​a​u​e​ ​p​a​s​s​i​n​g​ ​a​ ​s​c​a​l​a​r​ ​t​h​r​o​u​g​h​
/​/​ ​t​o​ ​-​p​e​r​f​o​r​m​S​e​l​e​c​t​o​r​O​n​M​a​i​n​T​h​r​e​a​d​ ​i​s​ ​a​w​k​w​a​r​d​.​ ​ ​T​h​e​ ​p​r​e​f​i​x​ ​i​s​ ​t​o​ ​a​v​o​i​d​
/​/​ ​a​n​y​ ​f​u​t​u​r​e​ ​n​a​m​e​ ​c​o​l​l​i​s​i​o​n​,​ ​b​e​c​a​u​s​e​ ​"​s​e​t​N​e​e​d​s​D​i​s​p​l​a​y​"​ ​i​s​ ​a​ ​v​e​r​y​
/​/​ ​o​b​v​i​o​u​s​ ​n​a​m​e​,​ ​a​n​d​ ​A​p​p​l​e​ ​m​a​y​ ​a​d​d​ ​t​h​e​i​r​ ​o​w​n​ ​s​e​t​N​e​e​d​s​D​i​s​p​l​a​y​ ​t​h​a​t​
/​/​ ​t​a​k​e​s​ ​n​o​ ​p​a​r​a​m​e​t​e​r​s​.​
-​ ​(​v​o​i​d​)​ ​b​n​r​_​s​e​t​N​e​e​d​s​D​i​s​p​l​a​y​;​

@​e​n​d​ ​/​/​ ​B​i​t​m​a​p​V​i​e​w​

Edit BitmapView.m to include the requisite headers and then implement dealloc and setBitmap:

#​i​m​p​o​r​t​ ​"​B​i​t​m​a​p​V​i​e​w​.​h​"​
#​i​m​p​o​r​t​ ​"​B​i​t​m​a​p​.​h​"​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​B​i​t​m​a​p​V​i​e​w​
@​s​y​n​t​h​e​s​i​z​e​ ​b​i​t​m​a​p​ ​=​ ​_​b​i​t​m​a​p​;​

/​/​ ​T​i​m​e​ ​t​o​ ​u​s​e​ ​a​ ​n​e​w​ ​b​i​t​m​a​p​.​
-​ ​(​v​o​i​d​)​ ​s​e​t​B​i​t​m​a​p​:​ ​(​B​i​t​m​a​p​ ​*​)​ ​b​i​t​m​a​p​ ​{​
 ​ ​ ​ ​[​_​b​i​t​m​a​p​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​_​b​i​t​m​a​p​ ​=​ ​[​b​i​t​m​a​p​ ​r​e​t​a​i​n​]​;​

 ​ ​ ​ ​[​s​e​l​f​ ​s​e​t​N​e​e​d​s​D​i​s​p​l​a​y​:​ ​Y​E​S​]​;​

}​ ​/​/​ ​s​e​t​B​i​t​m​a​p​

/​/​ ​C​l​e​a​n​ ​u​p​ ​o​u​r​ ​m​e​s​s​.​
-​ ​(​v​o​i​d​)​ ​d​e​a​l​l​o​c​ ​{​
 ​ ​ ​ ​[​_​b​i​t​m​a​p​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​s​u​p​e​r​ ​d​e​a​l​l​o​c​]​;​
}​ ​/​/​ ​d​e​a​l​l​o​c​

The view will need to redraw itself after any calculation operation has completed. Usually you do this by calling [view setNeedsDisplay: YES]. Unfortunately -setNeedsDisplay: is not thread safe. Ordinarily you would use one of the -performSelectorOnMainThread: methods to do this, but -performSelectorOnMainThread: only lets you pass an object parameter, not a BOOL. Cocoa does not do “autoboxing,” which automatically wraps primitive C types in an object type, so wrapping a BOOL in an NSValue will not work either. To work around this, add a method that takes no arguments which just does a setNeedsDisplay:YES so that it can be called easily via another thread:

-​ ​(​v​o​i​d​)​ ​b​n​r​_​s​e​t​N​e​e​d​s​D​i​s​p​l​a​y​ ​{​
 ​ ​ ​ ​[​s​e​l​f​ ​s​e​t​N​e​e​d​s​D​i​s​p​l​a​y​:​ ​Y​E​S​]​;​
}​ ​/​/​ ​b​n​r​_​s​e​t​N​e​e​d​s​D​i​s​p​l​a​y​

The prefix on the call is to future-proof this code. setNeedsDisplay, taking no arguments, is an obvious name. Apple already has a method named this in the iOS UIKit, but not in desktop AppKit. It is entirely possible that Apple will add setNeedsDisplay in a future version of Mac OS X. By prefixing this method, we make it obvious it’s one of our methods rather than one of Apple’s. Ordinarily, you do not need to worry about this, but being paranoid in this particular case is prudent.

And then add -drawRect:, which fills in the view’s bounds and frames it with a black rectangle and also draws the contents of the bitmap:

-​ ​(​v​o​i​d​)​ ​d​r​a​w​R​e​c​t​:​ ​(​N​S​R​e​c​t​)​ ​r​e​c​t​ ​{​
 ​ ​ ​ ​/​/​ ​G​o​ ​a​h​e​a​d​ ​a​n​d​ ​j​u​s​t​ ​d​r​a​w​ ​e​v​e​r​y​t​h​i​n​g​ ​e​v​e​r​y​ ​t​i​m​e​.​
 ​ ​ ​ ​N​S​R​e​c​t​ ​b​o​u​n​d​s​ ​=​ ​[​s​e​l​f​ ​b​o​u​n​d​s​]​;​

 ​ ​ ​ ​/​/​ ​F​i​l​l​ ​i​n​ ​t​h​e​ ​a​r​e​a​ ​w​i​t​h​ ​a​ ​n​i​c​e​ ​(​?​)​ ​p​u​r​p​l​i​s​h​ ​c​o​l​o​r​ ​p​r​i​o​r​ ​t​o​ ​d​r​a​w​i​n​g​ ​t​h​e​ ​b​i​t​m​a​p​
 ​ ​ ​ ​[​[​N​S​C​o​l​o​r​ ​p​u​r​p​l​e​C​o​l​o​r​]​ ​s​e​t​]​;​
 ​ ​ ​ ​N​S​R​e​c​t​F​i​l​l​ ​(​b​o​u​n​d​s​)​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​c​a​l​c​u​l​a​t​e​d​ ​b​i​t​s​ ​a​n​d​ ​d​r​a​w​ ​t​h​e​m​.​
 ​ ​ ​ ​N​S​B​i​t​m​a​p​I​m​a​g​e​R​e​p​ ​*​r​e​p​ ​=​ ​[​s​e​l​f​.​b​i​t​m​a​p​ ​i​m​a​g​e​R​e​p​]​;​
 ​ ​ ​ ​[​r​e​p​ ​d​r​a​w​A​t​P​o​i​n​t​:​ ​N​S​Z​e​r​o​P​o​i​n​t​]​;​

 ​ ​ ​ ​/​/​ ​A​n​d​ ​f​i​n​a​l​l​y​ ​p​u​t​ ​a​ ​n​i​c​e​ ​t​h​i​n​ ​b​o​r​d​e​r​ ​a​r​o​u​n​d​ ​t​h​e​ ​v​i​e​w​.​
 ​ ​ ​ ​[​[​N​S​C​o​l​o​r​ ​b​l​a​c​k​C​o​l​o​r​]​ ​s​e​t​]​;​
 ​ ​ ​ ​N​S​F​r​a​m​e​R​e​c​t​ ​(​b​o​u​n​d​s​)​;​

}​ ​/​/​ ​d​r​a​w​R​e​c​t​

CalcOperation

Now for the calculation portion of our program. The CalcOperation class is a subclass of NSOperation, and will be a simple-lifetime, non-concurrent operation which calculates a row of pixels. Recall that in 10.5, non-concurrent operations will run in their own threads. This operation will need a few of pieces of information: what bitmap to use, what line in that bitmap to deposit its pixel calculations into, the BitmapView that is showing the bitmap so that it can get redrawn, and the bounds of the area of the Mandelbrot set to calculate. Since each CalcOperation is responsible for filling in a row, it needs to know the vertical location within the Mandelbrot set, as well as the start and end points in the horizontal direction that denote the bounds of the portion of the Mandelbrot set being calculated.

Make a new class called CalcOperation. Edit the header file and add instance variables to store the different bits of state that it needs and an initializer to make a new operation.

/​/​ ​C​a​l​c​O​p​e​r​a​t​i​o​n​.​h​ ​-​-​ ​O​p​e​r​a​t​i​o​n​ ​t​o​ ​c​a​l​c​u​l​a​t​e​ ​a​ ​s​i​n​g​l​e​ ​l​i​n​e​ ​o​f​ ​t​h​e​ ​M​a​n​d​e​l​b​r​o​t​ ​S​e​t​
/​/​

#​i​m​p​o​r​t​ ​<​C​o​c​o​a​/​C​o​c​o​a​.​h​>​

@​c​l​a​s​s​ ​B​i​t​m​a​p​,​ ​B​i​t​m​a​p​V​i​e​w​;​

@​i​n​t​e​r​f​a​c​e​ ​C​a​l​c​O​p​e​r​a​t​i​o​n​ ​:​ ​N​S​O​p​e​r​a​t​i​o​n​ ​{​
 ​ ​ ​ ​B​i​t​m​a​p​ ​*​_​b​i​t​m​a​p​;​ ​ ​ ​/​/​ ​T​h​e​ ​b​i​t​m​a​p​ ​t​h​a​t​ ​h​o​l​d​s​ ​t​h​e​ ​p​i​x​e​l​'​s​ ​m​e​m​o​r​y​.​
 ​ ​ ​ ​N​S​U​I​n​t​e​g​e​r​ ​_​l​i​n​e​;​ ​ ​/​/​ ​T​h​e​ ​s​c​a​n​l​i​n​e​ ​o​f​ ​t​h​e​ ​b​i​t​m​a​p​ ​w​e​'​r​e​ ​r​e​s​p​o​n​s​i​b​l​e​ ​f​o​r​
 ​ ​ ​ ​B​i​t​m​a​p​V​i​e​w​ ​*​_​v​i​e​w​;​ ​/​/​ ​W​h​o​m​ ​t​o​ ​t​e​l​l​ ​t​o​ ​r​e​d​r​a​w​ ​w​h​e​n​ ​w​e​'​r​e​ ​d​o​n​e​ ​c​a​l​c​u​l​a​t​i​n​g​.​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​r​e​g​i​o​n​ ​o​f​ ​t​h​e​ ​M​a​n​d​e​l​b​r​o​t​ ​s​e​t​ ​t​o​ ​c​a​l​c​u​l​a​t​e​.​
 ​ ​ ​ ​d​o​u​b​l​e​ ​_​x​S​t​a​r​t​;​
 ​ ​ ​ ​d​o​u​b​l​e​ ​_​x​E​n​d​;​
 ​ ​ ​ ​d​o​u​b​l​e​ ​_​y​;​
}​

-​ ​(​i​d​)​ ​i​n​i​t​W​i​t​h​B​i​t​m​a​p​:​ ​(​B​i​t​m​a​p​ ​*​)​ ​b​i​t​m​a​p​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​i​t​m​a​p​V​i​e​w​:​ ​(​B​i​t​m​a​p​V​i​e​w​ ​*​)​ ​v​i​e​w​
 ​ ​ ​ ​ ​ ​ ​ ​c​a​l​c​u​l​a​t​e​L​i​n​e​:​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​l​i​n​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​x​S​t​a​r​t​:​ ​(​d​o​u​b​l​e​)​ ​x​S​t​a​r​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​x​E​n​d​:​ ​(​d​o​u​b​l​e​)​ ​x​E​n​d​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​y​:​ ​(​d​o​u​b​l​e​)​ ​y​;​

@​e​n​d​ ​/​/​ ​C​a​l​c​O​p​e​r​a​t​i​o​n​

Edit CalcOperation.m and add the functions that actually calculate the Mandelbrot set, as well as the initializer and -dealloc.

#​i​m​p​o​r​t​ ​"​C​a​l​c​O​p​e​r​a​t​i​o​n​.​h​"​

#​i​m​p​o​r​t​ ​"​B​i​t​m​a​p​.​h​"​
#​i​m​p​o​r​t​ ​<​c​o​m​p​l​e​x​.​h​>​

/​/​ ​T​h​i​s​ ​d​e​t​e​r​m​i​n​e​s​ ​h​o​w​ ​f​i​n​e​ ​t​h​e​ ​M​a​n​d​e​l​b​r​o​t​ ​s​e​t​ ​c​a​l​c​u​l​a​t​i​o​n​s​ ​a​r​e​.​
#​d​e​f​i​n​e​ ​L​O​O​P​ ​5​0​0​
#​d​e​f​i​n​e​ ​L​I​M​I​T​ ​2​5​6​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​C​a​l​c​O​p​e​r​a​t​i​o​n​

/​/​ ​T​h​i​s​ ​d​e​t​e​r​m​i​n​e​s​ ​w​h​a​t​ ​c​o​l​o​r​s​ ​g​o​ ​w​i​t​h​ ​w​h​i​c​h​ ​v​a​l​u​e​s​.​
/​/​ ​I​'​v​e​ ​s​e​t​ ​i​t​ ​u​p​ ​f​o​r​ ​a​ ​p​l​e​a​s​i​n​g​ ​b​l​u​e​-​c​a​s​t​ ​s​c​h​e​m​e​.​

v​o​i​d​ ​g​r​a​d​i​e​n​t​ ​(​i​n​t​ ​v​a​l​u​e​,​ ​u​n​s​i​g​n​e​d​ ​c​h​a​r​ ​*​b​u​f​f​e​r​)​ ​{​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​c​h​a​r​ ​*​p​t​r​ ​=​ ​b​u​f​f​e​r​;​
 ​ ​ ​ ​v​a​l​u​e​ ​=​ ​v​a​l​u​e​ ​*​ ​4​;​
 ​ ​ ​ ​i​f​ ​(​v​a​l​u​e​ ​>​ ​2​5​5​)​ ​v​a​l​u​e​ ​=​ ​2​5​5​;​

 ​ ​ ​ ​*​p​t​r​+​+​ ​=​ ​v​a​l​u​e​ ​/​ ​3​;​ ​ ​/​/​ ​R​e​d​
 ​ ​ ​ ​*​p​t​r​+​+​ ​=​ ​v​a​l​u​e​ ​/​ ​2​;​ ​ ​/​/​ ​G​r​e​e​n​
 ​ ​ ​ ​*​p​t​r​+​+​ ​=​ ​v​a​l​u​e​;​ ​ ​ ​ ​ ​ ​/​/​ ​B​l​u​e​
 ​ ​ ​ ​*​p​t​r​+​+​ ​=​ ​0​x​F​F​;​ ​ ​ ​ ​ ​ ​ ​/​/​ ​A​l​p​h​a​
}​ ​/​/​ ​g​r​a​d​i​e​n​t​

/​/​ ​b​u​f​f​e​r​ ​i​s​ ​a​ ​p​o​i​n​t​e​r​ ​t​o​ ​t​h​e​ ​f​o​u​r​ ​b​y​t​e​s​ ​t​h​a​t​ ​w​i​l​l​ ​h​o​l​d​
/​/​ ​t​h​e​ ​r​e​s​u​l​t​i​n​g​ ​c​o​l​o​r​

v​o​i​d​ ​m​a​n​d​e​l​b​r​o​t​ ​(​d​o​u​b​l​e​ ​x​,​ ​d​o​u​b​l​e​ ​y​,​ ​u​n​s​i​g​n​e​d​ ​c​h​a​r​ ​*​b​u​f​f​e​r​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​i​;​
 ​ ​ ​ ​c​o​m​p​l​e​x​ ​z​,​c​;​

 ​ ​ ​ ​c​ ​=​ ​x​ ​+​ ​(​y​ ​*​ ​1​.​0​i​)​;​
 ​ ​ ​ ​z​ ​=​ ​0​;​

 ​ ​ ​ ​f​o​r​ ​(​i​ ​=​ ​0​;​ ​i​ ​<​ ​L​O​O​P​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​z​ ​=​ ​(​z​ ​*​ ​z​)​ ​+​ ​c​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​c​a​b​s​(​z​)​ ​>​ ​L​I​M​I​T​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​g​r​a​d​i​e​n​t​ ​(​i​,​ ​b​u​f​f​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​
 ​ ​ ​ ​g​r​a​d​i​e​n​t​ ​(​0​,​ ​b​u​f​f​e​r​)​;​
}​ ​/​/​ ​m​a​n​d​e​l​b​r​o​t​

-​ ​(​i​d​)​ ​i​n​i​t​W​i​t​h​B​i​t​m​a​p​:​ ​(​B​i​t​m​a​p​ ​*​)​ ​b​i​t​m​a​p​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​i​t​m​a​p​V​i​e​w​:​ ​(​B​i​t​m​a​p​V​i​e​w​ ​*​)​ ​v​i​e​w​
 ​ ​ ​ ​ ​ ​ ​ ​c​a​l​c​u​l​a​t​e​L​i​n​e​:​ ​(​N​S​U​I​n​t​e​g​e​r​)​ ​l​i​n​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​x​S​t​a​r​t​:​ ​(​d​o​u​b​l​e​)​ ​x​S​t​a​r​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​x​E​n​d​:​ ​(​d​o​u​b​l​e​)​ ​x​E​n​d​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​y​:​ ​(​d​o​u​b​l​e​)​ ​y​ ​{​

 ​ ​ ​ ​i​f​ ​(​(​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​_​b​i​t​m​a​p​ ​=​ ​[​b​i​t​m​a​p​ ​r​e​t​a​i​n​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​l​i​n​e​ ​=​ ​l​i​n​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​x​S​t​a​r​t​ ​=​ ​x​S​t​a​r​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​x​E​n​d​ ​=​ ​x​E​n​d​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​y​ ​=​ ​y​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​

}​ ​/​/​ ​i​n​i​t​W​i​t​h​B​i​t​m​a​p​

-​ ​(​v​o​i​d​)​ ​d​e​a​l​l​o​c​ ​{​
 ​ ​ ​ ​[​_​b​i​t​m​a​p​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​_​v​i​e​w​ ​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​[​s​u​p​e​r​ ​d​e​a​l​l​o​c​]​;​

}​ ​/​/​ ​d​e​a​l​l​o​c​

And finally, the real interesting work of the program. Since CalcOperation is a simple-lifetime operation, you just need to override -main.

If an operation is cancelled while it is still in its queue, the operation will still be run, so it is necessary to check -isCancelled first thing in the method so you do not do unnecessary work.

And to make the display look cool, -main will sleep for a little bit so that the lines will filter in slow enough to see them.

-​ ​(​v​o​i​d​)​ ​m​a​i​n​ ​{​

 ​ ​ ​ ​/​/​ ​M​a​k​e​ ​s​u​r​e​ ​w​e​ ​w​e​r​e​n​'​t​ ​c​a​n​c​e​l​l​e​d​ ​b​e​f​o​r​e​ ​s​t​a​r​t​i​n​g​.​
 ​ ​ ​ ​i​f​ ​(​[​s​e​l​f​ ​i​s​C​a​n​c​e​l​l​e​d​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​(​@​"​C​a​n​c​e​l​l​i​n​g​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​P​u​t​t​i​n​g​ ​i​n​ ​a​ ​p​a​u​s​e​ ​m​a​k​e​s​ ​t​h​e​ ​s​c​a​n​ ​l​i​n​e​s​ ​f​i​l​l​ ​i​n​ ​s​l​o​w​e​r​,​ ​g​i​v​i​n​g​
 ​ ​ ​ ​/​/​ ​a​ ​g​r​o​o​v​i​e​r​ ​l​o​o​k​ ​t​o​ ​t​h​i​n​g​s​.​ ​ ​T​a​k​e​ ​t​h​i​s​ ​o​u​t​ ​f​o​r​ ​m​a​x​i​m​u​m​ ​p​e​r​f​o​r​m​a​n​c​e​.​
 ​ ​ ​ ​i​n​t​ ​u​s​e​c​ ​=​ ​r​a​n​d​o​m​(​)​ ​%​ ​1​0​0​0​;​
 ​ ​ ​ ​u​s​e​c​ ​*​=​ ​3​0​0​0​;​
 ​ ​ ​ ​u​s​l​e​e​p​ ​(​u​s​e​c​)​;​

Recall that NSOperationQueue, if left to its own devices, will choose the number of concurrent threads it manages based on the number of processors, as well as vague “other factors” mentioned in Apple’s documentation. One of those hidden other factors is that if the NSOperation blocks, then the operation queue may create another thread and assign it an operation to run. In this case, when the usleep() function is called to sleep the current thread for a specified number of microseconds, NSOperationQueue notices the operation has entered kernel-space and will schedule another CalcOperation. I have witnessed over sixty threads being created by MandelOpper. Performing I/O (e.g. NSLog, network communications, file I/O) or even entering an @synchronized block is enough for NSOperationQueue to think it needs to create more threads. If this turns out to be a problem for you, you can determine the number of processors in your system and pass that value to -setMaxConcurrentOperationCount: See the exercises for more information.

Next, set up some of the state that is necessary for the pixel calculations: the address in the bitmap to put the pixel values, how far in the Mandelbrot set to advance horizontally for each pixel, and a “you are here” variable that gets scooted after we calculate a pixel’s values.

 ​ ​ ​ ​/​/​ ​T​h​i​s​ ​i​s​ ​w​h​e​r​e​ ​w​e​ ​s​t​a​r​t​ ​f​i​l​l​i​n​g​ ​i​n​ ​m​e​m​o​r​y​.​
 ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​c​h​a​r​ ​*​s​c​a​n​l​i​n​e​ ​=​ ​[​_​b​i​t​m​a​p​ ​b​a​s​e​A​d​d​r​e​s​s​F​o​r​L​i​n​e​:​ ​_​l​i​n​e​]​;​

 ​ ​ ​ ​/​/​ ​H​o​w​ ​f​a​r​ ​i​n​ ​t​h​e​ ​M​a​n​d​e​l​b​r​o​t​ ​s​e​t​ ​t​o​ ​a​d​v​a​n​c​e​ ​f​o​r​ ​e​a​c​h​ ​p​i​x​e​l​.​
 ​ ​ ​ ​d​o​u​b​l​e​ ​x​D​e​l​t​a​ ​=​ ​(​_​x​E​n​d​ ​-​ ​_​x​S​t​a​r​t​)​ ​/​ ​_​b​i​t​m​a​p​.​w​i​d​t​h​;​

 ​ ​ ​ ​/​/​ ​S​t​a​r​t​ ​h​e​r​e​.​
 ​ ​ ​ ​d​o​u​b​l​e​ ​x​ ​=​ ​_​x​S​t​a​r​t​;​

Now loop over every pixel in the row and calculate the pixel values. Since Mandelbrot calculations could take a fair amount of time, check occasionally if we have been cancelled. If so, bail out because there is no need to do any more work.

 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​_​b​i​t​m​a​p​.​w​i​d​t​h​;​ ​i​+​+​)​ ​{​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​F​i​g​u​r​e​ ​o​u​t​ ​w​h​e​r​e​ ​i​n​ ​m​e​m​o​r​y​ ​t​o​ ​p​u​t​ ​t​h​e​ ​n​e​x​t​ ​p​i​x​e​l​.​
 ​ ​ ​ ​ ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​c​h​a​r​ ​*​p​i​x​e​l​ ​=​ ​(​s​c​a​n​l​i​n​e​ ​+​ ​i​ ​*​ ​4​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​S​c​o​o​t​ ​o​v​e​r​ ​a​n​d​ ​d​r​a​w​.​
 ​ ​ ​ ​ ​ ​ ​ ​x​ ​+​=​ ​x​D​e​l​t​a​;​
 ​ ​ ​ ​ ​ ​ ​ ​m​a​n​d​e​l​b​r​o​t​ ​(​x​,​ ​_​y​,​ ​p​i​x​e​l​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​h​e​c​k​ ​e​v​e​r​y​ ​n​o​w​ ​a​n​d​ ​t​h​e​n​ ​f​o​r​ ​a​ ​c​a​n​c​e​l​l​a​t​i​o​n​.​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​i​ ​%​ ​5​0​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​[​s​e​l​f​ ​i​s​C​a​n​c​e​l​l​e​d​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​(​@​"​c​a​n​c​e​l​l​i​n​g​!​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

If you have successfully completed a scan line, tell the BitmapView to draw itself again, using the no-argument -bnr_setNeedsDisplay added to BitmapView. This also ends CalcOperation.m.

 ​ ​ ​ ​/​/​ ​W​e​'​r​e​ ​d​o​n​e​!​ ​ ​T​e​l​l​ ​t​h​e​ ​v​i​e​w​ ​t​o​ ​u​p​d​a​t​e​ ​i​t​s​e​l​f​ ​w​h​e​n​ ​c​o​n​v​e​n​i​e​n​t​.​
 ​ ​ ​ ​[​_​v​i​e​w​ ​p​e​r​f​o​r​m​S​e​l​e​c​t​o​r​O​n​M​a​i​n​T​h​r​e​a​d​:​ ​@​s​e​l​e​c​t​o​r​(​b​n​r​_​s​e​t​N​e​e​d​s​D​i​s​p​l​a​y​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​t​h​O​b​j​e​c​t​:​ ​n​i​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​a​i​t​U​n​t​i​l​D​o​n​e​:​ ​N​O​]​;​
}​ ​/​/​ ​m​a​i​n​

@​e​n​d​ ​/​/​ ​C​a​l​c​O​p​e​r​a​t​i​o​n​

MandelOpperAppDelegate

Now that all of the worker classes have been written, it is time to hook everything together. Edit MandelOpperAppDelegate.h and add the interface for the class, including two IBOutlets for a BitmapView and a text field for a status line, which says whether the program is idling or calculating. There is also an IBAction for starting the calculations. There is an NSOperationQueue which will hold all of the CalcOperations created, one for each row in the bitmap, as well as the region of the Mandelbrot set that is being displayed.

#​i​m​p​o​r​t​ ​<​C​o​c​o​a​/​C​o​c​o​a​.​h​>​

@​c​l​a​s​s​ ​B​i​t​m​a​p​,​ ​B​i​t​m​a​p​V​i​e​w​;​

@​i​n​t​e​r​f​a​c​e​ ​M​a​n​d​e​l​O​p​p​e​r​A​p​p​D​e​l​e​g​a​t​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​{​
 ​ ​ ​ ​B​i​t​m​a​p​ ​*​_​b​i​t​m​a​p​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​h​e​r​e​ ​t​h​e​ ​M​a​n​d​e​l​b​r​o​t​ ​s​e​t​ ​i​m​a​g​e​ ​l​i​v​e​s​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​B​i​t​m​a​p​V​i​e​w​ ​*​_​b​i​t​m​a​p​V​i​e​w​;​ ​ ​/​/​ ​W​h​e​r​e​ ​i​t​ ​g​e​t​s​ ​d​i​s​p​l​a​y​e​d​

 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​_​s​t​a​t​u​s​L​i​n​e​;​ ​/​/​ ​T​e​l​l​ ​t​h​e​ ​u​s​e​r​ ​w​h​a​t​ ​w​e​'​r​e​ ​d​o​i​n​g​

 ​ ​ ​ ​N​S​O​p​e​r​a​t​i​o​n​Q​u​e​u​e​ ​*​_​q​u​e​u​e​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​H​o​l​d​s​ ​t​h​e​ ​c​a​l​c​u​l​a​t​i​o​n​ ​o​p​e​r​a​t​i​o​n​s​

 ​ ​ ​ ​N​S​R​e​c​t​ ​_​r​e​g​i​o​n​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​T​h​e​ ​r​e​g​i​o​n​ ​o​f​ ​t​h​e​ ​s​e​t​ ​b​e​i​n​g​ ​d​i​s​p​l​a​y​e​d​.​
}​

@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​B​i​t​m​a​p​V​i​e​w​ ​*​b​i​t​m​a​p​V​i​e​w​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​s​t​a​t​u​s​L​i​n​e​;​

/​/​ ​S​t​a​r​t​ ​c​a​l​c​u​l​a​t​i​n​g​ ​t​h​e​ ​s​e​t​ ​a​n​d​ ​d​i​s​p​l​a​y​ ​t​h​e​ ​r​e​s​u​l​t​s​.​
-​ ​(​I​B​A​c​t​i​o​n​)​ ​s​t​a​r​t​:​ ​(​i​d​)​ ​s​e​n​d​e​r​;​

@​e​n​d​ ​/​/​ ​M​a​n​d​e​l​O​p​p​e​r​A​p​p​D​e​l​e​g​a​t​e​

Open MainMenu.xib and drop a CustomView on the window. Make it the BitmapView class and add a text field. Set up the resizing connections so that the view will expand and the text field will stay at the bottom. Hook up the outlets to the BitmapView and the text field and hook up the button action, too, as shown in Figure 21.3.

Figure 21.3  MandelOpper window

Now edit MandelOpperAppDelegate.m. First off, add a method to update the status line. Also, override -awakeFromNib to set the status to “idle” and set the region to a rectangle that snuggles up to the Mandelbrot set, along with a -dealloc to release the objects that will eventually get created.

#​i​m​p​o​r​t​ ​"​M​a​n​d​e​l​O​p​p​e​r​A​p​p​D​e​l​e​g​a​t​e​.​h​"​

#​i​m​p​o​r​t​ ​"​B​i​t​m​a​p​.​h​"​
#​i​m​p​o​r​t​ ​"​B​i​t​m​a​p​V​i​e​w​.​h​"​
#​i​m​p​o​r​t​ ​"​C​a​l​c​O​p​e​r​a​t​i​o​n​.​h​"​

/​/​ ​A​d​o​p​t​ ​t​h​e​ ​p​r​o​t​o​c​o​l​ ​h​e​r​e​ ​s​o​ ​w​e​ ​d​o​n​'​t​ ​h​a​v​e​ ​t​o​ ​p​o​l​l​u​t​e​ ​o​u​r​ ​h​e​a​d​e​r​ ​w​i​t​h​ ​i​t​.​
@​i​n​t​e​r​f​a​c​e​ ​M​a​n​d​e​l​O​p​p​e​r​A​p​p​D​e​l​e​g​a​t​e​ ​(​)​ ​<​B​i​t​m​a​p​V​i​e​w​D​e​l​e​g​a​t​e​>​
@​e​n​d​ ​/​/​ ​e​x​t​e​n​s​i​o​n​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​M​a​n​d​e​l​O​p​p​e​r​A​p​p​D​e​l​e​g​a​t​e​
@​s​y​n​t​h​e​s​i​z​e​ ​b​i​t​m​a​p​V​i​e​w​ ​=​ ​_​b​i​t​m​a​p​V​i​e​w​;​
@​s​y​n​t​h​e​s​i​z​e​ ​s​t​a​t​u​s​L​i​n​e​ ​=​ ​_​s​t​a​t​u​s​L​i​n​e​;​

/​/​ ​C​h​a​n​g​e​ ​t​h​e​ ​m​e​s​s​a​g​e​ ​i​n​ ​a​ ​t​e​x​t​ ​f​i​e​l​d​ ​i​n​ ​t​h​e​ ​w​i​n​d​o​w​.​
-​ ​(​v​o​i​d​)​ ​u​p​d​a​t​e​S​t​a​t​u​s​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​m​e​s​s​a​g​e​ ​{​
 ​ ​ ​ ​[​s​e​l​f​.​s​t​a​t​u​s​L​i​n​e​ ​s​e​t​S​t​r​i​n​g​V​a​l​u​e​:​ ​m​e​s​s​a​g​e​]​;​
}​ ​/​/​ ​u​p​d​a​t​e​S​t​a​t​u​s​

-​ ​(​v​o​i​d​)​ ​a​w​a​k​e​F​r​o​m​N​i​b​ ​{​
 ​ ​ ​ ​/​/​ ​A​w​a​i​t​i​n​g​ ​i​n​s​t​r​u​c​t​i​o​n​s​,​ ​O​ ​M​a​s​t​e​r​
 ​ ​ ​ ​[​s​e​l​f​ ​u​p​d​a​t​e​S​t​a​t​u​s​:​ ​@​"​i​d​l​e​"​]​;​

 ​ ​ ​ ​/​/​ ​T​h​i​s​ ​g​i​v​e​s​ ​u​s​ ​a​ ​n​i​c​e​ ​v​i​e​w​ ​o​f​ ​t​h​e​ ​e​n​t​i​r​e​ ​M​a​n​d​e​l​b​r​o​t​ ​s​e​t​
 ​ ​ ​ ​_​r​e​g​i​o​n​ ​=​ ​N​S​M​a​k​e​R​e​c​t​ ​(​-​2​.​0​,​ ​-​1​.​2​,​ ​3​.​0​,​ ​2​.​4​)​;​
}​ ​/​/​ ​a​w​a​k​e​F​r​o​m​N​i​b​

/​/​ ​C​l​e​a​n​ ​u​p​ ​o​u​r​ ​m​e​s​s​.​
-​ ​(​v​o​i​d​)​ ​d​e​a​l​l​o​c​ ​{​
 ​ ​ ​ ​[​_​b​i​t​m​a​p​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​_​q​u​e​u​e​ ​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​[​s​u​p​e​r​ ​d​e​a​l​l​o​c​]​;​
}​ ​/​/​ ​d​e​a​l​l​o​c​

NSBlockOperation

You will be creating a bunch of CalcOperations for calculating individual rows of the Mandelbrot set. But how do you know when you are done? Operations of the same priority that are not blocking on a dependency can be run in any arbitrary order. Even if they were started in a predictable order, they may not all complete in a predictable order. One way to determine when all the calculations are done is to have a special “I’m done!” operation that is dependent on all of the calculation operations which would tell the app delegate that all the work has been finished. It would be annoying to have to create a special operation subclass just for this task, so you will use an NSBlockOperation, which you initialize with a block. When the NSBlockOperation runs, it runs its block.

You are not guaranteed which thread the all-done operation will be run from, and you should not do anything UI-related from anything other than the main thread, so the block uses -performSelectorOnMainThread to set the status line. Do not enter this code yet; it will appear partway through the -start method:

 ​ ​ ​ ​.​.​.​
 ​ ​ ​ ​N​S​B​l​o​c​k​O​p​e​r​a​t​i​o​n​ ​*​a​l​l​D​o​n​e​ ​=​ ​[​N​S​B​l​o​c​k​O​p​e​r​a​t​i​o​n​ ​b​l​o​c​k​O​p​e​r​a​t​i​o​n​W​i​t​h​B​l​o​c​k​:​ ​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​M​o​s​t​ ​l​i​k​e​l​y​ ​w​e​ ​a​r​e​ ​*​n​o​t​*​ ​t​h​e​ ​m​a​i​n​ ​t​h​r​e​a​d​ ​w​h​e​n​ ​t​h​i​s​ ​b​l​o​c​k​ ​r​u​n​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​a​n​d​ ​w​e​ ​n​e​e​d​ ​t​o​ ​b​e​ ​o​n​ ​t​h​e​ ​m​a​i​n​ ​t​h​r​e​a​d​ ​t​o​ ​u​p​d​a​t​e​ ​t​h​e​ ​s​t​a​t​u​s​ ​l​i​n​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​t​e​x​t​ ​f​i​e​l​d​.​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​p​e​r​f​o​r​m​S​e​l​e​c​t​o​r​O​n​M​a​i​n​T​h​r​e​a​d​:​ ​@​s​e​l​e​c​t​o​r​(​u​p​d​a​t​e​S​t​a​t​u​s​:​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​t​h​O​b​j​e​c​t​:​ ​@​"​d​o​n​e​!​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​a​i​t​U​n​t​i​l​D​o​n​e​:​ ​N​O​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​]​;​
 ​ ​ ​ ​.​.​.​

So, now let’s create the operation queue, make the calc operations, and add them to the operation queue. The -start: method is hooked up to a button, so that’s a good place to start things. If there are calculations currently running, cancel them and dispose of any previous operations. Update the status line to “running,” and make an NSOperationQueue to hold our new operations:

-​ ​(​I​B​A​c​t​i​o​n​)​ ​s​t​a​r​t​:​ ​(​i​d​)​ ​s​e​n​d​e​r​ ​{​
 ​ ​ ​ ​/​/​ ​S​t​o​p​ ​a​n​y​ ​i​n​-​f​l​i​g​h​t​ ​c​a​l​c​u​l​a​t​i​o​n​s​.​
 ​ ​ ​ ​[​_​q​u​e​u​e​ ​c​a​n​c​e​l​A​l​l​O​p​e​r​a​t​i​o​n​s​]​;​
 ​ ​ ​ ​[​_​q​u​e​u​e​ ​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​/​/​ ​I​n​f​o​r​m​ ​t​h​e​ ​u​s​e​r​ ​w​h​a​t​'​s​ ​g​o​i​n​g​ ​o​n​.​
 ​ ​ ​ ​[​s​e​l​f​ ​u​p​d​a​t​e​S​t​a​t​u​s​:​ ​@​"​r​u​n​n​i​n​g​"​]​;​

 ​ ​ ​ ​/​/​ ​T​h​i​s​ ​i​s​ ​t​h​e​ ​q​u​e​u​e​ ​t​h​a​t​ ​w​i​l​l​ ​r​u​n​ ​a​l​l​ ​o​f​ ​o​u​r​ ​o​p​e​r​a​t​i​o​n​s​.​
 ​ ​ ​ ​_​q​u​e​u​e​ ​=​ ​[​[​N​S​O​p​e​r​a​t​i​o​n​Q​u​e​u​e​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

Next, make the “all done” NSBlockOperation. You have to make this first so that you will have it available when you create the CalcOperations so you can hook up the dependencies.

 ​ ​ ​ ​/​/​ ​M​a​k​e​ ​a​ ​n​e​w​ ​o​p​e​r​a​t​i​o​n​ ​t​o​ ​t​e​l​l​ ​u​s​ ​w​e​'​v​e​ ​f​i​n​i​s​h​e​d​ ​c​a​l​c​u​l​a​t​i​n​g​.​
 ​ ​ ​ ​/​/​ ​E​v​e​r​y​ ​o​t​h​e​r​ ​a​c​t​i​o​n​ ​w​i​l​l​ ​b​e​c​o​m​e​ ​a​ ​d​e​p​e​n​d​e​n​c​y​ ​o​f​ ​t​h​i​s​ ​o​n​e​,​ ​m​a​k​i​n​g​
 ​ ​ ​ ​/​/​ ​i​t​ ​w​a​i​t​ ​u​n​t​i​l​ ​a​l​l​ ​o​f​ ​t​h​e​ ​c​a​l​c​u​l​a​t​i​o​n​s​ ​a​r​e​ ​d​o​n​e​.​
 ​ ​ ​ ​N​S​B​l​o​c​k​O​p​e​r​a​t​i​o​n​ ​*​a​l​l​D​o​n​e​ ​=​ ​[​N​S​B​l​o​c​k​O​p​e​r​a​t​i​o​n​ ​b​l​o​c​k​O​p​e​r​a​t​i​o​n​W​i​t​h​B​l​o​c​k​:​ ​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​M​o​s​t​ ​l​i​k​e​l​y​ ​w​e​ ​a​r​e​ ​*​n​o​t​*​ ​t​h​e​ ​m​a​i​n​ ​t​h​r​e​a​d​ ​w​h​e​n​ ​t​h​i​s​ ​b​l​o​c​k​ ​r​u​n​s​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​a​n​d​ ​w​e​ ​n​e​e​d​ ​t​o​ ​b​e​ ​o​n​ ​t​h​e​ ​m​a​i​n​ ​t​h​r​e​a​d​ ​t​o​ ​u​p​d​a​t​e​ ​t​h​e​ ​s​t​a​t​u​s​ ​l​i​n​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​t​e​x​t​ ​f​i​e​l​d​.​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​p​e​r​f​o​r​m​S​e​l​e​c​t​o​r​O​n​M​a​i​n​T​h​r​e​a​d​:​ ​@​s​e​l​e​c​t​o​r​(​u​p​d​a​t​e​S​t​a​t​u​s​:​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​t​h​O​b​j​e​c​t​:​ ​@​"​d​o​n​e​!​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​a​i​t​U​n​t​i​l​D​o​n​e​:​ ​N​O​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​]​;​

You will need a Bitmap to draw in, so create one. Associate that Bitmap with the BitmapView and also set up the various values used in creating the CalcOperations.

 ​ ​ ​ ​/​/​ ​C​o​n​s​t​r​u​c​t​ ​a​ ​b​i​t​m​a​p​ ​t​h​e​ ​s​i​z​e​ ​o​f​ ​t​h​e​ ​f​r​a​m​e​ ​(​u​s​i​n​g​ ​o​n​e​ ​p​o​i​n​t​ ​=​=​ ​o​n​e​ ​p​i​x​e​l​)​
 ​ ​ ​ ​N​S​R​e​c​t​ ​f​r​a​m​e​ ​=​ ​s​e​l​f​.​b​i​t​m​a​p​V​i​e​w​.​f​r​a​m​e​;​
 ​ ​ ​ ​N​S​U​I​n​t​e​g​e​r​ ​h​e​i​g​h​t​ ​=​ ​f​r​a​m​e​.​s​i​z​e​.​h​e​i​g​h​t​;​
 ​ ​ ​ ​N​S​U​I​n​t​e​g​e​r​ ​w​i​d​t​h​ ​=​ ​f​r​a​m​e​.​s​i​z​e​.​w​i​d​t​h​;​

 ​ ​ ​ ​[​_​b​i​t​m​a​p​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​_​b​i​t​m​a​p​ ​=​ ​[​[​B​i​t​m​a​p​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​W​i​d​t​h​:​ ​w​i​d​t​h​ ​ ​h​e​i​g​h​t​:​ ​h​e​i​g​h​t​]​;​

 ​ ​ ​ ​/​/​ ​T​e​l​l​ ​t​h​e​ ​b​i​t​m​a​p​ ​v​i​e​w​ ​t​o​ ​u​s​e​ ​t​h​i​s​ ​n​e​w​ ​b​i​t​m​a​p​.​
 ​ ​ ​ ​[​s​e​l​f​.​b​i​t​m​a​p​V​i​e​w​ ​s​e​t​B​i​t​m​a​p​:​ ​_​b​i​t​m​a​p​]​;​

 ​ ​ ​ ​/​/​ ​T​h​i​s​ ​i​s​ ​t​h​e​ ​c​h​a​n​g​e​ ​i​n​ ​Y​ ​c​o​v​e​r​e​d​ ​b​y​ ​o​n​e​ ​l​i​n​e​ ​o​f​ ​t​h​e​ ​b​i​t​m​a​p​
 ​ ​ ​ ​d​o​u​b​l​e​ ​d​e​l​t​a​Y​ ​=​ ​_​r​e​g​i​o​n​.​s​i​z​e​.​h​e​i​g​h​t​ ​/​ ​h​e​i​g​h​t​;​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​s​t​a​r​t​i​n​g​ ​p​o​i​n​t​ ​f​o​r​ ​t​h​e​ ​y​ ​c​o​o​r​d​i​n​a​t​e​s​ ​g​i​v​e​n​ ​t​o​ ​e​a​c​h​
 ​ ​ ​ ​/​/​ ​c​a​l​c​u​l​a​t​i​o​n​ ​o​p​e​r​a​t​i​o​n​.​
 ​ ​ ​ ​d​o​u​b​l​e​ ​y​ ​=​ ​N​S​M​a​x​Y​ ​(​_​r​e​g​i​o​n​)​;​

Now create a CalcOperation for each row and add the new CalcOperation as a dependency for the allDone operation.

 ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​a​ ​n​e​w​ ​C​a​l​c​O​p​e​r​a​t​i​o​n​ ​f​o​r​ ​e​a​c​h​ ​l​i​n​e​ ​o​f​ ​t​h​e​ ​b​i​t​m​a​p​.​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​f​r​a​m​e​.​s​i​z​e​.​h​e​i​g​h​t​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​C​a​l​c​O​p​e​r​a​t​i​o​n​ ​*​o​p​;​
 ​ ​ ​ ​ ​ ​ ​ ​o​p​ ​=​ ​[​[​C​a​l​c​O​p​e​r​a​t​i​o​n​ ​a​l​l​o​c​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​i​t​W​i​t​h​B​i​t​m​a​p​:​ ​_​b​i​t​m​a​p​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​i​t​m​a​p​V​i​e​w​:​ ​s​e​l​f​.​b​i​t​m​a​p​V​i​e​w​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​a​l​c​u​l​a​t​e​L​i​n​e​:​ ​i​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​x​S​t​a​r​t​:​ ​N​S​M​i​n​X​(​_​r​e​g​i​o​n​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​x​E​n​d​:​ ​N​S​M​a​x​X​(​_​r​e​g​i​o​n​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​y​:​ ​y​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​S​e​t​ ​t​h​e​ ​d​e​p​e​n​d​e​n​c​y​ ​f​o​r​ ​t​h​e​ ​a​l​l​D​o​n​e​ ​o​p​e​r​a​t​i​o​n​,​ ​a​n​d​ ​p​u​t​ ​i​n​ ​t​h​e​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​q​u​e​u​e​.​ ​ ​T​h​e​ ​o​p​e​r​a​t​i​o​n​ ​m​a​y​ ​s​t​a​r​t​ ​r​u​n​n​i​n​g​ ​i​m​m​e​d​i​a​t​e​l​y​.​
 ​ ​ ​ ​ ​ ​ ​ ​[​a​l​l​D​o​n​e​ ​a​d​d​D​e​p​e​n​d​e​n​c​y​:​ ​o​p​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​_​q​u​e​u​e​ ​a​d​d​O​p​e​r​a​t​i​o​n​:​ ​o​p​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​y​ ​-​=​ ​d​e​l​t​a​Y​;​
 ​ ​ ​ ​}​

The CalcOperations will start running as soon as they are added to the queue, so they can start doing real work while we are still adding other operations to the queue.

Finally add the allDone operation to the queue. The order that operations are added to the queue is important: if we added the all-done operation first, the queue would keep checking to see if all of the allDone operation’s dependencies have completed. It would be possible for us to add the allDone operation, add a couple of calculation operations, and then have the calculations complete on other processors before adding the rest of the operations to the queue. The queue would then fire the allDone operation, this loop would then add more operations, and mayhem would ensue.

 ​ ​ ​ ​/​/​ ​F​i​n​a​l​y​ ​a​d​d​ ​t​h​e​ ​a​l​l​-​d​o​n​e​ ​o​p​e​r​a​t​i​o​n​.​ ​ ​W​e​ ​c​o​u​l​d​ ​n​o​t​ ​h​a​v​e​ ​d​o​n​e​ ​t​h​i​s​
 ​ ​ ​ ​/​/​ ​f​i​r​s​t​,​ ​b​e​c​a​u​s​e​ ​t​h​e​ ​q​u​e​u​e​ ​m​a​y​ ​h​a​v​e​ ​d​r​a​i​n​e​d​ ​b​e​f​o​r​e​ ​w​e​ ​a​d​d​e​d​ ​a​l​l​ ​o​f​
 ​ ​ ​ ​/​/​ ​t​h​e​ ​C​a​l​c​O​p​e​r​a​t​i​o​n​s​
 ​ ​ ​ ​[​_​q​u​e​u​e​ ​a​d​d​O​p​e​r​a​t​i​o​n​:​ ​a​l​l​D​o​n​e​]​;​

}​ ​/​/​ ​s​t​a​r​t​

This wraps up the app delegate class and this exercise. Compile and run your application.

Complex-Lifetime Operations

MandelOpper used simple-lifetime operations, meaning that -main started, ran to completion, and exited. In Mac OS X 10.5, it is a non-concurrent operation and gets its own thread.

You may have operations that do work that is asynchronous in nature, such as accessing the network or waiting for data to arrive from a radiation detector. These operations could wait in -main, or they could be implemented as complex-lifetime operations that have more control over their lifetime.

In Mac OS X 10.5, you can create concurrent operations by overriding -isConcurrent and returning YES. This tells NSOperationQueue to start the operation in the current thread. In 10.6, the operation will be started in its own thread. The Cocoa documentation makes vague and scary sounding references to “setting up the execution environment” in relation to concurrent operations. This just means that you need to do whatever setup is necessary for your operation to do its work. If you wanted to run your concurrent operation in your own thread, you would need to create this thread yourself (in Mac OS X 10.5). If you were doing a network operation, you would decide what server you want to talk to and make the connection.

When writing a complex-lifetime operation, you need to override four methods, compared to just overriding -main for a non-concurrent / simple-lifetime operation:

- (void) start;
- (BOOL) isConcurrent; // 10.5 only
- (BOOL) isExecuting;
- (BOOL) isFinished;

-start is where you “set up your execution environment” and start your work. This is where you would build your network connection or initiate asynchronous I/O.

It is important not to [super start]!

The default -start does extra work and calls -main, which defeats the purpose of making our own -start method.

KVO properties

Override -isExecuting and -isFinished so that they return YES if your operation is currently executing or if it has finished executing. These should be KVO compliant. That is, when your execution state transitions from “not executing” to “executing”, or when you transition to a “finished” state, you should make sure that KVO notifications for the isFinished and isExecuting key occur.

There are actually a number of properties you can observe with KVO on NSOperation. All are read-only, except for queuePriority:

isCancelled is YES if the operation has been cancelled, NO if it is still free to execute.

isConcurrent is YES if the operation sets up its own execution environment, NO if NSOperationQueue should create a thread for it.

isExecuting is YES if the operation is currently running. The value starts off at NO, changes to YES when the operation runs, and then changes back to NO when the operation stops running.

isFinished is YES when the operation has finished.

isReady is YES if the operation can be run. NSOperation looks at its dependencies to decide if it is ready.

dependencies is an NSArray of all of the operations this operation is dependent on.

queuePriority is one of the NSOperationQueuePriority* constants. This is a mutable property.

Be aware of the KVC/KVO work that is performed under the hood when you are determining the granularity of your operations. If you have very fine-grained operations, you may spend more processor time in KVO overhead than actually doing real work.

ImageSnarfer

The next exercise is ImageSnarfer, which loads a batch of images from a website, controlled by a text file containing URLs, and displays them in a view. The NSOperation subclass will be a complex-lifetime operation.

Figure 21.4  ImageSnarfer screen

The SnarfOperation class uses NSURLConnection to download an image asynchronously. This exercise uses Objective-C 2.0 features and KVO notifications.

There are two other classes involved: ImageCanvas, which draws an array of images (fed by SnarfOperations), and AppController, which reads a text file of URLs and creates operations. All of these classes are shown in Figure 21.5.

Figure 21.5  ImageSnarfer classes

ImageCanvas

Create a new Cocoa Application project called ImageSnarfer. Turn on Objective-C Garbage Collection (-fobjc-gc-only). Create a new NSView subclass called ImageCanvas. The canvas has an array of images and also an array of NSPoints (wrapped in NSValues) that hold the randomized locations in the window to draw each image. For example, index zero in the origins array is the point to draw the image at index zero in the images array. There is also a method to add a new image to the canvas. Edit ImageCanvas.h and add the class definition:

#​i​m​p​o​r​t​ ​<​C​o​c​o​a​/​C​o​c​o​a​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​I​m​a​g​e​C​a​n​v​a​s​ ​:​ ​N​S​V​i​e​w​ ​{​
 ​ ​ ​ ​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​*​_​i​m​a​g​e​s​;​ ​ ​/​/​ ​a​r​r​a​y​ ​o​f​ ​N​S​I​m​a​g​e​s​
 ​ ​ ​ ​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​*​_​o​r​i​g​i​n​s​;​ ​/​/​ ​a​r​r​a​y​ ​o​f​ ​N​S​P​o​i​n​t​ ​v​a​l​u​e​s​
}​

/​/​ ​A​d​d​ ​a​n​ ​i​m​a​g​e​ ​t​o​ ​t​h​e​ ​c​a​n​v​a​s​.​ ​ ​T​h​e​ ​c​a​n​v​a​s​ ​w​i​l​l​ ​c​h​o​o​s​e​ ​a​ ​r​a​n​d​o​m​ ​l​o​c​a​t​i​o​n​
/​/​ ​t​o​ ​d​r​a​w​ ​t​h​e​ ​i​m​a​g​e​.​
-​ ​(​v​o​i​d​)​ ​a​d​d​I​m​a​g​e​:​ ​(​N​S​I​m​a​g​e​ ​*​)​ ​i​m​a​g​e​;​

@​e​n​d​ ​/​/​ ​I​m​a​g​e​C​a​n​v​a​s​

Now edit ImageCanvas.m. The -initWithFrame: method creates the arrays. The -addImage: method puts an image on the list of images and chooses a random point where to draw the image.

#​i​m​p​o​r​t​ ​"​I​m​a​g​e​C​a​n​v​a​s​.​h​"​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​I​m​a​g​e​C​a​n​v​a​s​

-​ ​(​i​d​)​ ​i​n​i​t​W​i​t​h​F​r​a​m​e​:​ ​(​N​S​R​e​c​t​)​ ​f​r​a​m​e​ ​{​
 ​ ​ ​ ​i​f​ ​(​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​W​i​t​h​F​r​a​m​e​:​ ​f​r​a​m​e​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​t​h​e​ ​p​l​a​c​e​ ​t​o​ ​s​t​o​r​e​ ​t​h​e​ ​i​m​a​g​e​s​ ​a​n​d​ ​t​h​e​i​r​ ​d​i​s​p​l​a​y​ ​l​o​c​a​t​i​o​n​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​_​i​m​a​g​e​s​ ​=​ ​[​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​a​r​r​a​y​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​o​r​i​g​i​n​s​ ​=​ ​[​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​a​r​r​a​y​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​

}​ ​/​/​ ​i​n​i​t​W​i​t​h​F​r​a​m​e​

/​/​ ​A​d​d​ ​a​n​ ​i​m​a​g​e​ ​t​o​ ​t​h​e​ ​v​i​e​w​'​s​ ​l​i​s​t​ ​o​f​ ​i​m​a​g​e​s​ ​t​o​ ​d​i​s​p​l​a​y​.​

-​ ​(​v​o​i​d​)​ ​a​d​d​I​m​a​g​e​:​ ​(​N​S​I​m​a​g​e​ ​*​)​ ​i​m​a​g​e​ ​{​
 ​ ​ ​ ​/​/​ ​B​a​i​l​ ​o​u​t​ ​i​f​ ​w​e​'​r​e​ ​g​i​v​e​n​ ​a​n​ ​o​b​v​i​o​u​s​l​y​ ​b​a​d​ ​i​m​a​g​e​.​
 ​ ​ ​ ​i​f​ ​(​i​m​a​g​e​ ​=​=​ ​n​i​l​)​ ​r​e​t​u​r​n​;​

 ​ ​ ​ ​/​/​ ​A​d​d​ ​i​t​ ​t​o​ ​t​h​e​ ​l​i​s​t​.​
 ​ ​ ​ ​[​_​i​m​a​g​e​s​ ​a​d​d​O​b​j​e​c​t​:​ ​i​m​a​g​e​]​;​

 ​ ​ ​ ​/​/​ ​I​n​ ​p​a​r​a​l​l​e​l​ ​t​o​ ​t​h​e​ ​i​m​a​g​e​s​ ​l​i​s​t​,​ ​s​t​o​r​e​ ​a​n​ ​o​r​i​g​i​n​ ​N​S​P​o​i​n​t​ ​w​r​a​p​p​e​d​
 ​ ​ ​ ​/​/​ ​i​n​ ​a​n​ ​N​S​V​a​l​u​e​.​ ​ ​P​i​c​k​ ​a​ ​r​a​n​d​o​m​ ​l​o​c​a​t​i​o​n​ ​i​n​ ​t​h​e​ ​v​i​e​w​ ​s​o​ ​t​h​a​t​ ​t​h​e​
 ​ ​ ​ ​/​/​ ​e​n​t​i​r​e​ ​i​m​a​g​e​ ​i​s​ ​v​i​s​i​b​l​e​ ​i​n​ ​t​h​e​ ​v​i​e​w​.​
 ​ ​ ​ ​N​S​R​e​c​t​ ​b​o​u​n​d​s​ ​=​ ​[​s​e​l​f​ ​b​o​u​n​d​s​]​;​
 ​ ​ ​ ​i​n​t​ ​m​a​x​X​ ​=​ ​b​o​u​n​d​s​.​s​i​z​e​.​w​i​d​t​h​ ​-​ ​[​i​m​a​g​e​ ​s​i​z​e​]​.​w​i​d​t​h​;​
 ​ ​ ​ ​i​n​t​ ​m​a​x​Y​ ​=​ ​b​o​u​n​d​s​.​s​i​z​e​.​h​e​i​g​h​t​ ​-​ ​[​i​m​a​g​e​ ​s​i​z​e​]​.​h​e​i​g​h​t​;​

 ​ ​ ​ ​N​S​P​o​i​n​t​ ​o​r​i​g​i​n​ ​=​ ​N​S​M​a​k​e​P​o​i​n​t​ ​(​r​a​n​d​o​m​(​)​ ​%​ ​m​a​x​X​,​ ​r​a​n​d​o​m​(​)​ ​%​ ​m​a​x​Y​)​;​

 ​ ​ ​ ​/​/​ ​W​r​a​p​ ​t​h​e​ ​p​o​i​n​t​ ​i​n​ ​a​n​ ​N​S​V​a​l​u​e​ ​a​n​d​ ​a​d​d​ ​t​o​ ​t​h​e​ ​l​i​s​t​ ​o​f​ ​o​r​i​g​i​n​s​.​
 ​ ​ ​ ​N​S​V​a​l​u​e​ ​*​v​a​l​u​e​ ​=​ ​[​N​S​V​a​l​u​e​ ​v​a​l​u​e​W​i​t​h​P​o​i​n​t​:​ ​o​r​i​g​i​n​]​;​
 ​ ​ ​ ​[​_​o​r​i​g​i​n​s​ ​a​d​d​O​b​j​e​c​t​:​ ​v​a​l​u​e​]​;​

 ​ ​ ​ ​/​/​ ​N​e​w​ ​s​t​u​f​f​ ​t​o​ ​s​e​e​,​ ​s​o​ ​c​a​u​s​e​ ​a​ ​r​e​d​r​a​w​ ​s​o​m​e​t​i​m​e​ ​i​n​ ​t​h​e​ ​f​u​t​u​r​e​.​
 ​ ​ ​ ​[​s​e​l​f​ ​s​e​t​N​e​e​d​s​D​i​s​p​l​a​y​:​ ​Y​E​S​]​;​

}​ ​/​/​ ​a​d​d​I​m​a​g​e​

The final method for the ImageCanvas is draws the images. One point of interest is the array is being enumerated by a block. The block is passed the index of the currently enumerated item, which makes looking up the corresponding image origin very easy.

/​/​ ​D​i​s​p​l​a​y​ ​a​l​l​ ​o​f​ ​t​h​e​ ​i​m​a​g​e​s​.​
-​ ​(​v​o​i​d​)​ ​d​r​a​w​R​e​c​t​:​ ​(​N​S​R​e​c​t​)​ ​r​e​c​t​ ​{​

 ​ ​ ​ ​/​/​ ​R​e​d​r​a​w​ ​e​v​e​r​y​t​h​i​n​g​ ​e​v​e​r​y​ ​t​i​m​e​.​
 ​ ​ ​ ​N​S​R​e​c​t​ ​b​o​u​n​d​s​ ​=​ ​[​s​e​l​f​ ​b​o​u​n​d​s​]​;​

 ​ ​ ​ ​/​/​ ​M​a​k​e​ ​a​ ​n​i​c​e​ ​w​h​i​t​e​ ​b​a​c​k​g​r​o​u​n​d​.​
 ​ ​ ​ ​[​[​N​S​C​o​l​o​r​ ​w​h​i​t​e​C​o​l​o​r​]​ ​s​e​t​]​;​
 ​ ​ ​ ​N​S​R​e​c​t​F​i​l​l​ ​(​b​o​u​n​d​s​)​;​

 ​ ​ ​ ​/​/​ ​W​e​'​l​l​ ​b​e​ ​p​u​t​t​i​n​g​ ​a​ ​b​l​a​c​k​ ​r​e​c​t​a​n​g​l​e​ ​a​r​o​u​n​d​ ​e​a​c​h​ ​i​m​a​g​e​,​ ​s​o​ ​g​o​ ​a​h​e​a​d​
 ​ ​ ​ ​/​/​ ​a​n​d​ ​m​a​k​e​ ​b​l​a​c​k​ ​t​h​e​ ​c​u​r​r​e​n​t​ ​d​r​a​w​i​n​g​ ​c​o​l​o​r​.​
 ​ ​ ​ ​[​[​N​S​C​o​l​o​r​ ​b​l​a​c​k​C​o​l​o​r​]​ ​s​e​t​]​;​

 ​ ​ ​ ​/​/​ ​W​a​l​k​ ​t​h​e​ ​i​m​a​g​e​s​
 ​ ​ ​ ​[​_​i​m​a​g​e​s​ ​e​n​u​m​e​r​a​t​e​O​b​j​e​c​t​s​U​s​i​n​g​B​l​o​c​k​:​ ​^​(​i​d​ ​o​b​j​e​c​t​,​ ​N​S​U​I​n​t​e​g​e​r​ ​i​n​d​e​x​,​ ​B​O​O​L​ ​*​s​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​i​m​a​g​e​ ​t​o​ ​d​r​a​w​,​ ​a​n​d​ ​w​h​e​r​e​ ​i​t​ ​l​i​v​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​V​a​l​u​e​ ​*​p​o​i​n​t​V​a​l​u​e​ ​=​ ​[​_​o​r​i​g​i​n​s​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​ ​i​n​d​e​x​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​P​o​i​n​t​ ​o​r​i​g​i​n​ ​=​ ​[​p​o​i​n​t​V​a​l​u​e​ ​p​o​i​n​t​V​a​l​u​e​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​I​m​a​g​e​ ​*​i​m​a​g​e​ ​=​ ​(​N​S​I​m​a​g​e​ ​*​)​o​b​j​e​c​t​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​D​r​a​w​ ​t​h​e​ ​i​m​a​g​e​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​i​m​a​g​e​ ​d​r​a​w​A​t​P​o​i​n​t​:​ ​o​r​i​g​i​n​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​r​o​m​R​e​c​t​:​ ​N​S​Z​e​r​o​R​e​c​t​ ​/​/​ ​t​h​e​ ​e​n​t​i​r​e​ ​i​m​a​g​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​p​e​r​a​t​i​o​n​:​ ​N​S​C​o​m​p​o​s​i​t​e​C​o​p​y​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​r​a​c​t​i​o​n​:​ ​1​.​0​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​G​i​v​e​ ​i​t​ ​a​ ​n​i​c​e​ ​l​i​t​t​l​e​ ​b​o​r​d​e​r​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​R​e​c​t​ ​i​m​a​g​e​B​o​u​n​d​s​ ​=​ ​{​ ​o​r​i​g​i​n​,​ ​i​m​a​g​e​.​s​i​z​e​ ​}​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​F​r​a​m​e​R​e​c​t​ ​(​i​m​a​g​e​B​o​u​n​d​s​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​]​;​

 ​ ​ ​ ​N​S​F​r​a​m​e​R​e​c​t​ ​(​b​o​u​n​d​s​)​;​

}​ ​/​/​ ​d​r​a​w​R​e​c​t​

SnarfOperation

SnarfOperation is our NSOperation subclass that loads an image from the Internet using NSURLConnection. NSURLConnection uses an NSURLRequest to specify what URL to fetch, along with other request information, like HTTP headers and cookies. NSURLConnection informs a delegate with callbacks, which you will see shortly. Because NSURLConnection works asynchronously, we can use it as the workhorse for our concurrent NSOperation subclass.

Create a new class called SnarfOperation and add this interface to SnarfOperation.h. There is storage for the isExecuting and isFinished properties and a BOOL for noting whether the image loading was successful or had an error. There are also instance variables for the various bits of overhead needed for the data download.

#​i​m​p​o​r​t​ ​<​C​o​c​o​a​/​C​o​c​o​a​.​h​>​

/​/​ ​I​n​ ​1​0​.​5​,​ ​t​h​i​s​ ​w​o​u​l​d​ ​b​e​ ​a​ ​c​o​n​c​u​r​r​e​n​t​ ​o​p​e​r​a​t​i​o​n​,​ ​m​e​a​n​i​n​g​ ​t​h​a​t​ ​w​e​
/​/​ ​w​o​n​'​t​ ​g​e​t​ ​a​ ​t​h​r​e​a​d​ ​a​u​t​o​m​a​t​i​c​a​l​l​y​ ​f​r​o​m​ ​C​o​c​o​a​.​ ​ ​F​o​r​ ​1​0​.​6​,​ ​t​h​i​s​ ​i​s​ ​a​
/​/​ ​c​o​m​p​l​e​x​-​l​i​f​e​t​i​m​e​ ​o​p​e​r​a​t​i​o​n​,​ ​m​e​a​n​i​n​g​ ​w​e​ ​w​i​l​l​ ​b​e​ ​c​o​n​t​r​o​l​l​i​n​g​ ​t​h​e​
/​/​ ​o​b​j​e​c​t​ ​l​i​f​e​t​i​m​e​ ​a​n​d​ ​h​a​n​d​l​i​n​g​ ​t​h​e​ ​K​V​O​ ​f​o​o​b​a​g​e​.​
/​/​ ​A​l​l​ ​S​n​a​r​f​O​p​e​r​a​t​i​o​n​s​ ​o​p​e​r​a​t​i​o​n​ ​w​i​l​l​ ​p​e​r​f​o​r​m​ ​t​h​e​i​r​ ​u​r​l​ ​l​o​a​d​i​n​g​ ​o​n​ ​t​h​e​ ​m​a​i​n​ ​t​h​r​e​a​d​.​

@​i​n​t​e​r​f​a​c​e​ ​S​n​a​r​f​O​p​e​r​a​t​i​o​n​ ​:​ ​N​S​O​p​e​r​a​t​i​o​n​ ​{​
 ​ ​ ​ ​B​O​O​L​ ​_​i​s​E​x​e​c​u​t​i​n​g​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​Y​E​S​ ​i​f​ ​w​e​'​r​e​ ​l​o​a​d​i​n​g​ ​a​n​ ​i​m​a​g​e​
 ​ ​ ​ ​B​O​O​L​ ​_​i​s​F​i​n​i​s​h​e​d​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​Y​E​S​ ​i​f​ ​t​h​e​ ​i​m​a​g​e​ ​l​o​a​d​e​d​ ​(​o​r​ ​t​h​e​r​e​s​ ​a​n​ ​e​r​r​o​r​)​
 ​ ​ ​ ​B​O​O​L​ ​_​w​a​s​S​u​c​c​e​s​s​f​u​l​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​Y​E​S​ ​i​f​ ​l​i​f​e​ ​i​s​ ​g​r​o​o​v​y​

 ​ ​ ​ ​N​S​U​R​L​ ​*​_​u​r​l​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​h​e​r​e​ ​t​o​ ​l​o​a​d​ ​t​h​e​ ​i​m​a​g​e​ ​f​r​o​m​
 ​ ​ ​ ​N​S​I​m​a​g​e​ ​*​_​i​m​a​g​e​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​T​h​e​ ​c​o​m​p​l​e​t​e​d​ ​i​m​a​g​e​.​

 ​ ​ ​ ​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​*​_​c​o​n​n​e​c​t​i​o​n​;​ ​ ​/​/​ ​O​u​r​ ​c​o​n​d​u​i​t​ ​t​o​ ​t​h​e​ ​i​n​t​e​r​n​e​t​
 ​ ​ ​ ​N​S​M​u​t​a​b​l​e​D​a​t​a​ ​*​_​i​m​a​g​e​D​a​t​a​;​ ​ ​ ​ ​ ​/​/​ ​D​a​t​a​ ​a​c​c​u​m​u​l​a​t​e​s​ ​h​e​r​e​ ​a​s​ ​i​t​ ​c​o​m​e​s​ ​i​n​.​
}​

/​/​ ​G​e​n​e​r​a​t​e​ ​t​h​e​ ​a​p​p​r​o​p​r​i​a​t​e​ ​i​n​t​e​r​f​a​c​e​s​.​

@​p​r​o​p​e​r​t​y​ ​(​r​e​a​d​o​n​l​y​)​ ​B​O​O​L​ ​i​s​E​x​e​c​u​t​i​n​g​;​
@​p​r​o​p​e​r​t​y​ ​(​r​e​a​d​o​n​l​y​)​ ​B​O​O​L​ ​i​s​F​i​n​i​s​h​e​d​;​
@​p​r​o​p​e​r​t​y​ ​(​r​e​a​d​o​n​l​y​)​ ​B​O​O​L​ ​w​a​s​S​u​c​c​e​s​s​f​u​l​;​
@​p​r​o​p​e​r​t​y​ ​(​r​e​a​d​o​n​l​y​)​ ​N​S​U​R​L​ ​*​u​r​l​;​
@​p​r​o​p​e​r​t​y​ ​(​r​e​a​d​o​n​l​y​)​ ​N​S​I​m​a​g​e​ ​*​i​m​a​g​e​;​

/​/​ ​M​a​k​e​ ​a​ ​n​e​w​ ​o​p​e​r​a​t​i​o​n​ ​w​h​a​t​ ​U​R​L​ ​t​o​ ​d​o​w​n​l​o​a​d​ ​i​m​a​g​e​s​ ​f​r​o​m​.​
-​ ​(​i​d​)​ ​i​n​i​t​W​i​t​h​U​R​L​:​ ​(​N​S​U​R​L​ ​*​)​ ​u​r​l​;​

@​e​n​d​ ​/​/​ ​S​n​a​r​f​O​p​e​r​a​t​i​o​n​

Start off SnarfOperation.m with a redeclaration of the Objective-C 2.0 properties so that they can be modified in this source file and then generate the accessors:

#​i​m​p​o​r​t​ ​"​S​n​a​r​f​O​p​e​r​a​t​i​o​n​.​h​"​

/​/​ ​R​e​p​u​b​l​i​s​h​ ​o​u​r​ ​r​e​a​d​-​o​n​l​y​ ​p​r​o​p​e​r​t​i​e​s​ ​s​o​ ​w​e​ ​t​h​e​y​ ​a​r​e​ ​m​u​t​a​b​l​e​ ​f​o​r​ ​u​s​.​

@​i​n​t​e​r​f​a​c​e​ ​S​n​a​r​f​O​p​e​r​a​t​i​o​n​ ​(​)​

@​p​r​o​p​e​r​t​y​ ​(​r​e​a​d​w​r​i​t​e​)​ ​B​O​O​L​ ​i​s​E​x​e​c​u​t​i​n​g​;​
@​p​r​o​p​e​r​t​y​ ​(​r​e​a​d​w​r​i​t​e​)​ ​B​O​O​L​ ​i​s​F​i​n​i​s​h​e​d​;​
@​p​r​o​p​e​r​t​y​ ​(​r​e​a​d​w​r​i​t​e​)​ ​B​O​O​L​ ​w​a​s​S​u​c​c​e​s​s​f​u​l​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​,​ ​r​e​a​d​w​r​i​t​e​)​ ​N​S​I​m​a​g​e​ ​*​i​m​a​g​e​;​

@​e​n​d​ ​/​/​ ​S​n​a​r​f​O​p​e​r​a​t​i​o​n​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​S​n​a​r​f​O​p​e​r​a​t​i​o​n​

/​/​ ​G​e​n​e​r​a​t​e​ ​t​h​e​ ​a​c​c​e​s​s​o​r​s​.​
@​s​y​n​t​h​e​s​i​z​e​ ​i​s​E​x​e​c​u​t​i​n​g​ ​=​ ​_​i​s​E​x​e​c​u​t​i​n​g​;​
@​s​y​n​t​h​e​s​i​z​e​ ​i​s​F​i​n​i​s​h​e​d​ ​=​ ​_​i​s​F​i​n​i​s​h​e​d​;​
@​s​y​n​t​h​e​s​i​z​e​ ​w​a​s​S​u​c​c​e​s​s​f​u​l​ ​=​ ​_​w​a​s​S​u​c​c​e​s​s​f​u​l​;​

@​s​y​n​t​h​e​s​i​z​e​ ​u​r​l​ ​=​ ​_​u​r​l​;​
@​s​y​n​t​h​e​s​i​z​e​ ​i​m​a​g​e​ ​=​ ​_​i​m​a​g​e​;​

NSOperation is KVO compliant for its isExecuting and isFinished properties. We can change the values of these properties and have KVO notifications happen for us automatically, except that NSOperation disables it for these two properties. Add this class method to allow the automatic KVO machinery to work again for these properties.

/​/​ ​N​S​O​p​e​r​a​t​i​o​n​ ​d​i​s​e​n​g​a​g​e​s​ ​t​h​e​ ​K​V​O​ ​m​a​c​h​i​n​e​r​y​ ​f​o​r​ ​t​h​e​ ​"​f​i​n​i​s​h​e​d​"​ ​a​n​d​
/​/​ ​"​e​x​e​c​u​t​i​n​g​"​ ​p​r​o​p​e​r​t​i​e​s​.​ ​ ​R​e​-​e​n​g​a​g​e​ ​t​h​e​ ​a​u​t​o​m​a​t​i​c​ ​m​a​c​h​i​n​e​r​y​ ​t​o​ ​d​o​
/​/​ ​a​u​t​o​m​a​g​i​c​ ​K​V​O​ ​n​o​t​i​f​i​c​a​t​i​o​n​s​ ​w​h​e​n​ ​w​e​ ​c​h​a​n​g​e​ ​t​h​e​ ​v​a​l​u​e​s​.​

+​ ​(​B​O​O​L​)​ ​a​u​t​o​m​a​t​i​c​a​l​l​y​N​o​t​i​f​i​e​s​O​b​s​e​r​v​e​r​s​F​o​r​K​e​y​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​k​e​y​ ​{​
 ​ ​ ​ ​/​/​ ​W​e​'​r​e​ ​l​a​z​y​,​ ​s​o​ ​l​e​t​ ​C​o​c​o​a​ ​d​o​ ​a​l​l​ ​t​h​e​ ​K​V​O​ ​g​r​o​o​v​i​n​e​s​s​ ​f​o​r​ ​t​h​e​s​e​
 ​ ​ ​ ​/​/​ ​a​t​t​r​i​b​u​t​e​s​.​
 ​ ​ ​ ​i​f​ ​(​[​k​e​y​ ​i​s​E​q​u​a​l​T​o​S​t​r​i​n​g​:​ ​@​"​i​s​F​i​n​i​s​h​e​d​"​]​
 ​ ​ ​ ​ ​ ​ ​ ​|​|​ ​[​k​e​y​ ​i​s​E​q​u​a​l​T​o​S​t​r​i​n​g​:​@​"​i​s​E​x​e​c​u​t​i​n​g​"​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​Y​E​S​;​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​O​t​h​e​r​w​i​s​e​ ​d​o​ ​w​h​a​t​e​v​e​r​ ​N​S​O​p​e​r​a​t​i​o​n​ ​d​o​e​s​.​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​[​s​u​p​e​r​ ​a​u​t​o​m​a​t​i​c​a​l​l​y​N​o​t​i​f​i​e​s​O​b​s​e​r​v​e​r​s​F​o​r​K​e​y​:​ ​k​e​y​]​;​
 ​ ​ ​ ​}​

}​ ​/​/​ ​a​u​t​o​m​a​t​i​c​a​l​l​y​N​o​t​i​f​i​e​s​O​b​s​e​r​v​e​r​s​F​o​r​K​e​y​

-initWithURL: just hangs on to the URL. It does not actually begin the download until told to later.

-​ ​(​i​d​)​ ​i​n​i​t​W​i​t​h​U​R​L​:​ ​(​N​S​U​R​L​ ​*​)​ ​u​ ​{​
 ​ ​ ​ ​i​f​ ​(​(​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​_​u​r​l​ ​=​ ​u​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​

}​ ​/​/​ ​i​n​i​t​W​i​t​h​U​R​L​

Since this a complex-lifetime operation, don’t override -main but instead override -start, which is where we set up our execution environment. In our case, “setting up the execution environment” just entails starting the NSURLConnection:

-​ ​(​v​o​i​d​)​ ​s​t​a​r​t​ ​{​
 ​ ​ ​ ​/​/​ ​A​v​o​i​d​ ​u​n​n​e​c​e​s​s​a​r​y​ ​w​o​r​k​.​
 ​ ​ ​ ​i​f​ ​(​s​e​l​f​.​i​s​C​a​n​c​e​l​l​e​d​)​ ​r​e​t​u​r​n​;​

 ​ ​ ​ ​/​/​ ​I​n​ ​1​0​.​6​,​ ​a​l​l​ ​N​S​O​p​e​r​a​t​i​o​n​s​ ​a​r​e​ ​r​u​n​ ​o​n​ ​a​ ​t​h​r​e​a​d​.​ ​ ​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​i​s​
 ​ ​ ​ ​/​/​ ​r​u​n​l​o​o​p​ ​b​a​s​e​d​,​ ​a​n​d​ ​t​h​e​ ​t​h​r​e​a​d​ ​t​h​a​t​ ​w​e​ ​a​r​e​ ​i​n​i​t​i​a​l​l​y​ ​k​i​c​k​ ​o​f​f​ ​t​h​e​ ​c​o​n​n​e​c​t​
 ​ ​ ​ ​/​/​ ​w​i​t​h​ ​m​i​g​h​t​ ​d​i​s​a​p​p​e​a​r​,​ ​c​o​u​r​t​e​s​y​ ​o​f​ ​G​C​D​,​ ​b​e​f​o​r​e​ ​w​e​ ​f​i​n​i​s​h​ ​t​h​e​ ​l​o​a​d​i​n​g​
 ​ ​ ​ ​/​/​ ​o​p​e​r​a​t​i​o​n​.​
 ​ ​ ​ ​/​/​ ​S​o​ ​w​e​ ​f​o​r​c​e​ ​t​h​e​ ​r​u​n​ ​o​f​ ​o​u​r​s​e​l​v​e​s​ ​o​n​ ​t​h​e​ ​m​a​i​n​ ​t​h​r​e​a​d​,​ ​w​h​i​c​h​ ​w​e​ ​k​n​o​w​ ​w​i​l​l​
 ​ ​ ​ ​/​/​ ​s​t​i​c​k​ ​a​r​o​u​n​d​,​ ​a​n​d​ ​g​l​o​m​ ​o​n​ ​t​o​ ​i​t​s​ ​r​u​n​l​o​o​p​ ​t​o​ ​r​u​n​ ​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​.​

 ​ ​ ​ ​i​f​ ​(​!​[​N​S​T​h​r​e​a​d​ ​i​s​M​a​i​n​T​h​r​e​a​d​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​p​e​r​f​o​r​m​S​e​l​e​c​t​o​r​O​n​M​a​i​n​T​h​r​e​a​d​:​ ​@​s​e​l​e​c​t​o​r​(​s​t​a​r​t​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​t​h​O​b​j​e​c​t​:​ ​n​i​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​a​i​t​U​n​t​i​l​D​o​n​e​:​ ​N​O​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​S​i​n​c​e​ ​w​e​'​v​e​ ​s​t​a​r​t​e​d​,​ ​w​e​'​r​e​ ​e​x​e​c​u​t​i​n​g​.​
 ​ ​ ​ ​s​e​l​f​.​i​s​E​x​e​c​u​t​i​n​g​ ​=​ ​Y​E​S​;​

 ​ ​ ​ ​/​/​ ​B​u​i​l​d​ ​a​ ​r​e​q​u​e​s​t​ ​t​o​ ​f​e​t​c​h​ ​t​h​e​ ​i​m​a​g​e​.​ ​ ​T​h​e​ ​d​e​f​a​u​l​t​ ​U​R​L​s​ ​i​n​ ​t​h​e​
 ​ ​ ​ ​/​/​ ​s​a​m​p​l​e​ ​a​r​e​ ​a​t​ ​b​o​r​k​w​a​r​e​.​c​o​m​,​ ​a​n​d​ ​t​h​i​s​ ​w​i​l​l​ ​l​e​t​ ​u​s​ ​k​n​o​w​ ​h​o​w​ ​m​u​c​h​
 ​ ​ ​ ​/​/​ ​t​r​a​f​f​i​c​ ​i​s​ ​b​e​i​n​g​ ​g​e​n​e​r​a​t​e​d​ ​b​y​ ​t​h​i​s​ ​s​a​m​p​l​e​.​
 ​ ​ ​ ​/​/​ ​C​o​o​k​i​e​s​ ​a​r​e​ ​a​l​s​o​ ​t​u​r​n​e​d​ ​o​f​f​ ​t​o​ ​r​e​d​u​c​e​ ​l​o​a​d​ ​o​n​ ​t​h​e​ ​s​e​r​v​e​r​.​
 ​ ​ ​ ​N​S​M​u​t​a​b​l​e​U​R​L​R​e​q​u​e​s​t​ ​*​r​e​q​u​e​s​t​ ​=​ ​[​N​S​M​u​t​a​b​l​e​U​R​L​R​e​q​u​e​s​t​ ​r​e​q​u​e​s​t​W​i​t​h​U​R​L​:​ ​s​e​l​f​.​u​r​l​]​;​
 ​ ​ ​ ​[​r​e​q​u​e​s​t​ ​s​e​t​V​a​l​u​e​:​ ​@​"​I​m​a​g​e​S​n​a​r​f​e​r​"​ ​ ​f​o​r​H​T​T​P​H​e​a​d​e​r​F​i​e​l​d​:​ ​@​"​U​s​e​r​-​A​g​e​n​t​"​]​;​
 ​ ​ ​ ​[​r​e​q​u​e​s​t​ ​s​e​t​H​T​T​P​S​h​o​u​l​d​H​a​n​d​l​e​C​o​o​k​i​e​s​:​ ​N​O​]​;​
 ​ ​ ​ ​[​r​e​q​u​e​s​t​ ​s​e​t​C​a​c​h​e​P​o​l​i​c​y​:​ ​N​S​U​R​L​R​e​q​u​e​s​t​R​e​l​o​a​d​I​g​n​o​r​i​n​g​L​o​c​a​l​C​a​c​h​e​D​a​t​a​]​;​

 ​ ​ ​ ​/​/​ ​_​i​m​a​g​e​D​a​t​a​ ​h​o​l​d​s​ ​t​h​e​ ​d​a​t​a​ ​t​h​a​t​ ​c​o​m​e​s​ ​d​o​w​n​ ​f​r​o​m​ ​t​h​e​ ​i​n​t​e​r​n​e​t​.​
 ​ ​ ​ ​_​i​m​a​g​e​D​a​t​a​ ​=​ ​[​N​S​M​u​t​a​b​l​e​D​a​t​a​ ​d​a​t​a​]​;​

 ​ ​ ​ ​_​c​o​n​n​e​c​t​i​o​n​ ​=​ ​[​[​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​R​e​q​u​e​s​t​:​ ​r​e​q​u​e​s​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​l​e​g​a​t​e​:​ ​s​e​l​f​]​;​
 ​ ​ ​ ​[​_​c​o​n​n​e​c​t​i​o​n​ ​s​t​a​r​t​]​;​

}​ ​/​/​ ​s​t​a​r​t​

The -done method is a utility method used elsewhere in the class. It releases the reference to the NSURLConnection so that it will get collected and sets the executing and finished attributes. KVO notifications will happen automatically because the values are changing through a property assignment.

-​ ​(​v​o​i​d​)​ ​d​o​n​e​ ​{​
 ​ ​ ​ ​_​c​o​n​n​e​c​t​i​o​n​ ​=​ ​n​i​l​;​

 ​ ​ ​ ​s​e​l​f​.​i​s​E​x​e​c​u​t​i​n​g​ ​=​ ​N​O​;​
 ​ ​ ​ ​s​e​l​f​.​i​s​F​i​n​i​s​h​e​d​ ​=​ ​Y​E​S​;​

}​ ​/​/​ ​d​o​n​e​

One user of -done is -cancel, which is called when the operation is cancelled. We may have a connection currently in use, so tell the connection to cancel itself. Also throw away any data that may have accumulated.

-​ ​(​v​o​i​d​)​ ​c​a​n​c​e​l​ ​{​
 ​ ​ ​ ​[​_​c​o​n​n​e​c​t​i​o​n​ ​c​a​n​c​e​l​]​;​

 ​ ​ ​ ​_​i​m​a​g​e​D​a​t​a​ ​=​ ​n​i​l​;​

 ​ ​ ​ ​[​s​e​l​f​ ​d​o​n​e​]​;​
 ​ ​ ​ ​[​s​u​p​e​r​ ​c​a​n​c​e​l​]​;​

}​ ​/​/​ ​c​a​n​c​e​l​

-isExecuting and -isFinished need to be overridden when making a concurrent operation. Luckily, the implementations of these were created automatically for us by the isExecuting and isFinished properties.

-image builds an NSImage and returns it to the caller.

-​ ​(​N​S​I​m​a​g​e​ ​*​)​ ​i​m​a​g​e​ ​{​
 ​ ​ ​ ​i​f​ ​(​s​e​l​f​.​w​a​s​S​u​c​c​e​s​s​f​u​l​ ​&​&​ ​_​i​m​a​g​e​ ​=​=​ ​n​i​l​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​U​s​e​ ​t​h​e​ ​p​r​o​p​e​r​t​y​ ​s​o​ ​t​h​a​t​ ​s​o​m​e​o​n​e​ ​i​n​t​e​r​e​s​t​e​d​ ​i​n​ ​t​h​e​ ​i​m​a​g​e​ ​v​i​a​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​K​V​O​ ​w​i​l​l​ ​g​e​t​ ​a​ ​n​o​t​i​f​i​c​a​t​i​o​n​.​
 ​ ​ ​ ​ ​ ​ ​ ​s​e​l​f​.​i​m​a​g​e​ ​=​ ​[​[​N​S​I​m​a​g​e​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​D​a​t​a​:​ ​_​i​m​a​g​e​D​a​t​a​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​i​m​a​g​e​D​a​t​a​ ​=​ ​n​i​l​;​ ​/​/​ ​D​o​n​'​t​ ​n​e​e​d​ ​t​o​ ​h​a​n​g​ ​o​n​ ​t​o​ ​i​t​ ​a​n​y​m​o​r​e​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​_​i​m​a​g​e​;​

}​ ​/​/​ ​i​m​a​g​e​

NSURLConnection delegate methods

The rest of SnarfOperation’s implementation is four delegate methods that are called during the URL loading process:

-connection:didReceiveResponse: is called when the connection is made to the web server and a response has been received. This may be called multiple times, like in the face of HTTP redirects, so forget about any data that may have been previously downloaded.

-​ ​(​v​o​i​d​)​ ​c​o​n​n​e​c​t​i​o​n​:​ ​(​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​*​)​ ​c​
 ​d​i​d​R​e​c​e​i​v​e​R​e​s​p​o​n​s​e​:​ ​(​N​S​U​R​L​R​e​s​p​o​n​s​e​ ​*​)​ ​r​e​s​p​o​n​s​e​ ​{​

 ​ ​ ​ ​/​/​ ​T​h​i​s​ ​c​a​n​ ​b​e​ ​c​a​l​l​e​d​ ​m​u​l​t​p​l​e​ ​t​i​m​e​s​ ​(​l​i​k​e​ ​f​o​r​ ​r​e​d​i​r​e​c​t​i​o​n​)​,​
 ​ ​ ​ ​/​/​ ​s​o​ ​t​o​s​s​ ​a​n​y​ ​a​c​c​u​m​u​l​a​t​e​d​ ​d​a​t​a​.​

 ​ ​ ​ ​[​_​i​m​a​g​e​D​a​t​a​ ​s​e​t​L​e​n​g​t​h​:​ ​0​]​;​

}​ ​/​/​ ​d​i​d​R​e​c​e​i​v​e​R​e​s​p​o​n​s​e​

-connection:didReceiveData: will be called zero or more times with data received from the web server.

-​ ​(​v​o​i​d​)​ ​c​o​n​n​e​c​t​i​o​n​:​ ​(​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​*​)​ ​c​
 ​ ​ ​ ​ ​d​i​d​R​e​c​e​i​v​e​D​a​t​a​:​ ​(​N​S​D​a​t​a​ ​*​)​ ​d​a​t​a​ ​{​

 ​ ​ ​ ​[​_​i​m​a​g​e​D​a​t​a​ ​a​p​p​e​n​d​D​a​t​a​:​ ​d​a​t​a​]​;​

}​ ​/​/​ ​d​i​d​R​e​c​e​i​v​e​D​a​t​a​

-connection:didFailWithError: will be called if there was a failure, such as an inability to connect with the web server, or if the family dog pulled out the network cable part way through. You should release the accumulated data, make a note that the operation failed, and then tell observers that the operation is done.

-​ ​(​v​o​i​d​)​ ​c​o​n​n​e​c​t​i​o​n​:​ ​(​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​*​)​ ​c​
 ​ ​ ​d​i​d​F​a​i​l​W​i​t​h​E​r​r​o​r​:​ ​(​N​S​E​r​r​o​r​ ​*​)​ ​e​r​r​o​r​ ​{​

 ​ ​ ​ ​_​i​m​a​g​e​D​a​t​a​ ​=​ ​n​i​l​;​

 ​ ​ ​ ​s​e​l​f​.​w​a​s​S​u​c​c​e​s​s​f​u​l​ ​=​ ​N​O​;​
 ​ ​ ​ ​[​s​e​l​f​ ​d​o​n​e​]​;​

}​ ​/​/​ ​d​i​d​F​a​i​l​W​i​t​h​E​r​r​o​r​

Finally, -connectionDidFinishLoading is called when all the data has been successfully read from the web server. Make a note that the operation was a success.

-​ ​(​v​o​i​d​)​ ​c​o​n​n​e​c​t​i​o​n​D​i​d​F​i​n​i​s​h​L​o​a​d​i​n​g​:​ ​(​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​*​)​ ​c​ ​{​

 ​ ​ ​ ​s​e​l​f​.​w​a​s​S​u​c​c​e​s​s​f​u​l​ ​=​ ​Y​E​S​;​
 ​ ​ ​ ​[​s​e​l​f​ ​d​o​n​e​]​;​

}​ ​/​/​ ​c​o​n​n​e​c​t​i​o​n​D​i​d​F​i​n​i​s​h​L​o​a​d​i​n​g​

@​e​n​d​ ​/​/​ ​S​n​a​r​f​O​p​e​r​a​t​i​o​n​

ImageSnarferAppDelegate

The last class for ImageSnarfer is the ImageSnarferAppDelegate, created for you in the Xcode template. Here is the header:

#​i​m​p​o​r​t​ ​<​C​o​c​o​a​/​C​o​c​o​a​.​h​>​

@​c​l​a​s​s​ ​I​m​a​g​e​C​a​n​v​a​s​;​

@​i​n​t​e​r​f​a​c​e​ ​I​m​a​g​e​S​n​a​r​f​e​r​A​p​p​D​e​l​e​g​a​t​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​{​
 ​ ​ ​ ​N​S​O​p​e​r​a​t​i​o​n​Q​u​e​u​e​ ​ ​ ​ ​ ​*​_​r​u​n​Q​u​e​u​e​;​ ​ ​/​/​ ​H​o​l​d​s​ ​t​h​e​ ​i​m​a​g​e​ ​l​o​a​d​i​n​g​ ​o​p​e​r​a​t​i​o​n​s​
 ​ ​ ​ ​I​m​a​g​e​C​a​n​v​a​s​ ​*​_​i​m​a​g​e​C​a​n​v​a​s​;​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​h​e​r​e​ ​t​o​ ​d​r​a​w​ ​t​h​e​ ​i​m​a​g​e​s​
 ​ ​ ​ ​i​n​t​ ​_​c​o​n​c​u​r​r​e​n​t​E​x​e​c​u​t​i​o​n​;​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​T​h​e​ ​n​u​m​b​e​r​ ​o​f​ ​o​p​e​r​a​t​i​o​n​s​ ​c​u​r​r​e​n​t​l​y​ ​i​n​-​f​l​i​g​h​t​.​
}​

@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​I​m​a​g​e​C​a​n​v​a​s​ ​*​i​m​a​g​e​C​a​n​v​a​s​;​

/​/​ ​S​t​a​r​t​ ​l​o​a​d​i​n​g​ ​t​h​e​ ​i​m​a​g​e​s​.​
-​ ​(​I​B​A​c​t​i​o​n​)​ ​s​t​a​r​t​:​ ​(​i​d​)​ ​s​e​n​d​e​r​;​

@​e​n​d​ ​/​/​ ​I​m​a​g​e​S​n​a​r​f​e​r​A​p​p​D​e​l​e​g​a​t​e​

There are three instance variables: the NSOperationQueue that will hold a lot of SnarfOperations, an ImageCanvas to draw loaded images, and a count of the number of operations that are currently running. This count will be updated by observing the operations and seeing when they start and stop.

In ImageSnarferAppDelegate.m, start out with some local property definitions so that getters and setters will be generated. The user interface will access them via bindings.

#​i​m​p​o​r​t​ ​"​I​m​a​g​e​S​n​a​r​f​e​r​A​p​p​D​e​l​e​g​a​t​e​.​h​"​

#​i​m​p​o​r​t​ ​"​S​n​a​r​f​O​p​e​r​a​t​i​o​n​.​h​"​
#​i​m​p​o​r​t​ ​"​I​m​a​g​e​C​a​n​v​a​s​.​h​"​

/​/​ ​T​e​l​l​ ​O​b​j​e​c​t​i​v​e​-​C​ ​t​h​a​t​ ​t​h​e​s​e​ ​a​r​e​ ​p​r​o​p​e​r​t​i​e​s​.​ ​ ​T​h​e​s​e​ ​a​r​e​ ​n​o​t​ ​i​n​
/​/​ ​t​h​e​ ​h​e​a​d​e​r​ ​b​e​c​a​u​s​e​ ​u​s​e​r​s​ ​o​f​ ​t​h​i​s​ ​c​l​a​s​s​ ​s​h​o​u​l​d​n​'​t​ ​b​e​ ​p​o​k​i​n​g​ ​a​t​ ​t​h​e​s​e​.​

@​i​n​t​e​r​f​a​c​e​ ​I​m​a​g​e​S​n​a​r​f​e​r​A​p​p​D​e​l​e​g​a​t​e​ ​(​)​
@​p​r​o​p​e​r​t​y​ ​N​S​O​p​e​r​a​t​i​o​n​Q​u​e​u​e​ ​*​r​u​n​Q​u​e​u​e​;​
@​p​r​o​p​e​r​t​y​ ​i​n​t​ ​c​o​n​c​u​r​r​e​n​t​E​x​e​c​u​t​i​o​n​;​
@​e​n​d​ ​/​/​ ​e​x​t​e​n​s​i​o​n​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​I​m​a​g​e​S​n​a​r​f​e​r​A​p​p​D​e​l​e​g​a​t​e​

@​s​y​n​t​h​e​s​i​z​e​ ​r​u​n​Q​u​e​u​e​ ​=​ ​_​r​u​n​Q​u​e​u​e​;​
@​s​y​n​t​h​e​s​i​z​e​ ​i​m​a​g​e​C​a​n​v​a​s​ ​=​ ​_​i​m​a​g​e​C​a​n​v​a​s​;​
@​s​y​n​t​h​e​s​i​z​e​ ​c​o​n​c​u​r​r​e​n​t​E​x​e​c​u​t​i​o​n​ ​=​ ​_​c​o​n​c​u​r​r​e​n​t​E​x​e​c​u​t​i​o​n​;​

The URLs to fetch are http:// URLs in a text file in the application’s bundle, one to a line. The -contents method reads the file, puts the URL strings into an array, and randomizes it.

-​ ​(​N​S​A​r​r​a​y​ ​*​)​ ​c​o​n​t​e​n​t​s​ ​{​
 ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​p​a​t​h​ ​t​o​ ​t​h​e​ ​c​o​n​t​e​n​t​s​ ​f​i​l​e​.​
 ​ ​ ​ ​N​S​B​u​n​d​l​e​ ​*​b​u​n​d​l​e​ ​=​ ​[​N​S​B​u​n​d​l​e​ ​m​a​i​n​B​u​n​d​l​e​]​;​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​c​o​n​t​e​n​t​s​P​a​t​h​ ​=​ ​[​b​u​n​d​l​e​ ​p​a​t​h​F​o​r​R​e​s​o​u​r​c​e​:​ ​@​"​c​o​n​t​e​n​t​s​"​ ​ ​o​f​T​y​p​e​:​ ​@​"​t​x​t​"​]​;​

 ​ ​ ​ ​/​/​ ​R​e​a​d​ ​t​h​e​ ​f​i​l​e​ ​a​n​d​ ​t​u​r​n​ ​i​t​ ​i​n​t​o​ ​a​n​ ​a​r​r​a​y​ ​o​f​ ​s​t​r​i​n​g​s​ ​s​p​l​i​t​
 ​ ​ ​ ​/​/​ ​o​n​ ​n​e​w​l​i​n​e​s​.​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​c​o​n​t​e​n​t​s​S​t​r​i​n​g​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​C​o​n​t​e​n​t​s​O​f​F​i​l​e​:​ ​c​o​n​t​e​n​t​s​P​a​t​h​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​c​o​d​i​n​g​:​ ​N​S​U​T​F​8​S​t​r​i​n​g​E​n​c​o​d​i​n​g​ ​ ​e​r​r​o​r​:​ ​N​U​L​L​]​;​

 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​r​a​w​C​o​n​t​e​n​t​s​ ​=​ ​[​c​o​n​t​e​n​t​s​S​t​r​i​n​g​ ​c​o​m​p​o​n​e​n​t​s​S​e​p​a​r​a​t​e​d​B​y​S​t​r​i​n​g​:​ ​@​"​\​n​"​]​;​
 ​ ​ ​ ​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​*​m​u​t​a​b​l​e​C​o​n​t​e​n​t​s​ ​=​ ​[​r​a​w​C​o​n​t​e​n​t​s​ ​m​u​t​a​b​l​e​C​o​p​y​]​;​

 ​ ​ ​ ​/​/​ ​E​a​t​ ​a​ ​s​t​r​a​y​ ​n​e​w​l​i​n​e​ ​a​t​ ​t​h​e​ ​e​n​d​ ​w​h​i​c​h​ ​w​o​u​l​d​ ​c​a​u​s​e​ ​a​n​ ​e​m​p​t​y​
 ​ ​ ​ ​/​/​ ​s​t​r​i​n​g​.​
 ​ ​ ​ ​N​S​U​I​n​t​e​g​e​r​ ​c​o​u​n​t​ ​=​ ​[​m​u​t​a​b​l​e​C​o​n​t​e​n​t​s​ ​c​o​u​n​t​]​;​
 ​ ​ ​ ​i​f​ ​(​c​o​u​n​t​ ​=​=​ ​0​)​ ​g​o​t​o​ ​b​a​i​l​o​u​t​;​

 ​ ​ ​ ​i​f​ ​(​[​[​m​u​t​a​b​l​e​C​o​n​t​e​n​t​s​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​ ​c​o​u​n​t​ ​-​ ​1​]​ ​l​e​n​g​t​h​]​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​m​u​t​a​b​l​e​C​o​n​t​e​n​t​s​ ​r​e​m​o​v​e​L​a​s​t​O​b​j​e​c​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​u​n​t​-​-​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​S​h​u​f​f​l​e​ ​t​h​e​ ​a​r​r​a​y​.​
 ​ ​ ​ ​s​r​a​n​d​o​m​ ​(​t​i​m​e​(​N​U​L​L​)​)​;​

 ​ ​ ​ ​/​/​ ​S​h​u​f​f​l​e​ ​t​h​e​ ​i​m​a​g​e​ ​a​d​d​r​e​s​s​e​s​.​
 ​ ​ ​ ​f​o​r​ ​(​N​S​U​I​n​t​e​g​e​r​ ​i​ ​=​ ​c​o​u​n​t​ ​-​ ​1​;​ ​i​ ​!​=​ ​0​;​ ​i​-​-​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​U​I​n​t​e​g​e​r​ ​n​e​w​I​n​d​e​x​ ​=​ ​r​a​n​d​o​m​(​)​ ​%​ ​i​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​m​u​t​a​b​l​e​C​o​n​t​e​n​t​s​ ​e​x​c​h​a​n​g​e​O​b​j​e​c​t​A​t​I​n​d​e​x​:​ ​i​ ​ ​w​i​t​h​O​b​j​e​c​t​A​t​I​n​d​e​x​:​ ​n​e​w​I​n​d​e​x​]​;​
 ​ ​ ​ ​}​

b​a​i​l​o​u​t​:​
 ​ ​ ​ ​r​e​t​u​r​n​ ​m​u​t​a​b​l​e​C​o​n​t​e​n​t​s​;​

}​ ​/​/​ ​c​o​n​t​e​n​t​s​

-start: is an action hooked up to a button which initiates the downloading process. First are some preliminaries, such as canceling any previous runs and making an NSOperationQueue. The queue is manually set to have a concurrency of four to be nice to the web server. The number of requests to put into the queue is capped at 50, also to be nice to the web server. You can get a pleasing display with 50 images vs. using every URL in the contents file.

-​ ​(​I​B​A​c​t​i​o​n​)​ ​s​t​a​r​t​:​ ​(​i​d​)​ ​s​e​n​d​e​r​ ​{​
 ​ ​ ​ ​/​/​ ​S​t​o​p​ ​a​n​y​ ​w​o​r​k​ ​t​h​a​t​'​s​ ​i​n​-​p​r​o​g​r​e​s​s​.​
 ​ ​ ​ ​[​s​e​l​f​.​r​u​n​Q​u​e​u​e​ ​c​a​n​c​e​l​A​l​l​O​p​e​r​a​t​i​o​n​s​]​;​

 ​ ​ ​ ​/​/​ ​M​a​k​e​ ​a​ ​n​e​w​ ​q​u​e​u​e​ ​t​o​ ​p​u​t​ ​o​u​r​ ​o​p​e​r​a​t​i​o​n​s​.​
 ​ ​ ​ ​s​e​l​f​.​r​u​n​Q​u​e​u​e​ ​=​ ​[​[​N​S​O​p​e​r​a​t​i​o​n​Q​u​e​u​e​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​/​/​ ​S​i​n​c​e​ ​w​e​'​r​e​ ​f​u​n​n​e​l​i​n​g​ ​e​v​e​r​y​t​h​i​n​g​ ​i​n​t​o​ ​o​n​e​ ​t​h​r​e​a​d​,​ ​w​e​
 ​ ​ ​ ​/​/​ ​d​o​n​'​t​ ​r​e​a​l​l​y​ ​c​a​r​e​ ​a​b​o​u​t​ ​t​h​e​ ​n​u​m​b​e​r​ ​o​f​ ​p​r​o​c​e​s​s​e​r​s​.​ ​ ​B​u​t​ ​i​t​'​s​ ​g​o​o​d​
 ​ ​ ​ ​/​/​ ​t​o​ ​t​h​r​o​t​t​l​e​ ​o​u​r​s​e​l​v​e​s​ ​s​o​ ​w​e​'​r​e​ ​n​o​t​ ​t​o​o​ ​m​e​a​n​ ​t​o​ ​t​h​e​ ​s​e​r​v​e​r​.​
 ​ ​ ​ ​[​s​e​l​f​.​r​u​n​Q​u​e​u​e​ ​s​e​t​M​a​x​C​o​n​c​u​r​r​e​n​t​O​p​e​r​a​t​i​o​n​C​o​u​n​t​:​ ​4​]​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​s​e​t​ ​o​f​ ​U​R​L​s​ ​t​o​ ​l​o​a​d​
 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​c​o​n​t​e​n​t​s​ ​=​ ​[​s​e​l​f​ ​c​o​n​t​e​n​t​s​]​;​

 ​ ​ ​ ​/​/​ ​A​l​s​o​ ​b​e​ ​n​i​c​e​ ​t​o​ ​t​h​e​ ​s​e​r​v​e​r​.​ ​ ​5​0​ ​s​h​o​u​l​d​ ​b​e​ ​e​n​o​u​g​h​ ​t​o​ ​d​e​m​o​n​s​t​r​a​t​e​
 ​ ​ ​ ​/​/​ ​c​o​n​c​u​r​r​e​n​c​y​ ​a​n​d​ ​s​t​i​l​l​ ​g​i​v​e​ ​a​ ​p​l​e​a​s​i​n​g​ ​d​i​s​p​l​a​y​,​ ​v​s​ ​a​l​w​a​y​s​ ​l​o​a​d​i​n​g​
 ​ ​ ​ ​/​/​ ​2​9​0​ ​i​m​a​g​e​s​ ​e​v​e​r​y​ ​r​u​n​.​
 ​ ​ ​ ​i​n​t​ ​m​a​x​ ​=​ ​M​I​N​ ​(​[​c​o​n​t​e​n​t​s​ ​c​o​u​n​t​]​,​ ​5​0​)​;​

Then convert the URL strings to NSURLs and make a SnarfOperation for each one. Add KVO observers for isFinished (so you know the operation is done) and isExecuting (when it starts and stop executing, so you can keep the concurrency count accurate).

Finally the operation is added to the run queue:

 ​ ​ ​ ​/​/​ ​W​a​l​k​ ​t​h​e​ ​a​r​r​a​y​ ​a​n​d​ ​m​a​k​e​ ​d​o​w​n​l​o​a​d​ ​o​p​e​r​a​t​i​o​n​s​.​
 ​ ​ ​ ​f​o​r​ ​(​N​S​I​n​t​e​g​e​r​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​m​a​x​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​T​u​r​n​ ​t​h​e​ ​s​t​r​i​n​g​ ​i​n​t​o​ ​a​n​ ​U​R​L​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​u​r​l​S​t​r​i​n​g​ ​=​ ​[​c​o​n​t​e​n​t​s​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​ ​i​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​ ​*​u​r​l​ ​=​ ​[​N​S​U​R​L​ ​U​R​L​W​i​t​h​S​t​r​i​n​g​:​ ​u​r​l​S​t​r​i​n​g​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​M​a​k​e​ ​a​ ​n​e​w​ ​o​p​e​r​a​t​i​o​n​ ​t​o​ ​d​o​w​n​l​o​a​d​ ​t​h​a​t​ ​U​R​L​
 ​ ​ ​ ​ ​ ​ ​ ​S​n​a​r​f​O​p​e​r​a​t​i​o​n​ ​*​o​p​ ​=​ ​[​[​S​n​a​r​f​O​p​e​r​a​t​i​o​n​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​U​R​L​:​ ​u​r​l​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​a​t​c​h​ ​f​o​r​ ​t​h​e​ ​f​i​n​i​s​h​e​d​ ​s​t​a​t​e​ ​o​f​ ​t​h​i​s​ ​o​p​e​r​a​t​i​o​n​.​
 ​ ​ ​ ​ ​ ​ ​ ​[​o​p​ ​a​d​d​O​b​s​e​r​v​e​r​:​ ​s​e​l​f​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​r​K​e​y​P​a​t​h​:​ ​@​"​i​s​F​i​n​i​s​h​e​d​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​p​t​i​o​n​s​:​ ​0​ ​ ​/​/​ ​I​t​ ​j​u​s​t​ ​c​h​a​n​g​e​s​ ​s​t​a​t​e​ ​o​n​c​e​,​ ​s​o​ ​d​o​n​'​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​w​o​r​r​y​ ​a​b​o​u​t​ ​w​h​a​t​'​s​ ​i​n​ ​t​h​e​ ​n​o​t​i​f​i​c​a​t​i​o​n​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​e​x​t​:​ ​N​U​L​L​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​W​a​t​c​h​ ​f​o​r​ ​w​h​e​n​ ​t​h​i​s​ ​o​p​e​r​a​t​i​o​n​ ​s​t​a​r​t​s​ ​e​x​e​c​u​t​i​n​g​,​ ​s​o​ ​w​e​ ​c​a​n​ ​u​p​d​a​t​e​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​t​h​e​ ​u​s​e​r​ ​i​n​t​e​r​f​a​c​e​.​
 ​ ​ ​ ​ ​ ​ ​ ​[​o​p​ ​a​d​d​O​b​s​e​r​v​e​r​:​ ​s​e​l​f​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​r​K​e​y​P​a​t​h​:​ ​@​"​i​s​E​x​e​c​u​t​i​n​g​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​p​t​i​o​n​s​:​ ​N​S​K​e​y​V​a​l​u​e​O​b​s​e​r​v​i​n​g​O​p​t​i​o​n​N​e​w​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​e​x​t​:​ ​N​U​L​L​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​S​c​h​e​d​u​l​e​ ​t​h​e​ ​o​p​e​r​a​t​i​o​n​ ​f​o​r​ ​r​u​n​n​i​n​g​.​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​.​r​u​n​Q​u​e​u​e​ ​a​d​d​O​p​e​r​a​t​i​o​n​:​ ​o​p​]​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​s​t​a​r​t​

The last method is the KVO observation method. Put the image onto the ImageCanvas and unhook the app delegate from the KVO machinery when isFinished floats by. When isExecuting floats by, update the concurrentExecution instance variable.

-​ ​(​v​o​i​d​)​ ​o​b​s​e​r​v​e​V​a​l​u​e​F​o​r​K​e​y​P​a​t​h​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​k​e​y​P​a​t​h​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​f​O​b​j​e​c​t​:​ ​(​i​d​)​ ​o​b​j​e​c​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​h​a​n​g​e​:​ ​(​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​)​ ​c​h​a​n​g​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​e​x​t​:​ ​(​v​o​i​d​ ​*​)​ ​c​o​n​t​e​x​t​ ​{​

 ​ ​ ​ ​i​f​ ​(​[​k​e​y​P​a​t​h​ ​i​s​E​q​u​a​l​T​o​S​t​r​i​n​g​:​ ​@​"​i​s​F​i​n​i​s​h​e​d​"​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​I​f​ ​i​t​'​s​ ​d​o​n​e​,​ ​i​t​ ​h​a​s​ ​d​o​w​n​l​o​a​d​e​d​ ​t​h​e​ ​i​m​a​g​e​.​ ​ ​G​e​t​ ​t​h​e​ ​i​m​a​g​e​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​r​o​m​ ​t​h​e​ ​o​p​e​r​a​t​i​o​n​ ​a​n​d​ ​p​u​t​ ​i​t​ ​o​n​ ​t​h​e​ ​d​i​s​p​l​a​y​ ​l​i​s​t​.​
 ​ ​ ​ ​ ​ ​ ​ ​S​n​a​r​f​O​p​e​r​a​t​i​o​n​ ​*​o​p​ ​=​ ​(​S​n​a​r​f​O​p​e​r​a​t​i​o​n​ ​*​)​ ​o​b​j​e​c​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​I​m​a​g​e​ ​*​i​m​a​g​e​ ​=​ ​o​p​.​i​m​a​g​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​_​i​m​a​g​e​C​a​n​v​a​s​ ​a​d​d​I​m​a​g​e​:​ ​i​m​a​g​e​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​U​n​h​o​o​k​ ​t​h​e​ ​o​b​s​e​r​v​a​t​i​o​n​.​
 ​ ​ ​ ​ ​ ​ ​ ​[​o​p​ ​r​e​m​o​v​e​O​b​s​e​r​v​e​r​:​ ​s​e​l​f​ ​ ​f​o​r​K​e​y​P​a​t​h​:​ ​@​"​i​s​F​i​n​i​s​h​e​d​"​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​o​p​ ​r​e​m​o​v​e​O​b​s​e​r​v​e​r​:​ ​s​e​l​f​ ​ ​f​o​r​K​e​y​P​a​t​h​:​ ​@​"​i​s​E​x​e​c​u​t​i​n​g​"​]​;​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​i​f​ ​(​[​k​e​y​P​a​t​h​ ​i​s​E​q​u​a​l​T​o​S​t​r​i​n​g​:​ ​@​"​i​s​E​x​e​c​u​t​i​n​g​"​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​S​n​a​r​f​O​p​e​r​a​t​i​o​n​ ​*​o​p​ ​=​ ​(​S​n​a​r​f​O​p​e​r​a​t​i​o​n​ ​*​)​ ​o​b​j​e​c​t​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​U​p​d​a​t​e​ ​c​o​n​c​u​r​r​e​n​t​E​x​e​c​u​t​i​o​n​ ​t​o​ ​r​e​f​l​e​c​t​ ​t​o​ ​t​h​e​ ​n​u​m​b​e​r​ ​o​f​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​o​p​e​r​a​t​i​o​n​s​ ​c​u​r​r​e​n​t​l​y​ ​r​u​n​n​i​n​g​.​ ​ ​A​ ​s​t​a​t​u​s​ ​l​i​n​e​ ​t​e​x​t​ ​f​i​e​l​d​ ​i​s​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​b​o​u​n​d​ ​t​o​ ​c​o​n​c​u​r​r​e​n​t​E​x​e​c​u​t​i​o​n​.​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​o​p​.​i​s​E​x​e​c​u​t​i​n​g​)​ ​s​e​l​f​.​c​o​n​c​u​r​r​e​n​t​E​x​e​c​u​t​i​o​n​+​+​;​
 ​ ​ ​ ​ ​ ​ ​ ​e​l​s​e​ ​s​e​l​f​.​c​o​n​c​u​r​r​e​n​t​E​x​e​c​u​t​i​o​n​-​-​;​

 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​T​h​e​ ​n​o​t​i​f​i​c​a​t​i​o​n​ ​i​s​ ​u​n​i​n​t​e​r​e​s​t​i​n​g​ ​t​o​ ​u​s​,​ ​l​e​t​ ​s​o​m​e​o​n​e​ ​e​l​s​e​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​h​a​n​d​l​e​ ​i​t​.​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​u​p​e​r​ ​o​b​s​e​r​v​e​V​a​l​u​e​F​o​r​K​e​y​P​a​t​h​:​ ​k​e​y​P​a​t​h​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​f​O​b​j​e​c​t​:​ ​o​b​j​e​c​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​h​a​n​g​e​:​ ​c​h​a​n​g​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​e​x​t​:​ ​c​o​n​t​e​x​t​]​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​o​b​s​e​r​v​e​V​a​l​u​e​F​o​r​K​e​y​P​a​t​h​

Mop-up

That’s it for the code. Add a text file to the project called contents.txt and add some image URLs to it, like:

h​t​t​p​:​/​/​b​o​r​k​w​a​r​e​.​c​o​m​/​c​o​r​e​b​o​o​k​/​s​n​a​r​f​e​r​I​m​a​g​e​s​/​B​R​K​_​5​5​9​2​.​j​p​g​
h​t​t​p​:​/​/​b​o​r​k​w​a​r​e​.​c​o​m​/​c​o​r​e​b​o​o​k​/​s​n​a​r​f​e​r​I​m​a​g​e​s​/​B​R​K​_​7​8​5​3​.​j​p​g​
h​t​t​p​:​/​/​b​o​r​k​w​a​r​e​.​c​o​m​/​c​o​r​e​b​o​o​k​/​s​n​a​r​f​e​r​I​m​a​g​e​s​/​R​C​K​_​1​7​2​2​.​j​p​g​

You can fetch a file of over 250 image URLs from http://borkware.com/corebook/​snarferImages/​contents.txt. Make sure that the file is included in the Copy Bundle Resources phase of the ImageSnarfer target.

Finally, open MainMenu.xib and add an ImageCanvas to the window, along with two text fields and a Start button. Adjust the resizing behavior to your liking.

Figure 21.6  ImageSnarfer window

Hook up the ImageSnarferAppDelegate’s imageCanvas outlet to point to the ImageCanvas, and hook up the Start button action to the ImageSnarferAppDelegate’s -start: method.

Set up bindings for the two Labels. Each are “Value With Pattern” bindings:

T​o​p​ ​l​a​b​e​l​:​
 ​ ​ ​ ​ ​ ​ ​ ​B​i​n​d​ ​t​o​:​ ​ ​A​p​p​ ​C​o​n​t​r​o​l​l​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​M​o​d​e​l​ ​K​e​y​ ​P​a​t​h​:​ ​r​u​n​Q​u​e​u​e​.​o​p​e​r​a​t​i​o​n​s​.​@​c​o​u​n​t​
 ​ ​ ​ ​ ​ ​ ​ ​D​i​s​p​l​a​y​ ​P​a​t​t​e​r​n​:​ ​G​o​o​d​i​e​s​ ​i​n​ ​t​h​e​ ​q​u​e​u​e​:​ ​%​{​v​a​l​u​e​1​}​@​

B​o​t​t​o​m​ ​l​a​b​e​l​:​
 ​ ​ ​ ​ ​ ​ ​ ​B​i​n​d​ ​t​o​:​ ​ ​A​p​p​ ​C​o​n​t​r​o​l​l​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​M​o​d​e​l​ ​K​e​y​ ​P​a​t​h​:​ ​c​o​n​c​u​r​r​e​n​t​E​x​e​c​u​t​i​o​n​
 ​ ​ ​ ​ ​ ​ ​ ​D​i​s​p​l​a​y​ ​P​a​t​t​e​r​n​:​ ​C​o​n​c​u​r​r​e​n​c​y​:​ ​%​{​v​a​l​u​e​1​}​@​

Now you should be able to compile and run your program and have it display some images.

Exercises

Add the ability for the user to click and drag out a region of the Mandelbrot set to zoom in on.

Existing images do not move if you resize the ImageSnarfer window. They either get clipped or the window gets a large expanse of white space, which looks strange. Make the window resize prettier, according your own definition of pretty.

The SnarfOperations stop loading when the window is resized or when a menu is being displayed. Why? (Hint: run loops.) Make the images continue loading even when the window is being resized.

NSOperationQueue can create new threads for non-concurrent operations if one of the currently running operations enters the kernel. You can work around this by explicitly calling -setMaxConcurrentOperationCount: on your NSOperationQueue. Try changing the queue in MandelOpper to only use the number of processors on your machine. Here is a function to give you the number of processors on your system:

#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​e​r​r​o​r​
#​i​m​p​o​r​t​ ​<​s​y​s​/​s​y​s​c​t​l​.​h​>​ ​/​/​ ​f​o​r​ ​s​y​s​c​t​l​b​y​n​a​m​e​(​)​
#​i​m​p​o​r​t​ ​<​s​y​s​/​t​y​p​e​s​.​h​>​
#​i​m​p​o​r​t​ ​<​e​r​r​n​o​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​e​r​r​n​o​
.​.​.​
i​n​t​ ​p​r​o​c​e​s​s​o​r​C​o​u​n​t​ ​(​)​ ​{​
 ​ ​ ​ ​i​n​t​ ​p​r​o​c​e​s​s​o​r​C​o​u​n​t​ ​=​ ​1​;​

 ​ ​ ​ ​i​n​t​ ​c​o​u​n​t​;​
 ​ ​ ​ ​s​i​z​e​_​t​ ​s​i​z​e​ ​=​ ​s​i​z​e​o​f​ ​(​c​o​u​n​t​)​;​

 ​ ​ ​ ​i​n​t​ ​r​e​s​u​l​t​ ​=​ ​s​y​s​c​t​l​b​y​n​a​m​e​ ​(​"​h​w​.​c​a​c​h​e​c​o​n​f​i​g​"​,​ ​&​c​o​u​n​t​,​ ​&​s​i​z​e​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​U​L​L​,​ ​0​)​;​ ​ ​/​/​ ​n​e​w​ ​v​a​l​u​e​,​ ​n​o​t​ ​u​s​e​d​
 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​s​y​s​c​t​l​b​y​n​a​m​e​ ​r​e​t​u​r​n​e​d​ ​e​r​r​o​r​:​ ​%​d​/​%​s​\​n​"​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​o​c​e​s​s​o​r​C​o​u​n​t​ ​=​ ​c​o​u​n​t​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​p​r​o​c​e​s​s​o​r​C​o​u​n​t​;​

}​ ​/​/​ ​p​r​o​c​e​s​s​o​r​C​o​u​n​t​

Are there any drawbacks to this technique?

22
Grand Central Dispatch

As mentioned in Chapter 10: Performance Tuning, we have reached the end of free performance as the transistors of new processors are being put into more and more cores rather than drastically speeding up existing cores.

Multi-threaded programming can be hard. You have race conditions, which can be fixed by locking, but it can be difficult to get the proper granularity of locking to prevent all of your threads from serializing on one code path. Finding the right locking granularity is hard. Make your locks too big, and they force serialization. Make them too small, and you drown in locking and unlocking overhead. Parallel algorithms also are more difficult to understand than serial algorithms, or at least they are not as familiar as serial algorithms.

Figure 22.1 shows a typical scenario. There are multiple threads wanting to do work with some sensitive data structure. By using locks, the critical sections are serialized. Threads block while waiting for the locks to become available so that they can work with the sensitive data structure.

Figure 22.1  The traditional locking model

Mac OS X 10.6 and iOS 4 introduced Grand Central Dispatch (GCD), which is fundamentally a queueing mechanism. It provides a way of taking chunks of work, usually expressed as blocks, and serializing them. But does this undo all the work you have done with threads to take advantage of multiple cores? In isolation, yes.

Figure 22.2  Working with a serial queue

Figure 22.2 shows GCD’s view of the world. You have a serial queue. The queue controls access to the sensitive data structure. All work manipulating that data structure happens on only one thread, so you do not need to deal with locking. Individual threads can perform work on this data structure by putting work blocks onto the queue.

If the work is fire-and-forget, the thread can go off and do more work while the sensitive queue gets drained. If the thread needs to wait for the work to be done, it can wait until the work completes and then continue. In this case, performance really is not worse than waiting for a contentious lock. In fact, the performance characteristics become more deterministic. Once you have some work in a queue (which drains in FIFO order), your work will be handled. With a highly contentious lock, your thread might become starved for attention, or at least CPU time.

But, if you add more queues, you have more opportunities for parallelism.

Figure 22.3  Multiple serial queues

Figure 22.3 shows two different sensitive data structures protected by a queue and threads putting units of work onto the queues. Each of the sensitive data structures has serialized access, but no locks. By having a number of these queue-faced data structures, you can have good parallelism application-wide.

Apple has called this architecture “islands of serialization in a sea of concurrency.”

A program can optimize its use of multiple processors so that it always takes advantage of the cores available on the user’s machine. As time goes forward, more and more applications will be doing that. If the user decides to run two programs crunching on large data sets, and if each naively assumes it has free reign of the user’s processors, there could be a lot of contention as both programs fight for CPU time.

Individual programs do not have the system-wide gestalt of resource consumption. Scaling to the degree of concurrency to best utilize the available computing resources on a machine requires an awareness of hardware that can’t be baked into an existing framework. GCD cooperates with the kernel, so it knows system-wide who is vying for computing resources and can properly schedule tasks for maximum throughput.

In short, GCD’s promise is if you give it work, it will make sure it runs as soon as reasonably possible.

Grand Central Dispatch is the marketing name for libdispatch, which is included in the OS X C library, and all of its public functions and types are prefixed with dispatch_. You get GCD’s data types and functions with every program, and you don’t need to pull in any extra libraries or frameworks. All you need to start using GCD is to include the <dispatch/dispatch.h> header file and use its functions. No special compiler or linker flags are required.

GCD integrates with the Foundation and Core Foundation runloop architecture. Running the main thread’s runloop drains GCD’s main queue. If you aren’t using a runloop, you will have to call dispatch_main() on your main thread to drain the main queue.

If you are running on an OS that predates GCD support, you can use NSOperationQueue to provide some of the behaviors of GCD. You will not get the system-wide coordinating view like you do with GCD, of course.

GCD Terminology

GCD tasks are the things that need to be done, like rendering a web page or displaying a new message that comes from a social network. A task is a discrete goal your application wants to accomplish. Tasks can depend on other tasks, and they can be broken down into subtasks. Tasks are ultimately broken up into work items.

Work items are the individual chunklets of work that need to be done. For a web page, this would be work like parsing the request, hitting a database, running a template engine, merging the results, and so on.

Tasks are built from work items. Work items get put onto queues. Dispatching happens when work items run. When a work item gets to the front of a queue, it gets assigned to a thread so the work can actually happen.

GCD operates at a level of abstraction above threads. Threads become an implementation detail.

Queues

Dispatch queues are GCD’s fundamental structure. A queue is a list of work items. A queue, by definition, is drained in a FIFO manner – first in, first out order. GCD maintains a thread pool where it actually runs the work items that are pulled from queues. Work items can be submitted as a block or a function pointer/context pair. Each serial queue is backed by a global queue.

There are two kinds of queues, serial queues and global queues. Serial queues dispatch their work items and complete them in FIFO order. Serial queues will not have two different work items executing simultaneously. This is what serializes access to sensitive data structures. The main queue, the queue for doing work on the main thread, is a serial queue.

A global queue issues work in FIFO order, but it does not have any guarantees on the order in which work completes. The actual work blocks are run from a thread pool maintained by GCD and are balanced system-wide by the OS kernel. There are three global queues of different priority: high, default, and low. The global queues have effectively unlimited “width,” that is, the number of work items they can have running simultaneously. Serial queues have a width of one.

It is important to remember that the three global queues are the only ones that run concurrently. Serial queues dispatch and complete in FIFO order, whereas global queues just dispatch in FIFO order.

Every serial queue needs a target queue. The target queue is where the work ultimately gets scheduled to run. You can have serial queues target other serial queues. So long as the chain of queues eventually reaches a global queue, work will get done, as shown in Figure 22.4

Figure 22.4  Dispatch queue architecture

A serial queue’s priority is determined by the priority of its target queue. By targeting a serial queue to the default priority global queue, its work items end up scheduled on the default priority queue’s work queue. Retargeting the serial queue to the high priority global queue will cause any enqueued work items not already scheduled by the default priority global queue to run on the high priority global queue.

Serial queues are very lightweight data structures, being measured in bytes rather than dozens of kilobytes. Feel free to create as many special purpose queues as makes sense for your design, without worrying about having to manage an expensive data structure, such as threads, which are pretty heavyweight.

That being said, unless there is a reason to run work items serially, such as ensuring only a single thread manipulates a resource at a time, you should directly enqueue work items to one of the global queues.

Object-Oriented Design

Grand Central Dispatch is an object-oriented library, even though it is implemented purely in C. The fundamental GCD types are pointers to opaque data structures, but there is an inheritance model involved. The base class for dispatch objects is dispatch_object_t. You never directly create one of these; instead, you create concrete objects such as queues, sources, or groups. There are a couple of types, such as dispatch_once_t and dispatch_time_t. These are not objects but semi-opaque scalar types, as shown in Figure 22.5

Figure 22.5  Dispatch classes and functions

Dispatch API

Queues

Before you can schedule work on a queue, you need to get ahold of a queue to do the work on. You can get the main queue with

dispatch_queue_t dispatch_get_main_queue (void);

You can schedule activity on the main thread by using this queue. This is a serial queue, so work will issue and complete in FIFO order.

dispatch_queue_t dispatch_get_global_queue (long priority,
                                            unsigned long flags);

This gives you one of the global concurrent queues, whose priority is one of the DISPATCH_QUEUE_PRIORITY_LOW, DEFAULT, or HIGH priority constants.

Pass 0UL for flags; it is ignored now and is an expansion point for the future. The priority refers to which queue gets its blocks scheduled. The default queue’s blocks only get run on threads if the high priority queue is empty. Similarly, low priority queues will only issue its blocks to threads if no blocks are pending in the high or default priority queues.

You can get the current queue with:

dispatch_queue_t dispatch_get_current_queue (void);

“Current queue” is the queue that issued the currently running piece of work. If you are not in the middle of a piece of work, this will return the main queue, if you are on the main thread, or the default priority global queue otherwise. The actual queue this returns may be surprising, having unexpected properties, such as serial vs concurrent. Apple recommends only using dispatch_get_current_queue() for identity tests (“Am I really running on the main queue?”) or for debugging.

Create your own queues with

dispatch_queue_t dispatch_queue_create (const char *label,
                                        dispatch_queue_attr_t attr);

label can be anything, but Apple recommends using the reverse-DNS naming scheme. e.g. something like com.bignerdranch.BigGroovy.FacebookQueue. The label is optional but because it appears in Instruments, the debugger, and in crash reports, it can be a very useful piece of information. Pass NULL for attr. It is unused in OS X 10.6 and iOS 4, but can be used as an expansion point for the future. The queue returned from this call is a serial queue.

Set a queue’s target queue with this function:

void dispatch_set_target_queue (dispatch_object_t object,
                                dispatch_queue_t target);

Work items issued from the queue will be placed into this queue for ultimate scheduling. You can set the target queue for arbitrary GCD objects. For non-queue objects, this will tell the object where to run its context pointer finalization function.

Dispatching

Now you have a queue. What do you do with it? You dispatch work to it. dispatch_async() will throw a chunk of work on the queue and then return. The work will eventually find its way to a global queue where it is run on a thread.

Because this work happens asynchronously, it is possible that the work started running before dispatch_async() returns. It is also possible, if it is a short piece of work, that it started and completed before dispatch_async() returns.

There are two flavors of many calls in GCD: one that takes a block and another that takes a function:

void dispatch_async (dispatch_queue_t queue, void (^block)(void));

void dispatch_async_f (dispatch_queue_t queue, void *context,
                       void (*function)(void *));

The function-taking variants follow the same convention of having _f appended to the name, taking a context pointer, and a function pointer. It is just an implementation detail, but much of GCD has the _f functions as the fundamental implementation with the block versions being wrappers on top of it. Only the block versions will be discussed here. The function versions work the same way.

You can choose to block your thread’s execution until the work item has completed execution by calling dispatch_sync():

     void dispatch_sync (dispatch_queue_t queue, void (^block)(void));

Be careful that you do not make recursive calls to dispatch_sync(). This can create a deadlock situation where the inner synchronous dispatch has to wait for the outer one to complete, but the outer dispatch_sync() can’t complete until the inner one has finished.

A runloop on the main thread will cause GCD to start processing blocks for the main queue. If you do not have a runloop handy, or are running at a lower level than Core Foundation, you can use dispatch_main():

void dispatch_main (void);

This function never returns. It will loop forever, handling work items added to the main queue.

dispatch_after(), described later, lets you schedule a block for execution sometime in the future.

Memory management

Grand Central Dispatch is near the bottom of the OS X food chain, living in the system library. You do not get fancy things like garbage collection, so there is API for managing object life cycles. GCD uses reference counting, like in Cocoa. Express your long-term interest in an object by retaining it:

void dispatch_retain (dispatch_object_t object);

Because GCD objects have an inheritance relationship with dispatch_object_t, you use this function to retain a dispatch_queue_t or a dispatch_source_t. Objects received from calls like dispatch_queue_create() are already retained for you. When you are done with an object, release it.

void dispatch_release (dispatch_object_t object);

Over-releasing an object will crash your program, so be sure to check the crash log after a crash. The library tries to supply useful application-specific information in the crash report, such as:

B​U​G​ ​I​N​ ​C​L​I​E​N​T​ ​O​F​ ​L​I​B​D​I​S​P​A​T​C​H​:​
O​v​e​r​-​r​e​l​e​a​s​e​ ​o​f​ ​a​n​ ​o​b​j​e​c​t​

Queues and dispatch sources also have context pointers, where you can put a pointer to your own data. You can set and retrieve the context pointer using these functions:

void dispatch_set_context (dispatch_object_t object, void *context);

void *dispatch_get_context (dispatch_object_t object);

It is not easy to figure out exactly when an object is finally destroyed so that you can clean up your context pointer. You can specify a finalizer function that is invoked asynchronously when the object is destroyed:

void dispatch_set_finalizer_f (dispatch_object_t object,
                               dispatch_function_t finalizer);

A dispatch_function_t is a function that takes a void pointer parameter, which would be your context pointer, and returns nothing:

typedef void (*dispatch_function_t) (void *);

Oddly enough, there is no block version of the finalizer.

The queue and dispatch source objects can be suspended and resumed. These objects have a “suspend count” that when non-zero, causes a queue to stop issuing work items or a dispatch source from dispatching event handling blocks. The suspended state is checked before a block is executed, say by taking a block off of a queue or by triggering a dispatch source. Currently executing blocks or functions will not be preempted, so suspension only controls if something starts executing or not.

void dispatch_suspend (dispatch_object_t object);

void dispatch_resume (dispatch_object_t object);

It is important to always balance your suspends with resumes; otherwise, things like finalizer functions or cancellation handlers will not be called. Over-resuming an object will crash your program, and releasing a suspended object is undefined.

WordCounter

Figure 22.6 shows a program that counts the number of words in a text field as well as the number of unique words. Text fields are updated with the values.

Figure 22.6  WordCounter screenshot

The code for this application is straightforward. In Xcode, make a new Cocoa Application (not document-based) called WordCounter. Add the objects seen in Figure 22.6: an NSTextView, a count button, a progress indicator, and two labels for the different counts. Configure your object layout appropriately. Update your AppDelegate class to point to the UI items, as well as the -count: action method:

#​i​m​p​o​r​t​ ​<​C​o​c​o​a​/​C​o​c​o​a​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​W​o​r​d​C​o​u​n​t​e​r​A​p​p​D​e​l​e​g​a​t​e​ ​:​ ​N​S​O​b​j​e​c​t​ ​<​N​S​A​p​p​l​i​c​a​t​i​o​n​D​e​l​e​g​a​t​e​>​ ​{​
 ​ ​ ​ ​N​S​W​i​n​d​o​w​ ​*​_​w​i​n​d​o​w​;​

 ​ ​ ​ ​N​S​T​e​x​t​V​i​e​w​ ​*​_​w​o​r​d​s​V​i​e​w​;​
 ​ ​ ​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​_​c​o​u​n​t​L​a​b​e​l​;​
 ​ ​ ​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​_​u​n​i​q​u​e​L​a​b​e​l​;​
 ​ ​ ​ ​N​S​B​u​t​t​o​n​ ​*​_​c​o​u​n​t​B​u​t​t​o​n​;​
 ​ ​ ​ ​N​S​P​r​o​g​r​e​s​s​I​n​d​i​c​a​t​o​r​ ​*​_​s​p​i​n​n​e​r​;​
}​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​W​i​n​d​o​w​ ​*​w​i​n​d​o​w​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​V​i​e​w​ ​*​w​o​r​d​s​V​i​e​w​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​c​o​u​n​t​L​a​b​e​l​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​T​e​x​t​F​i​e​l​d​ ​*​u​n​i​q​u​e​L​a​b​e​l​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​B​u​t​t​o​n​ ​*​c​o​u​n​t​B​u​t​t​o​n​;​
@​p​r​o​p​e​r​t​y​ ​(​a​s​s​i​g​n​)​ ​I​B​O​u​t​l​e​t​ ​N​S​P​r​o​g​r​e​s​s​I​n​d​i​c​a​t​o​r​ ​*​s​p​i​n​n​e​r​;​
-​ ​(​I​B​A​c​t​i​o​n​)​ ​c​o​u​n​t​:​ ​(​i​d​)​ ​s​e​n​d​e​r​;​
@​e​n​d​ ​/​/​ ​W​o​r​d​C​o​u​n​t​e​r​A​p​p​D​e​l​e​g​a​t​e​

Make the connections in Interface Builder. AppDelegate.m has the code that actually Does Stuff, with the beginning of the file is standard boilerplate:

#​i​m​p​o​r​t​ ​"​W​o​r​d​C​o​u​n​t​e​r​A​p​p​D​e​l​e​g​a​t​e​.​h​"​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​W​o​r​d​C​o​u​n​t​e​r​A​p​p​D​e​l​e​g​a​t​e​
@​s​y​n​t​h​e​s​i​z​e​ ​w​i​n​d​o​w​ ​=​ ​_​w​i​n​d​o​w​;​
@​s​y​n​t​h​e​s​i​z​e​ ​w​o​r​d​s​V​i​e​w​ ​=​ ​_​w​o​r​d​s​V​i​e​w​;​
@​s​y​n​t​h​e​s​i​z​e​ ​c​o​u​n​t​L​a​b​e​l​ ​=​ ​_​c​o​u​n​t​L​a​b​e​l​;​
@​s​y​n​t​h​e​s​i​z​e​ ​u​n​i​q​u​e​L​a​b​e​l​ ​=​ ​_​u​n​i​q​u​e​L​a​b​e​l​;​
@​s​y​n​t​h​e​s​i​z​e​ ​c​o​u​n​t​B​u​t​t​o​n​ ​=​ ​_​c​o​u​n​t​B​u​t​t​o​n​;​
@​s​y​n​t​h​e​s​i​z​e​ ​s​p​i​n​n​e​r​ ​=​ ​_​s​p​i​n​n​e​r​;​

The method that does the work actually wants to return two values: the count of words and an NSCountedSet that has the word frequencies. You cannot really return two values at the same time. One approach is returning one of the values via the ordinary method return and returning the other value as an out parameter. You can also make a struct that contains the two return values:

t​y​p​e​d​e​f​ ​s​t​r​u​c​t​ ​{​
 ​ ​ ​ ​N​S​U​I​n​t​e​g​e​r​ ​c​o​u​n​t​;​
 ​ ​ ​ ​N​S​C​o​u​n​t​e​d​S​e​t​ ​*​f​r​e​q​u​e​n​c​i​e​s​;​
}​ ​C​o​u​n​t​A​n​d​S​e​t​;​

-countWords does the heavy lifting:

-​ ​(​C​o​u​n​t​A​n​d​S​e​t​)​ ​c​o​u​n​t​W​o​r​d​s​:​ ​(​N​S​S​t​r​i​n​g​ ​*​)​ ​s​t​r​i​n​g​ ​{​
 ​ ​ ​ ​N​S​S​c​a​n​n​e​r​ ​*​s​c​a​n​n​e​r​ ​=​ ​[​N​S​S​c​a​n​n​e​r​ ​s​c​a​n​n​e​r​W​i​t​h​S​t​r​i​n​g​:​ ​s​t​r​i​n​g​]​;​
 ​ ​ ​ ​N​S​C​h​a​r​a​c​t​e​r​S​e​t​ ​*​w​h​i​t​e​S​p​a​c​e​ ​=​ ​[​N​S​C​h​a​r​a​c​t​e​r​S​e​t​ ​w​h​i​t​e​s​p​a​c​e​A​n​d​N​e​w​l​i​n​e​C​h​a​r​a​c​t​e​r​S​e​t​]​;​

 ​ ​ ​ ​N​S​U​I​n​t​e​g​e​r​ ​c​o​u​n​t​ ​=​ ​0​;​
 ​ ​ ​ ​N​S​C​o​u​n​t​e​d​S​e​t​ ​*​w​o​r​d​S​e​t​ ​=​ ​[​N​S​C​o​u​n​t​e​d​S​e​t​ ​s​e​t​]​;​

 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​w​o​r​d​;​
 ​ ​ ​ ​w​h​i​l​e​ ​(​[​s​c​a​n​n​e​r​ ​s​c​a​n​U​p​T​o​C​h​a​r​a​c​t​e​r​s​F​r​o​m​S​e​t​:​ ​w​h​i​t​e​S​p​a​c​e​ ​ ​i​n​t​o​S​t​r​i​n​g​:​ ​&​w​o​r​d​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​w​o​r​d​S​e​t​ ​a​d​d​O​b​j​e​c​t​:​ ​[​w​o​r​d​ ​l​o​w​e​r​c​a​s​e​S​t​r​i​n​g​]​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​u​n​t​+​+​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​C​o​u​n​t​A​n​d​S​e​t​ ​r​e​s​u​l​t​ ​=​ ​{​ ​c​o​u​n​t​,​ ​w​o​r​d​S​e​t​ ​}​;​

 ​ ​ ​ ​s​l​e​e​p​ ​(​2​)​;​ ​/​/​ ​M​a​c​h​i​n​e​s​ ​t​h​e​s​e​ ​d​a​y​s​ ​a​r​e​ ​t​o​o​ ​d​a​n​g​ ​f​a​s​t​!​

 ​ ​ ​ ​r​e​t​u​r​n​ ​r​e​s​u​l​t​;​

}​ ​/​/​ ​c​o​u​n​t​W​o​r​d​s​

There is a sleep() in there just to slow things down a bit to make a point later on. Then call countWords in the button handler:

-​ ​(​I​B​A​c​t​i​o​n​)​ ​c​o​u​n​t​:​ ​(​i​d​)​ ​s​e​n​d​e​r​ ​{​
 ​ ​ ​ ​C​o​u​n​t​A​n​d​S​e​t​ ​r​e​s​u​l​t​ ​=​ ​[​s​e​l​f​ ​c​o​u​n​t​W​o​r​d​s​:​ ​s​e​l​f​.​w​o​r​d​s​V​i​e​w​.​s​t​r​i​n​g​]​;​

 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​l​a​b​e​l​S​t​r​i​n​g​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​ ​@​"​W​o​r​d​ ​c​o​u​n​t​:​ ​%​u​"​,​ ​r​e​s​u​l​t​.​c​o​u​n​t​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​c​o​u​n​t​L​a​b​e​l​ ​s​e​t​S​t​r​i​n​g​V​a​l​u​e​:​ ​l​a​b​e​l​S​t​r​i​n​g​]​;​

 ​ ​ ​ ​l​a​b​e​l​S​t​r​i​n​g​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​ ​@​"​U​n​i​q​u​e​ ​w​o​r​d​s​:​ ​%​u​"​,​ ​r​e​s​u​l​t​.​f​r​e​q​u​e​n​c​i​e​s​.​c​o​u​n​t​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​u​n​i​q​u​e​L​a​b​e​l​ ​s​e​t​S​t​r​i​n​g​V​a​l​u​e​:​ ​l​a​b​e​l​S​t​r​i​n​g​]​;​

}​ ​/​/​ ​c​o​u​n​t​

This method gets the count and the word frequency set out of the returned structure and populates the labels. You can go ahead and build and run the app. It works! But because -countWords: takes a couple of seconds, you get the Spinning Pizza of Death showing that the UI has locked up. On a desktop system, a locked up app is annoying but not a big deal. You can go work on something else and come back. It is a big deal on a mobile device. If your app’s user interface is locked up, the user cannot do anything but stare. If you stay locked up too long, say longer than fifteen or thirty seconds, the iOS watchdog will kill your program.

WordCounter cheats by putting in a two-second sleep, simulating a lot of processing work being done. Machines these days are too fast to bog down with simple code like this. A MacBookPro took less than a quarter second to count all of /usr/share/dict/words, which is 235,000 words long. But the point still stands that it is possible to block the main thread by doing work.

The solution is to not do all the heavy lifting at the same time on the main thread. There are a couple of approaches you could use. You could slice the work apart and do it in the main thread in an asynchronous manner. Count a thousand words, then return to the RunLoop, count a thousand more words, then return to the RunLoop. You could also spin off a pthread or an NSThread to do the work.

The downside of these approaches is that it’s a lot of work. You will end up rewriting your code in the first case, and in the thread cases, you will need to have a way to rendezvous with the main thread and get the results, not to mention setting up thread functions or methods that get run on an actual thread.

Grand Central Dispatch makes “throw this piece of work onto a background thread for awhile. Ok, throw this other piece of work on to the main thread to clean up” kinds of work very easy. A common “GCD is Cool!” demo is to put a dispatch_async() onto a background thread into -countWords: for the counting, and a dispatch_async() onto the main thread to update the labels. Real life is a little more complicated, but not much more. And certainly easier than manually spinning off another thread.

One thing you’ll need to do is prevent the user from interacting with the window’s controls. You don’t want the user to click “Count!” while the counting is happening. You also don’t want the user to change the text. The count would then be different from the text in the text field. Also, it would be nice to put up a spinning progress indicator so the user knows something is happening. Two helper methods do this work:

-​ ​(​v​o​i​d​)​ ​d​i​s​a​b​l​e​E​v​e​r​y​t​h​i​n​g​ ​{​
 ​ ​ ​ ​/​/​ ​N​S​T​e​x​t​V​i​e​w​ ​d​o​e​s​n​'​t​ ​h​a​v​e​ ​a​n​ ​e​n​a​b​l​e​d​ ​b​i​n​d​i​n​g​,​ ​s​o​ ​w​e​'​l​l​ ​j​u​s​t​ ​d​o​ ​i​t​ ​a​l​l​ ​h​e​r​e​.​
 ​ ​ ​ ​[​s​e​l​f​.​w​o​r​d​s​V​i​e​w​ ​s​e​t​E​d​i​t​a​b​l​e​:​ ​N​O​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​c​o​u​n​t​B​u​t​t​o​n​ ​s​e​t​E​n​a​b​l​e​d​:​ ​N​O​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​s​p​i​n​n​e​r​ ​s​e​t​H​i​d​d​e​n​:​ ​N​O​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​s​p​i​n​n​e​r​ ​s​t​a​r​t​A​n​i​m​a​t​i​o​n​:​ ​s​e​l​f​]​;​
}​ ​/​/​ ​d​i​s​a​b​l​e​E​v​e​r​y​t​h​i​n​g​

-​ ​(​v​o​i​d​)​ ​e​n​a​b​l​e​E​v​e​r​y​t​h​i​n​g​ ​{​
 ​ ​ ​ ​[​s​e​l​f​.​w​o​r​d​s​V​i​e​w​ ​s​e​t​E​d​i​t​a​b​l​e​:​ ​Y​E​S​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​c​o​u​n​t​B​u​t​t​o​n​ ​s​e​t​E​n​a​b​l​e​d​:​ ​Y​E​S​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​s​p​i​n​n​e​r​ ​s​t​o​p​A​n​i​m​a​t​i​o​n​:​ ​s​e​l​f​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​s​p​i​n​n​e​r​ ​s​e​t​H​i​d​d​e​n​:​ ​Y​E​S​]​;​
}​ ​/​/​ ​e​n​a​b​l​e​E​v​e​r​y​t​h​i​n​g​

Now, before GCDing things, add in the enabling and disabling of stuff:

-​ ​(​I​B​A​c​t​i​o​n​)​ ​c​o​u​n​t​:​ ​(​i​d​)​ ​s​e​n​d​e​r​ ​{​

 ​ ​ ​ ​[​s​e​l​f​ ​d​i​s​a​b​l​e​E​v​e​r​y​t​h​i​n​g​]​;​

 ​ ​ ​ ​C​o​u​n​t​A​n​d​S​e​t​ ​r​e​s​u​l​t​ ​=​ ​[​s​e​l​f​ ​c​o​u​n​t​W​o​r​d​s​:​ ​s​e​l​f​.​w​o​r​d​s​V​i​e​w​.​s​t​r​i​n​g​]​;​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​s​e​t​ ​i​s​ ​a​u​t​o​r​e​l​e​a​s​e​d​,​ ​b​u​t​ ​w​e​ ​w​a​n​t​ ​i​t​ ​t​o​ ​s​u​r​v​i​v​e​ ​u​n​t​i​l​ ​t​h​e​
 ​ ​ ​ ​/​/​ ​n​e​x​t​ ​b​l​o​c​k​ ​r​u​n​s​,​ ​a​t​ ​s​o​m​e​ ​u​n​s​p​e​c​i​f​i​e​d​ ​p​o​i​n​t​ ​i​n​ ​t​h​e​ ​f​u​t​u​r​e​.​
 ​ ​ ​ ​[​r​e​s​u​l​t​.​f​r​e​q​u​e​n​c​i​e​s​ ​r​e​t​a​i​n​]​;​

 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​l​a​b​e​l​S​t​r​i​n​g​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​ ​@​"​W​o​r​d​ ​c​o​u​n​t​:​ ​%​u​"​,​ ​r​e​s​u​l​t​.​c​o​u​n​t​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​c​o​u​n​t​L​a​b​e​l​ ​s​e​t​S​t​r​i​n​g​V​a​l​u​e​:​ ​l​a​b​e​l​S​t​r​i​n​g​]​;​

 ​ ​ ​ ​l​a​b​e​l​S​t​r​i​n​g​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​ ​@​"​U​n​i​q​u​e​ ​w​o​r​d​s​:​ ​%​u​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​.​f​r​e​q​u​e​n​c​i​e​s​.​c​o​u​n​t​]​;​

 ​ ​ ​ ​[​s​e​l​f​.​u​n​i​q​u​e​L​a​b​e​l​ ​s​e​t​S​t​r​i​n​g​V​a​l​u​e​:​ ​l​a​b​e​l​S​t​r​i​n​g​]​;​

 ​ ​ ​ ​[​s​e​l​f​ ​e​n​a​b​l​e​E​v​e​r​y​t​h​i​n​g​]​;​
 ​ ​ ​ ​[​r​e​s​u​l​t​.​f​r​e​q​u​e​n​c​i​e​s​ ​r​e​l​e​a​s​e​]​;​

}​ ​/​/​ ​c​o​u​n​t​

There is one bit of memory management subtlety. The plan is to put the word calculations onto a background thread. That thread has its own autorelease pool, and the NSCountedSet created is put into the pool. The pool can be, and most likely will be, drained before we get around to running the UI code on the main thread. That is why result.frequencies is retained. You can run the app now, and it should behave the way it did before.

Now to figure out what needs to be run in the background and what needs to be run on the main thread. The UI manipulation code needs to be run on the main thread. -countWords: and retaining the word frequency set need to happen on the background thread. Knowing that, you can write the final GCD-enhanced version:

-​ ​(​I​B​A​c​t​i​o​n​)​ ​c​o​u​n​t​:​ ​(​i​d​)​ ​s​e​n​d​e​r​ ​{​
 ​ ​ ​ ​[​s​e​l​f​ ​d​i​s​a​b​l​e​E​v​e​r​y​t​h​i​n​g​]​;​
 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​a​s​y​n​c​ ​(​d​i​s​p​a​t​c​h​_​g​e​t​_​g​l​o​b​a​l​_​q​u​e​u​e​ ​(​D​I​S​P​A​T​C​H​_​Q​U​E​U​E​_​P​R​I​O​R​I​T​Y​_​D​E​F​A​U​L​T​,​ ​0​)​,​ ​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​o​u​n​t​A​n​d​S​e​t​ ​r​e​s​u​l​t​ ​=​ ​[​s​e​l​f​ ​c​o​u​n​t​W​o​r​d​s​:​ ​s​e​l​f​.​w​o​r​d​s​V​i​e​w​.​s​t​r​i​n​g​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​T​h​e​ ​s​e​t​ ​i​s​ ​a​u​t​o​r​e​l​e​a​s​e​d​,​ ​b​u​t​ ​w​e​ ​w​a​n​t​ ​i​t​ ​t​o​ ​s​u​r​v​i​v​e​ ​u​n​t​i​l​ ​t​h​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​n​e​x​t​ ​b​l​o​c​k​ ​r​u​n​s​,​ ​a​t​ ​s​o​m​e​ ​u​n​s​p​e​c​i​f​i​e​d​ ​p​o​i​n​t​ ​i​n​ ​t​h​e​ ​f​u​t​u​r​e​.​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​r​e​s​u​l​t​.​f​r​e​q​u​e​n​c​i​e​s​ ​r​e​t​a​i​n​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​s​p​a​t​c​h​_​a​s​y​n​c​ ​(​d​i​s​p​a​t​c​h​_​g​e​t​_​m​a​i​n​_​q​u​e​u​e​(​)​,​ ​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​l​a​b​e​l​S​t​r​i​n​g​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​ ​@​"​W​o​r​d​ ​c​o​u​n​t​:​ ​%​u​"​,​ ​r​e​s​u​l​t​.​c​o​u​n​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​.​c​o​u​n​t​L​a​b​e​l​ ​s​e​t​S​t​r​i​n​g​V​a​l​u​e​:​ ​l​a​b​e​l​S​t​r​i​n​g​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​l​a​b​e​l​S​t​r​i​n​g​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​ ​@​"​U​n​i​q​u​e​ ​w​o​r​d​s​:​ ​%​u​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​.​f​r​e​q​u​e​n​c​i​e​s​.​c​o​u​n​t​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​.​u​n​i​q​u​e​L​a​b​e​l​ ​s​e​t​S​t​r​i​n​g​V​a​l​u​e​:​ ​l​a​b​e​l​S​t​r​i​n​g​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​e​n​a​b​l​e​E​v​e​r​y​t​h​i​n​g​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​r​e​s​u​l​t​.​f​r​e​q​u​e​n​c​i​e​s​ ​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​)​;​

}​ ​/​/​ ​c​o​u​n​t​

Here is how this method runs: first, -disableEverything is called. The IBAction is handled on the main thread, so we are on the main thread for this method. The UI is disabled. Then, the rest of the method is a block that gets thrown on the default priority global queue. The method then exits and control returns to the runloop.

At some point in time in the future, the block will be taken off the front of the global queue and run. The first thing it does is call -countWords:. That will happily do the work it needs to do. Then the frequencies set is retained. The retain happens on the same thread where -countWords: was called.

Next, another block is put onto the main queue, and this block exits because there is no more work to do. At some unspecified point in time in the future, the main queue block starts running. It updates the UI and releases the frequencies set.

This is a common coding pattern when using GCD. You have nested dispatch_async blocks. The “outer” block is put on a queue, some work is done, then it is peeled away like an onion, and an inner block is put on another queue. Then that block runs, is peeled away, and an even more nested block is run.

Notice that the main queue block referenced result, the return from -countWords:. This is simply the block capturing the variables in the outer scope. The frequency set was not automatically retained by the block because it was living inside of a struct. If we had an NSCountedSet variable in the outer scope, the main queue block would have retained it, and the explicit retain here would not have been necessary.

Iteration

Even something as simple as a for-loop iterating over data performing work can be enhanced by GCD. The classic way of iterating through an array and performing an operation on each element is through a for loop.

e​n​u​m​ ​{​
 ​ ​ ​ ​k​N​u​m​b​e​r​C​o​u​n​t​ ​=​ ​2​0​0​0​0​0​
}​;​

s​t​a​t​i​c​ ​i​n​t​ ​n​u​m​b​e​r​s​[​k​N​u​m​b​e​r​C​o​u​n​t​]​;​
s​t​a​t​i​c​ ​i​n​t​ ​r​e​s​u​l​t​s​[​k​N​u​m​b​e​r​C​o​u​n​t​]​;​

f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​k​N​u​m​b​e​r​C​o​u​n​t​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​r​e​s​u​l​t​s​[​i​]​ ​=​ ​W​o​r​k​ ​(​n​u​m​b​e​r​s​,​ ​i​)​;​
}​

You could walk through the array doing work that is not really interrelated and then sift through the work to make the final conclusion, like a map/reduce operation.

You can parallelize these by using dispatch_apply(). The call itself is synchronous, so you know all the work has been completed when the function returns, but the call fans out over as many CPUs as it deems optimal to do the work.

d​i​s​p​a​t​c​h​_​q​u​e​u​e​_​t​ ​q​u​e​u​e​ ​=​
 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​g​e​t​_​g​l​o​b​a​l​_​q​u​e​u​e​ ​(​D​I​S​P​A​T​C​H​_​Q​U​E​U​E​_​P​R​I​O​R​I​T​Y​_​D​E​F​A​U​L​T​,​ ​0​)​;​

d​i​s​p​a​t​c​h​_​a​p​p​l​y​ ​(​k​N​u​m​b​e​r​C​o​u​n​t​,​ ​q​u​e​u​e​,​ ​^​(​s​i​z​e​_​t​ ​i​n​d​e​x​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​s​[​i​n​d​e​x​]​ ​=​ ​W​o​r​k​ ​(​n​u​m​b​e​r​s​,​ ​i​n​d​e​x​)​;​
 ​ ​ ​ ​}​)​;​

This is similar to NSArray’s -enumerateObjectsWithOptions:usingBlock: and giving it the NSEnumerationConcurrent option. Be sure you use a global queue for your dispatch_apply(). Using a serial queue will serialize all your work, defeating any attempt to parallelize this operation.

There is a certain amount of overhead for each operation that is put on the queue and subsequently executed. If the work you are doing inside of the block is very fast, you may want to do more work inside of each parallel operation:

e​n​u​m​ ​{​
 ​ ​ ​ ​k​N​u​m​b​e​r​C​o​u​n​t​ ​=​ ​2​0​0​0​0​0​,​
 ​ ​ ​ ​k​S​t​r​i​d​e​ ​=​ ​1​0​0​0​
}​;​
d​i​s​p​a​t​c​h​_​a​p​p​l​y​ ​(​k​N​u​m​b​e​r​C​o​u​n​t​ ​/​ ​k​S​t​r​i​d​e​,​ ​q​u​e​u​e​,​ ​^​(​s​i​z​e​_​t​ ​i​n​d​e​x​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​_​t​ ​j​i​n​d​e​x​ ​=​ ​i​n​d​e​x​ ​*​ ​k​S​t​r​i​d​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​i​z​e​_​t​ ​j​S​t​o​p​ ​=​ ​M​I​N​(​j​i​n​d​e​x​ ​+​ ​k​S​t​r​i​d​e​,​ ​k​N​u​m​b​e​r​C​o​u​n​t​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​w​h​i​l​e​ ​(​j​i​n​d​e​x​ ​<​ ​j​S​t​o​p​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​s​[​j​i​n​d​e​x​]​ ​=​ ​W​o​r​k​ ​(​n​u​m​b​e​r​s​,​ ​j​i​n​d​e​x​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​)​;​

This would do 1000 iterations inside of each dispatched operation. You would determine the optimal stride by experimentation.

You should also make sure that your array processing works correctly when run in parallel. Iterations depending on the results of subsequent iterations usually cannot be easily parallelized. One quick check you can make is to iterate through your array backwards. If the result is correct, then there is a good chance your iteration can be parallelized.

Safe Global Initialization

Sometimes you need to perform one-time initialization and have the work performed exactly one time, no matter how many threads are wanting that initialization to happen. This is often used for lazy initialization or for creating the One True Object when using singletons. There is a technique called Double-Checked Locking to do this, but it is very hard to get right. You can use pthread_once() to serialize a one-time initialization. You can also use dispatch_once().

void dispatch_once (dispatch_once_t *predicate, void (^block)(void));

s​t​a​t​i​c​ ​d​i​s​p​a​t​c​h​_​o​n​c​e​_​t​ ​i​n​i​t​i​a​l​i​z​a​t​i​o​n​P​r​e​d​i​c​a​t​e​;​
s​t​a​t​i​c​ ​b​l​a​h​ ​*​s​t​u​f​f​T​o​I​n​i​t​i​a​l​i​z​e​

d​i​s​p​a​t​c​h​_​o​n​c​e​ ​(​&​i​n​i​t​i​a​l​i​z​a​t​i​o​n​P​r​e​d​i​c​a​t​e​,​ ​^​{​
 ​ ​ ​ ​s​t​u​f​f​T​o​I​n​i​t​i​a​l​z​i​e​ ​=​ ​g​o​o​p​ ​(​)​;​
}​)​;​
.​.​.​

A dispatch_once_t is used as the guard variable and dispatch_once() will execute its block exactly once, no matter the number of threads and cores wanting the initialization to happen.

If possible, prefer the dispatch* family of functions to similar pthread functions. The dispatch functions will have the best performance possible.

Time, Time, Time

When dealing with real-world code, you frequently need to interact with time, whether having calls time out after awhile or scheduling things to happen at specific times. GCD calls these times “temporal milestones,” and they are represented by a dispatch_time_t. This type is a “semi-opaque” integer; it has an integer value, but its contents are subject to change. Time can be expressed relative to another time or relative to two distinguished moments in time – now and forever:

s​t​a​t​i​c​ ​c​o​n​s​t​ ​d​i​s​p​a​t​c​h​_​t​i​m​e​_​t​ ​D​I​S​P​A​T​C​H​_​T​I​M​E​_​N​O​W​ ​=​ ​0​;​
s​t​a​t​i​c​ ​c​o​n​s​t​ ​d​i​s​p​a​t​c​h​_​t​i​m​e​_​t​ ​D​I​S​P​A​T​C​H​_​T​I​M​E​_​F​O​R​E​V​E​R​ ​=​ ​~​0​u​l​l​;​

Use DISPATCH_TIME_FOREVER for an infinite timeout.

There are two different kinds of time used in GCD. The “host clock,” also known a the system clock, is based on a counter in the OS. The counter increases so long as the machine is running. Sleeping the machine freezes the host clock. If you set a timeout for one hour in the future and then sleep the machine for thirty minutes, the operation will actually timeout one hour and thirty minutes into the future.

The other clock is the “wall clock,” which represents a moment in time that could be reported by a clock and calendar sitting on your desk. Wall clock time continues in motion if the machine is slept and could move forward or backwards arbitrarily. The user might travel to a different time zone, or daylight savings time could kick in. So, if you set a timeout for an hour in the future using wall clock time and you sleep the machine for thirty minutes, the timeout will happen thirty minutes later, barring time zone changes.

dispatch_time() returns a milestone value relative to an existing milestone, after adding the given number of nanoseconds

dispatch_time_t dispatch_time (dispatch_time_t base,
                               int64_t nanoseconds);

If the base time is a wall clock time, the return time will be a wall clock time too. DISPATCH_TIME_NOW counts as using a host clock time.

Use dispatch_walltime() to create a milestone relative to a fixed point in time using the wall clock.

dispatch_time_t dispatch_walltime (struct timespec *base,
                                   int64_t nanoseconds);

You can pass a struct timespec for a particular point in time, which is the number of seconds and nanoseconds since the Unix epoch of 1970, or you can pass NULL to use the current time.

Overflow returns DISPATCH_TIME_FOREVER, while underflow causes the smallest representable value to be returned.

A milestone for twenty three seconds in the future would be created with:

d​i​s​p​a​t​c​h​_​t​i​m​e​_​t​ ​ ​m​i​l​e​s​t​o​n​e​ ​=​ ​d​i​s​p​a​t​c​h​_​t​i​m​e​ ​(​D​I​S​P​A​T​C​H​_​T​I​M​E​_​N​O​W​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​2​3​u​l​l​ ​*​ ​N​S​E​C​_​P​E​R​_​S​E​C​)​;​

The ull modifier for the constant keeps the math from being truncated to 32 bits. NSEC_PER_SEC is a handy constant for multiplying seconds by a billion. There are other constants for easily working with time at different granularities: USEC_PER_SEC for microseconds and NSEC_PER_USEC for converting between microseconds and nanoseconds.

You can set a timeout for an absolute time by putting the time into a struct tm, and then converting it struct timespec with mktime():

 ​ ​ ​ ​s​t​r​u​c​t​ ​t​m​ ​t​m​ ​=​ ​{​0​}​;​
 ​ ​ ​ ​s​t​r​p​t​i​m​e​ ​(​"​2​0​1​6​-​0​9​-​1​3​ ​1​3​:​1​3​"​,​ ​"​%​Y​-​%​m​-​%​d​ ​%​H​:​%​M​"​,​ ​&​t​m​)​;​

 ​ ​ ​ ​s​t​r​u​c​t​ ​t​i​m​e​s​p​e​c​ ​t​s​ ​=​ ​{​}​;​
 ​ ​ ​ ​t​s​.​t​v​_​s​e​c​ ​=​ ​m​k​t​i​m​e​ ​(​&​t​m​)​;​

 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​t​i​m​e​_​t​ ​t​i​m​e​o​u​t​ ​=​ ​d​i​s​p​a​t​c​h​_​w​a​l​l​t​i​m​e​ ​(​&​t​s​,​ ​0​)​;​

You can use a dispatch_time_t in calls to dispatch_after():

void dispatch_after (dispatch_time_t when, dispatch_queue_t queue,
                     void (^block)(void));

This function enqueues a work item to be run after a given time. Passing DISPATCH_TIME_NOW to dispatch_after() has the same effect as calling dispatch_async().

Dispatch Groups

Sometimes you want to fan some work out to a number of different queues and then wait until everything completes. For instance, rendering a page in a server might involve hitting a database, running the text through a rendering engine, and appending any insightful and relevant comments that have been left on the page.

A dispatch group is a collection of blocks that have been dispatched onto queues asynchronously.

To create a group to track the collection of blocks, you use

dispatch_group_t dispatch_group_create (void);

Rather than dispatching directly onto other queues, you would use dispatch_group_async() to put the block onto a queue, but you also add the work item to the work group.

void dispatch_group_async (dispatch_group_t group,
                           dispatch_queue_t queue,
                           void (^block)(void));

In this case, the block is added to the group, and then dispatch_async()’d to the given queue. There are two ways to get notified when all of the blocks in the group have completed. You can specify a block to run with dispatch_group_notify().

void dispatch_group_notify (dispatch_group_t group,
                            dispatch_queue_t queue,
                            void (^block)(void));

group, after it has finished with the last block that has been scheduled with it, will dispatch_async() the notification block onto queue.

You can also block until the group has drained by using dispatch_group_wait(), giving it a timeout.

long dispatch_group_wait (dispatch_group_t group,
                          dispatch_time_t timeout);

The function returns zero if the group drained before timeout and returns a non-zero value if the timeout expires. Give a timeout of DISPATCH_TIME_FOREVER for an infinite timeout.

You can manually control the count of work items currently in a group by using dispatch_group_enter and _leave:

void dispatch_group_enter (dispatch_group_t group);

void dispatch_group_leave (dispatch_group_t group);

Entering a dispatch group increments its count by one, and leaving decrements it. The group won’t notify or break out of a wait until its count goes to zero.

Example 22.1 shows dispatch groups in action.

Example 22.1. group.m

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​
#​i​m​p​o​r​t​ ​<​d​i​s​p​a​t​c​h​/​d​i​s​p​a​t​c​h​.​h​>​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​
#​i​m​p​o​r​t​ ​<​u​n​i​s​t​d​.​h​>​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​o​ ​g​r​o​u​p​ ​g​r​o​u​p​.​m​

i​n​t​ ​m​a​i​n​ ​(​v​o​i​d​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​g​r​o​u​p​_​t​ ​g​r​o​u​p​ ​=​ ​d​i​s​p​a​t​c​h​_​g​r​o​u​p​_​c​r​e​a​t​e​ ​(​)​;​

 ​ ​ ​ ​/​/​ ​F​a​n​ ​o​u​t​ ​t​h​e​ ​w​o​r​k​
 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​g​r​o​u​p​_​a​s​y​n​c​ ​(​g​r​o​u​p​,​ ​d​i​s​p​a​t​c​h​_​g​e​t​_​g​l​o​b​a​l​_​q​u​e​u​e​(​0​,​ ​0​)​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​^​{​ ​p​r​i​n​t​f​ ​(​"​I​ ​s​e​e​m​ ​t​o​ ​b​e​ ​a​ ​v​e​r​b​\​n​"​)​;​ ​}​)​;​
 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​g​r​o​u​p​_​a​s​y​n​c​ ​(​g​r​o​u​p​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​s​p​a​t​c​h​_​g​e​t​_​m​a​i​n​_​q​u​e​u​e​(​)​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​^​{​ ​p​r​i​n​t​f​ ​(​"​m​a​i​n​ ​q​u​e​u​e​ ​g​r​o​o​v​y​!​\​n​"​)​;​ ​}​)​;​
 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​g​r​o​u​p​_​a​s​y​n​c​ ​(​g​r​o​u​p​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​s​p​a​t​c​h​_​g​e​t​_​g​l​o​b​a​l​_​q​u​e​u​e​(​D​I​S​P​A​T​C​H​_​Q​U​E​U​E​_​P​R​I​O​R​I​T​Y​_​L​O​W​,​ ​0​)​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​^​{​ ​s​l​e​e​p​ ​(​5​)​;​ ​ ​p​r​i​n​t​f​ ​(​"​b​a​c​k​g​r​o​u​n​d​ ​g​r​o​o​v​y​!​\​n​"​)​;​ ​}​)​;​

 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​g​r​o​u​p​_​n​o​t​i​f​y​ ​(​g​r​o​u​p​,​ ​d​i​s​p​a​t​c​h​_​g​e​t​_​g​l​o​b​a​l​_​q​u​e​u​e​(​0​,​ ​0​)​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​^​{​ ​p​r​i​n​t​f​ ​(​"​a​l​l​ ​d​o​n​e​!​\​n​"​)​;​ ​}​)​;​

 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​a​s​y​n​c​ ​(​d​i​s​p​a​t​c​h​_​g​e​t​_​g​l​o​b​a​l​_​q​u​e​u​e​(​0​,​ ​0​)​,​ ​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​S​t​a​r​t​i​n​g​ ​t​o​ ​w​a​i​t​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​l​o​n​g​ ​r​e​s​u​l​t​ ​=​ ​d​i​s​p​a​t​c​h​_​g​r​o​u​p​_​w​a​i​t​ ​(​g​r​o​u​p​,​ ​D​I​S​P​A​T​C​H​_​T​I​M​E​_​F​O​R​E​V​E​R​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​W​a​i​t​ ​r​e​t​u​r​n​e​d​ ​w​i​t​h​ ​r​e​s​u​l​t​ ​%​l​d​!​\​n​"​,​ ​r​e​s​u​l​t​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​s​p​a​t​c​h​_​r​e​l​e​a​s​e​ ​(​g​r​o​u​p​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​)​;​

 ​ ​ ​ ​s​l​e​e​p​ ​(​1​0​)​;​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​s​t​a​r​t​i​n​g​ ​r​u​n​l​o​o​p​\​n​"​)​;​
 ​ ​ ​ ​[​[​N​S​R​u​n​L​o​o​p​ ​c​u​r​r​e​n​t​R​u​n​L​o​o​p​]​ ​r​u​n​U​n​t​i​l​D​a​t​e​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​[​N​S​D​a​t​e​ ​d​a​t​e​W​i​t​h​T​i​m​e​I​n​t​e​r​v​a​l​S​i​n​c​e​N​o​w​:​ ​1​0​]​]​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​o​u​t​ ​o​f​ ​r​u​n​l​o​o​p​\​n​"​)​;​

 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​ ​/​/​ ​m​a​i​n​

A dispatch group is created, and three blocks are dispatched – two to global queues and one to the main queue. A notification handler is added, as well as a work item to wait on the dispatch group. The waiting is done on a global queue (hence, a background thread) so that the main thread will be free to run the block dispatched to the main queue.

Finally, a runloop is spun for ten seconds to drain any work items on the main queue. When run, group produces this output:

%​ ​.​/​g​r​o​u​p​
I​ ​s​e​e​m​ ​t​o​ ​b​e​ ​a​ ​v​e​r​b​
S​t​a​r​t​i​n​g​ ​t​o​ ​w​a​i​t​
b​a​c​k​g​r​o​u​n​d​ ​g​r​o​o​v​y​!​ ​ ​/​/​ ​5​ ​s​e​c​o​n​d​s​ ​l​a​t​e​r​
s​t​a​r​t​i​n​g​ ​r​u​n​l​o​o​p​ ​ ​ ​ ​/​/​ ​5​ ​s​e​c​o​n​d​s​ ​l​a​t​e​r​
m​a​i​n​ ​q​u​e​u​e​ ​g​r​o​o​v​y​!​
W​a​i​t​ ​r​e​t​u​r​n​e​d​ ​w​i​t​h​ ​r​e​s​u​l​t​ ​0​!​
a​l​l​ ​d​o​n​e​!​
o​u​t​ ​o​f​ ​r​u​n​l​o​o​p​

There are some things of interest to notice. The first work item (“I seem to be a verb”) completes before the wait happens. The third work item (“background groovy!”) has most likely started, but it has not completed yet. You can see the background groovy item completing before the main thread has finished sleeping. Also notice that the work item dispatched to the main queue does not get run until the runloop starts.

An implementation of dispatch_group_async() shows how you would use dispatch_group_enter() and _leave.

v​o​i​d​ ​g​r​o​o​v​y​_​d​i​s​p​a​t​c​h​_​g​r​o​u​p​_​a​s​y​n​c​ ​(​
 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​g​r​o​u​p​_​t​ ​g​r​o​u​p​,​
 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​q​u​e​u​e​_​t​ ​q​u​e​u​e​,​
 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​b​l​o​c​k​_​t​ ​w​o​r​k​)​
{​
 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​r​e​t​a​i​n​(​g​r​o​u​p​)​;​
 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​g​r​o​u​p​_​e​n​t​e​r​(​g​r​o​u​p​)​;​

 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​a​s​y​n​c​(​q​u​e​u​e​,​ ​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​w​o​r​k​(​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​d​i​s​p​a​t​c​h​_​g​r​o​u​p​_​l​e​a​v​e​(​g​r​o​u​p​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​d​i​s​p​a​t​c​h​_​r​e​l​e​a​s​e​(​g​r​o​u​p​)​;​
 ​ ​ ​ ​}​)​;​
}​

Here’s the interesting takeaway from this: the group is retained before being entered and is not released until after it has been left. This prevents the group from becoming empty between the calls and getting destroyed. The group is entered before dispatching the work item and left after the actual work of the item has been completed.

Also, be aware that your application will crash if it disposes of a dispatch group while it still contains work items. Over-leaving the group, that is calling dispatch_group_leave() when there are no work items in the group, will also cause a crash.

Dispatch Sources

Dispatch sources represent streams of events. When an event associated with a dispatch source occurs, an event handler (such as a block) is scheduled on a designated queue. You make a new dispatch source with dispatch_source_create():

dispatch_source_t dispatch_source_create (dispatch_source_type_t type,
                                          uintptr_t handle,
                                          unsigned long mask,
                                          dispatch_queue_t queue);

type is one of these constants:

 ​ ​D​I​S​P​A​T​C​H​_​S​O​U​R​C​E​_​T​Y​P​E​_​D​A​T​A​_​A​D​D​
 ​ ​D​I​S​P​A​T​C​H​_​S​O​U​R​C​E​_​T​Y​P​E​_​D​A​T​A​_​O​R​
 ​ ​D​I​S​P​A​T​C​H​_​S​O​U​R​C​E​_​T​Y​P​E​_​M​A​C​H​_​S​E​N​D​
 ​ ​D​I​S​P​A​T​C​H​_​S​O​U​R​C​E​_​T​Y​P​E​_​M​A​C​H​_​R​E​C​V​
 ​ ​D​I​S​P​A​T​C​H​_​S​O​U​R​C​E​_​T​Y​P​E​_​P​R​O​C​
 ​ ​D​I​S​P​A​T​C​H​_​S​O​U​R​C​E​_​T​Y​P​E​_​R​E​A​D​
 ​ ​D​I​S​P​A​T​C​H​_​S​O​U​R​C​E​_​T​Y​P​E​_​S​I​G​N​A​L​
 ​ ​D​I​S​P​A​T​C​H​_​S​O​U​R​C​E​_​T​Y​P​E​_​T​I​M​E​R​
 ​ ​D​I​S​P​A​T​C​H​_​S​O​U​R​C​E​_​T​Y​P​E​_​V​N​O​D​E​
 ​ ​D​I​S​P​A​T​C​H​_​S​O​U​R​C​E​_​T​Y​P​E​_​W​R​I​T​E​

These constants should look familiar, seeing some of the common culprits you have seen with kqueue(): signals, sockets, files, processes and presses. There are also less common things like mach messages.

You can also create your own custom sources to handle other kinds of events within GCD. Dispatch sources are like using kqueue() but without having to create your own kernel queue or poll for new events. Your block gets scheduled automatically onto a queue when a new event happens.

The expected values of the handle and mask arguments depend on the dispatch source type you are creating. For example, the signal source type would use the signal number as the handle.

queue is the queue where the event handling block is enqueued when something interesting happens on the dispatch source.

The handler block is set with dispatch_source_set_event_handler():

void dispatch_source_set_event_handler (dispatch_source_t source,
                                        void (^block)(void));

Dispatch sources are created in a suspended state. They will do nothing until you make it active by calling dispatch_resume() on it. This lets you do all your configuration of the source without worrying about it firing underneath you. When you are done with a dispatch source, you need to dispatch_source_cancel() it:

void dispatch_source_cancel (dispatch_source_t source);

You can set a handler block that is enqueued when the dispatch source is cancelled:

void dispatch_source_set_cancel_handler (dispatch_source_t source,
                                         void (^block)(void));

Some source types, like those that monitor mach ports and file descriptors, require a cancellation handler where you close the file descriptor. If you close the descriptor before the cancellation handler runs, it might result in a race condition where the file descriptor is re-used for another file and then the dispatch source has a block executed, which could read or write with this different file.

Cancellation does not interrupt any currently executing handler block. You can test for cancellation inside of your handler by calling dispatch_source_testcancel():

long dispatch_source_testcancel (dispatch_source_t source);

This returns a non-zero value if the source has been cancelled.

You also need to set a handler for your dispatch source. The handler is a block which will be dispatched onto the queue that you specified in the dispatch_source_create().

void dispatch_source_set_event_handler (dispatch_source_t source,
                                        void (^block)(void));

Inside of the handler, you can query the source. For instance, you can get the handle and mask parameters used to create the handler:

uintptr_t dispatch_source_get_handle (dispatch_source_t source);

unsigned long dispatch_source_get_mask (dispatch_source_t source);

You can also get at the pending “data” that is peculiar to each handler with dispatch_source_get_data(), such as the number of bytes available to read on a file descriptor or the number of times a signal has fired.

unsigned long dispatch_source_get_data (dispatch_source_t source);

Figure 22.7 shows the different sources and their moving parts.

Figure 22.7  Dispatch source types

Signal sources

As if we did not have enough signal handling API already, GCD adds its own. That being said, this is probably the easiest way to handle signals on OS X and iOS. The source type is DISPATCH_SOURCE_SIGNAL, the handle is the signal number, and the mask is unused so leave it at zero. This code will print on the main queue when a SIGUSR1 is delivered to the application:

 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​s​o​u​r​c​e​_​t​ ​s​o​u​r​c​e​ ​=​ ​d​i​s​p​a​t​c​h​_​s​o​u​r​c​e​_​c​r​e​a​t​e​
 ​ ​ ​ ​ ​ ​ ​ ​(​D​I​S​P​A​T​C​H​_​S​O​U​R​C​E​_​T​Y​P​E​_​S​I​G​N​A​L​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​S​I​G​U​S​R​1​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​0​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​s​p​a​t​c​h​_​g​e​t​_​m​a​i​n​_​q​u​e​u​e​ ​(​)​)​;​

 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​s​o​u​r​c​e​_​s​e​t​_​e​v​e​n​t​_​h​a​n​d​l​e​r​ ​(​s​o​u​r​c​e​,​ ​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​(​"​s​i​g​n​a​l​ ​w​i​t​h​ ​d​a​t​a​ ​%​l​d​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​s​p​a​t​c​h​_​s​o​u​r​c​e​_​g​e​t​_​d​a​t​a​(​s​o​u​r​c​e​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​)​;​

 ​ ​ ​ ​s​i​g​n​a​l​ ​(​S​I​G​U​S​R​1​,​ ​S​I​G​_​I​G​N​)​;​
 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​r​e​s​u​m​e​ ​(​s​o​u​r​c​e​)​;​

This is a very compact way to handle signals, even compared to kqueue. Notice that this does not supersede existing signal-handling mechanics. You could still kqueue this signal. And do not forget to SIG_IGN the signal so that the default signal handing behavior does not kill your process. Also notice that you have to dispatch_resume() the handler for it to start working.

File read source

Much like kqueue’s EVFILT_READ, DISPATCH_SOURCE_TYPE_READ will monitor file descriptors and tell you when they can be read from. The handle is the file descriptor, and the mask is not used, so pass zero. The data returned by dispatch_source_get_data() is an “estimated” number of bytes that can be read from the descriptor. This is a minimum read buffer size, but the system does not guarantee that it will actually read this number of bytes. Apple recommends using non-blocking I/O and handling any truncated reads or error conditions that might occur. This differs from kqueue, which tells you how many bytes you can read before blocking.

This code will read from standard-in and print the characters as they come in.

 ​ ​ ​ ​f​c​n​t​l​ ​(​S​T​D​I​N​_​F​I​L​E​N​O​,​ ​F​_​S​E​T​F​L​,​ ​O​_​N​O​N​B​L​O​C​K​)​;​

 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​s​o​u​r​c​e​_​t​ ​r​e​a​d​e​r​ ​=​ ​d​i​s​p​a​t​c​h​_​s​o​u​r​c​e​_​c​r​e​a​t​e​
 ​ ​ ​ ​ ​ ​ ​ ​(​D​I​S​P​A​T​C​H​_​S​O​U​R​C​E​_​T​Y​P​E​_​R​E​A​D​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​S​T​D​I​N​_​F​I​L​E​N​O​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​0​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​s​p​a​t​c​h​_​g​e​t​_​g​l​o​b​a​l​_​q​u​e​u​e​ ​(​D​I​S​P​A​T​C​H​_​Q​U​E​U​E​_​P​R​I​O​R​I​T​Y​_​D​E​F​A​U​L​T​,​ ​0​)​)​;​

 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​s​o​u​r​c​e​_​s​e​t​_​e​v​e​n​t​_​h​a​n​d​l​e​r​ ​(​r​e​a​d​e​r​,​ ​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​n​s​i​g​n​e​d​ ​l​o​n​g​ ​b​y​t​e​s​A​v​a​i​l​a​b​l​e​ ​=​ ​d​i​s​p​a​t​c​h​_​s​o​u​r​c​e​_​g​e​t​_​d​a​t​a​ ​(​r​e​a​d​e​r​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​s​o​u​r​c​e​_​d​a​t​a​:​ ​%​l​u​\​n​"​,​ ​b​y​t​e​s​A​v​a​i​l​a​b​l​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​1​0​2​4​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​s​i​z​e​_​t​ ​a​m​o​u​n​t​R​e​a​d​ ​=​ ​r​e​a​d​ ​(​S​T​D​I​N​_​F​I​L​E​N​O​,​ ​b​u​f​f​e​r​,​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​ ​-​ ​1​)​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​a​m​o​u​n​t​R​e​a​d​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​s​p​a​t​c​h​_​s​o​u​r​c​e​_​c​a​n​c​e​l​ ​(​r​e​a​d​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​a​m​o​u​n​t​R​e​a​d​ ​=​=​ ​-​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​e​r​r​o​r​ ​r​e​a​d​i​n​g​:​ ​%​d​/​%​s​\​n​"​,​ ​e​r​r​n​o​,​ ​s​t​r​e​r​r​o​r​(​e​r​r​n​o​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​u​f​f​e​r​[​a​m​o​u​n​t​R​e​a​d​]​ ​=​ ​'​\​0​0​0​'​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​a​c​t​u​a​l​l​y​ ​r​e​a​d​ ​%​l​d​:​ ​|​%​s​|​\​n​"​,​ ​a​m​o​u​n​t​R​e​a​d​,​ ​b​u​f​f​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​)​;​

 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​s​o​u​r​c​e​_​s​e​t​_​c​a​n​c​e​l​_​h​a​n​d​l​e​r​ ​(​r​e​a​d​e​r​,​ ​^​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​c​a​n​c​e​l​ ​h​a​n​d​l​e​r​!​\​n​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​l​o​s​e​ ​(​d​i​s​p​a​t​c​h​_​s​o​u​r​c​e​_​g​e​t​_​h​a​n​d​l​e​(​r​e​a​d​e​r​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​i​s​p​a​t​c​h​_​r​e​l​e​a​s​e​ ​(​r​e​a​d​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​)​;​

 ​ ​ ​ ​d​i​s​p​a​t​c​h​_​r​e​s​u​m​e​ ​(​r​e​a​d​e​r​)​;​

Notice that the standard-in file descriptor is set to a non-blocking mode. The event handler block is eventually run on a background thread when there is data to be read. Here it just reads in the maximal amount it can into its buffer, relying on the non-blocking I/O to only read the number of bytes available if it is less than the buffer size. Once the end of file happens, the source cancels itself, causing the cancellation handler to be run, which closes the file descriptor and releases the dispatch source.

File write source

The file write source works like kqueue’s EVFILT_WRITE. These sources monitor file descriptors for buffer space for writing. The dispatch source handle is the file descriptor, and the mask is unused and should be zero.

Using write sources is a bit more difficult than using kqueue, because the source does not tell you how much data you can write before blocking. File descriptors for DISPATCH_SOURCE_TYPE_WRITE dispatch sources should be configured for non-blocking I/O, and you should handle any truncated writes or errors that might happen.

Timer sources

A timer source is one that submits the event handler block to the queue periodically, using the interval given to dispatch_source_set_timer():

void dispatch_source_set_timer (dispatch_source_t source,
                                dispatch_time_t start,
                                uint64_t interval, uint64_t leeway);

The system tries its best to submit the event handler block to the queue on time, but the time the block actually runs might be later.

start is the initial fire time for the timer. Use DISPATCH_TIME_NOW to have the timer start now. interval is in nanoseconds and tells the timer source the period of the timer. leeway is a hint to the system that it can defer the timer’s scheduling in order to help overall system performance. This value is also in nanoseconds. Of course, there will be some latency for all timers even if you use a zero leeway, because OS X is not a real-time system.

The timer will repeat forever until the program exits or dispatch_source_cancel() is called. If you want a one-shot timer, have the timer’s event handler cancel the timer.

The handle and mask arguments when creating the dispatch source are unused and should be set to zero. The data returned by dispatch_source_get_data() is the number of times the timer has fired since the last invocation of the event handler block.

This code will print “timer” to standard out every two seconds. The timer will not actually start until the dispatch source is resumed:

d​i​s​p​a​t​c​h​_​s​o​u​r​c​e​_​t​ ​t​i​m​e​r​ ​=​ ​d​i​s​p​a​t​c​h​_​s​o​u​r​c​e​_​c​r​e​a​t​e​
 ​ ​ ​ ​(​D​I​S​P​A​T​C​H​_​S​O​U​R​C​E​_​T​Y​P​E​_​T​I​M​E​R​,​ ​0​,​ ​0​,​
 ​ ​ ​ ​ ​d​i​s​p​a​t​c​h​_​g​e​t​_​g​l​o​b​a​l​_​q​u​e​u​e​ ​(​D​I​S​P​A​T​C​H​_​Q​U​E​U​E​_​P​R​I​O​R​I​T​Y​_​L​O​W​,​ ​0​)​)​;​

d​i​s​p​a​t​c​h​_​s​o​u​r​c​e​_​s​e​t​_​e​v​e​n​t​_​h​a​n​d​l​e​r​(​t​i​m​e​r​,​ ​^​{​ ​p​r​i​n​t​f​(​"​t​i​m​e​r​\​n​"​)​;​ ​}​)​;​

d​i​s​p​a​t​c​h​_​s​o​u​r​c​e​_​s​e​t​_​t​i​m​e​r​ ​(​t​i​m​e​r​,​ ​D​I​S​P​A​T​C​H​_​T​I​M​E​_​N​O​W​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​2​u​l​l​ ​*​ ​N​S​E​C​_​P​E​R​_​S​E​C​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​1​0​0​0​0​0​0​)​;​
d​i​s​p​a​t​c​h​_​r​e​s​u​m​e​ ​(​t​i​m​e​r​)​;​

Custom sources

Custom sources are created by specifying a source type of DISPATCH_SOURCE_TYPE_DATA_ADD or _OR and configuring them like you would any other source. Trigger them with dispatch_source_merge_data():

void dispatch_source_merge_data (dispatch_source_t source,
                                 unsigned long data);

The data is an unsigned long, which is merged into the data that has been accumulated since the last time the event handler was run, either by adding/summing or or-ing the supplied value, per the type of the source. Only if the merged data is non-zero will the source be triggered. So if you wish to ensure the source is triggered, you must ensure the supplied data is non-zero. The data buffer is zeroed out for each invocation of the data source’s event handler.

If the data being merged in is irrelevant to you and you simply want the source to be triggered, do not worry about the type or the data, just create a custom source, rig up the handlers, and trigger it with a non-zero value.

Under the Hood

Dispatch sources are actually dispatch queues with some extra frobs. This explains their having a target queue property and a meaningful suspend count property.

Every source except timers and custom sources ends up being expressed as a kevent added to a single kqueue. This kqueue is owned and managed by a manager GCD queue. If you attempt to track a file descriptor that kqueue does not support, GCD will fall back and use select().

The manager queue itself is responsible for handling timers and custom sources. These use pseudo-kevent in order to fit in with the kevent-centric design of the manager queue.

Dispatch timers are kept in two lists corresponding to the type of timer (walltime or clocktime). The lists are sorted in ascending order by the next fire time and terminated by a sentinel timer with a fire time of DISPATCH_TIME_NOW.

Semaphores

GCD provides semaphores – objects that threads can wait on and signal with (where signal is not a Unix signal). There are two kinds of semaphores: binary semaphores and counting semaphores.

A binary semaphore is a single flag. The flag starts out “signaled.” Waiting on it claims the flag. Any further attempts to wait on it require blocking until it is signaled again.

A counting semaphore is often initialized with the count of some resource and then used to allocate that resource. Waiting on the semaphore claims a unit, and signaling releases that claim. Only when the units have run out (the semaphore’s count being zero) does waiting on the semaphore actually block, requiring waiting for a unit to become available. Waiting can block, but signaling is always non-blocking.

A dispatch semaphore is a counting semaphore. What makes it special is that it takes care to be as fast as possible in its implementation. It only calls down to the kernel when a thread has to block or be woken. The kernel is not involved if there is no contention in the semaphore operation.

Unlike Unix semaphores, dispatch semaphores only work within a single process, so they can’t be used for any form of interprocess communication.

Create a dispatch with dispatch_semaphore_create(). Get rid of it by releasing it.

dispatch_semaphore_t dispatch_semaphore_create (long count);

count initializes the semaphore’s count. Pass a count of zero to make a binary semaphore. You can wait on a semaphore up until a specific time with dispatch_semaphore_wait():

long dispatch_semaphore_wait (dispatch_semaphore_t semaphore,
                              dispatch_time_t timeout);

The return value will be zero on success, non-zero if the wait timed out. Signal a semaphore with this function:

long dispatch_semaphore_signal (dispatch_semaphore_t semaphore);

It returns a non-zero value if a thread was woken up due to the signal, and zero otherwise.

Under the hood, dispatch groups are just dispatch semaphores created with a very large value. A work item is marked as entering by waiting on the semaphore. A work item is marked as leaving by signaling it. Unfortunately, this abstraction leaks in a bad way if you over-leave a group. The program will crash without any handy application-specific information, with the reported reason being something like EXC_ARITHMETIC (SIGFPE) with the backtrace showing the crash in dispatch_semaphore_signal().

GCD or NSOperation?

You have now seen two different ways of architecting your code to support task-oriented parallelism, NSOperation and Grand Central Dispatch.

With GCD, the code for a particular operation tends to be concentrated in one place. You have a block that does work and then puts blocks onto other queues to do other work – the whole “layers of an onion” thing. GCD is very fast. There is little baggage associated with it. There is no reliance on KVO. GCD calls tend to be more ad-hoc and disposable, “I’ll just put this here to run this on the main queue” kinds of things. Plus, it is easier to copy and paste and create similar blocks doing the same work.

NSOperation tends to have the code more spread around. The code implementing an operation is usually in its own class with its own set of source files. Operations live at a higher level of abstraction, too. NSOperation is available pre-Snow Leopard, in case you are supporting older operating systems. NSOperation allows dependencies between operations.

Constructing similar things in GCD can become complicated as you build up dispatch groups and control things with counted semaphores.

Operations are easier to cancel. You need to invent your own cancellation mechanism in GCD. Finally, because operations are frequently expressed as classes, they are more concrete, reusable objects.

If you need to do work in the background, you should seriously consider using GCD or NSOperations for partitioning the work. The farther away from raw threads you can get, the happier you will be.

For the More Curious: Synchronizing Tasks

Synchronization enforces a temporal ordering among events. If you were using threads directly, you would use mutexes, semaphores, and condition variables (see Chapter 20: Multithreading) to express the desired synchronization.

Grand Central Dispatch allows for a simpler approach. To serialize access to a resource, use a serial queue associated with the resource rather than a mutex.

To wait for an asynchronous event, use a dispatch source rather than a condition variable.

To wait for a task to complete, simply dispatch the follow-up task from the end of that task. If a task depends on several tasks that can execute concurrently, target them all to a concurrent queue and use a dispatch group to asynchronously follow up on the completion of the group of tasks that were blocking the one task.

If you must drop down to basics, prefer using GCD’s semaphores and dispatch predicates to the Posix threads equivalents, except where portability is a concern.

If you have access to NSOperation, prefer that to Grand Central Dispatch; it is layered on top of GCD, but it simplifies expressing interdependencies between tasks. As a higher-level API, it is also less likely to change (or at least likely to change more slowly) as the system evolves.

For the More Curious: The dispatch_debug() Function

Dispatch objects provide one more function we have yet to discuss. The dispatch_debug function logs debugging information about any dispatch object:

void dispatch_debug (dispatch_object_t object,
                     const char *message, ...);

In the released version of the library, dispatch_debug() logs to the system log rather than to the console.

The logged information is intended to assist developers of the libdispatch library and is only marginally useful to users of Grand Central Dispatch. For example, here is the output generated for several different kinds of dispatch objects:

c​o​m​.​b​i​g​n​e​r​d​r​a​n​c​h​.​d​i​s​p​a​t​c​h​_​d​e​b​u​g​:​ ​
 ​ ​ ​ ​c​o​m​.​a​p​p​l​e​.​r​o​o​t​.​d​e​f​a​u​l​t​-​p​r​i​o​r​i​t​y​[​0​x​7​f​f​f​7​1​0​0​1​a​f​0​]​ ​=​ ​{​ ​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​f​c​n​t​ ​=​ ​0​x​f​f​f​f​f​f​f​f​,​ ​s​u​s​p​e​n​d​_​c​n​t​ ​=​ ​0​x​1​,​ ​p​a​r​e​n​t​ ​=​ ​0​x​0​
 ​ ​ ​ ​}​:​ ​g​l​o​b​a​l​ ​q​u​e​u​e​ ​e​x​a​m​p​l​e​
c​o​m​.​b​i​g​n​e​r​d​r​a​n​c​h​.​d​i​s​p​a​t​c​h​_​d​e​b​u​g​:​ ​
 ​ ​ ​ ​c​o​m​.​b​i​g​n​e​r​d​r​a​n​c​h​.​e​x​a​m​p​l​e​q​[​0​x​1​0​0​1​0​0​2​0​0​]​ ​=​ ​{​ ​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​f​c​n​t​ ​=​ ​0​x​2​,​ ​s​u​s​p​e​n​d​_​c​n​t​ ​=​ ​0​x​0​,​ ​p​a​r​e​n​t​ ​=​ ​0​x​7​f​f​f​7​1​0​0​1​b​9​8​ ​
 ​ ​ ​ ​}​:​ ​s​e​r​i​a​l​ ​q​u​e​u​e​ ​e​x​a​m​p​l​e​
c​o​m​.​b​i​g​n​e​r​d​r​a​n​c​h​.​d​i​s​p​a​t​c​h​_​d​e​b​u​g​:​ ​
 ​ ​ ​ ​s​e​m​a​p​h​o​r​e​[​0​x​1​0​0​1​0​0​2​b​0​]​ ​=​ ​{​ ​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​f​c​n​t​ ​=​ ​0​x​1​,​ ​s​u​s​p​e​n​d​_​c​n​t​ ​=​ ​0​x​0​,​ ​p​o​r​t​ ​=​ ​0​x​0​,​ ​
 ​ ​ ​ ​ ​ ​ ​ ​v​a​l​u​e​ ​=​ ​9​2​2​3​3​7​2​0​3​6​8​5​4​7​7​5​8​0​7​,​ ​o​r​i​g​ ​=​ ​9​2​2​3​3​7​2​0​3​6​8​5​4​7​7​5​8​0​7​ ​
 ​ ​ ​ ​}​:​ ​g​r​o​u​p​ ​e​x​a​m​p​l​e​
c​o​m​.​b​i​g​n​e​r​d​r​a​n​c​h​.​d​i​s​p​a​t​c​h​_​d​e​b​u​g​:​ ​
 ​ ​ ​ ​s​e​m​a​p​h​o​r​e​[​0​x​1​0​0​1​0​0​3​2​0​]​ ​=​ ​{​ ​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​f​c​n​t​ ​=​ ​0​x​1​,​ ​s​u​s​p​e​n​d​_​c​n​t​ ​=​ ​0​x​0​,​ ​p​o​r​t​ ​=​ ​0​x​0​,​ ​
 ​ ​ ​ ​ ​ ​ ​ ​v​a​l​u​e​ ​=​ ​0​,​ ​o​r​i​g​ ​=​ ​0​ ​
 ​ ​ ​ ​}​:​ ​s​e​m​a​p​h​o​r​e​ ​e​x​a​m​p​l​e​
c​o​m​.​b​i​g​n​e​r​d​r​a​n​c​h​.​d​i​s​p​a​t​c​h​_​d​e​b​u​g​:​ ​
 ​ ​ ​ ​k​e​v​e​n​t​-​s​o​u​r​c​e​[​0​x​1​0​0​1​0​0​3​9​0​]​ ​=​ ​{​ ​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​f​c​n​t​ ​=​ ​0​x​2​,​ ​s​u​s​p​e​n​d​_​c​n​t​ ​=​ ​0​x​0​,​ ​
 ​ ​ ​ ​ ​ ​ ​ ​t​a​r​g​e​t​ ​=​ ​c​o​m​.​a​p​p​l​e​.​r​o​o​t​.​d​e​f​a​u​l​t​-​p​r​i​o​r​i​t​y​[​0​x​7​f​f​f​7​1​0​0​1​a​f​0​]​,​ ​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​n​d​i​n​g​_​d​a​t​a​ ​=​ ​0​x​0​,​ ​p​e​n​d​i​n​g​_​d​a​t​a​_​m​a​s​k​ ​=​ ​0​x​1​8​,​ ​
 ​ ​ ​ ​ ​ ​ ​ ​f​i​l​t​e​r​ ​=​ ​E​V​F​I​L​T​_​V​N​O​D​E​ ​
 ​ ​ ​ ​}​:​ ​v​n​o​d​e​ ​s​o​u​r​c​e​ ​e​x​a​m​p​l​e​
c​o​m​.​b​i​g​n​e​r​d​r​a​n​c​h​.​d​i​s​p​a​t​c​h​_​d​e​b​u​g​:​ ​
 ​ ​ ​ ​k​e​v​e​n​t​-​s​o​u​r​c​e​[​0​x​1​0​0​1​0​0​4​c​0​]​ ​=​ ​{​ ​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​f​c​n​t​ ​=​ ​0​x​2​,​ ​s​u​s​p​e​n​d​_​c​n​t​ ​=​ ​0​x​2​,​ ​
 ​ ​ ​ ​ ​ ​ ​ ​t​a​r​g​e​t​ ​=​ ​c​o​m​.​b​i​g​n​e​r​d​r​a​n​c​h​.​e​x​a​m​p​l​e​q​[​0​x​1​0​0​1​0​0​2​0​0​]​,​ ​
 ​ ​ ​ ​ ​ ​ ​ ​p​e​n​d​i​n​g​_​d​a​t​a​ ​=​ ​0​x​1​,​ ​p​e​n​d​i​n​g​_​d​a​t​a​_​m​a​s​k​ ​=​ ​0​x​f​f​f​f​f​f​f​f​,​ ​
 ​ ​ ​ ​ ​ ​ ​ ​f​i​l​t​e​r​ ​=​ ​D​I​S​P​A​T​C​H​_​E​V​F​I​L​T​_​C​U​S​T​O​M​_​O​R​ ​
 ​ ​ ​ ​}​:​ ​d​a​t​a​-​o​r​ ​s​o​u​r​c​e​ ​e​x​a​m​p​l​e​

Notice that the data-or source is suspended (as indicated by a suspend_cnt of 2 or more) and has pending data. The pending data mask for the vnode source indicates that it will be triggered by the LINK and ATTRIB events; the debug output does not include the handle, so it is impossible to tell which file descriptor is being monitored.

The reference count it reports is the internal reference count, not the external reference count. It provides no information whatsoever about how many times you’d have to call dispatch_release() to dispose of the object. Dispatch objects have no equivalent to the Objective-C -retainCount method.

The suspend count reported is that manipulated by the library. The number of times you’d have to call dispatch_resume() on the object is half the reported suspend count. This means that only a reported suspend count greater than 1 indicates a suspended object. The first bit of the suspend count does not in fact indicate suspension, but it is used by the library as a lock bit.

Exercises

Rewrite WordCounter’s -countWords: method to return its value in a way other than a struct. How does that affect the memory management that was done in the main queue block? Is the rewritten version cleaner and easier to understand than the original version?

Do more word analysis calculations in WordCounter. Display the word frequencies in a tableview. Hit the network and download definitions of words. Or hit the network and download images from Google Image Search and display them in a tableview.

What happens if a serial queue is made to target itself?

What happens if you create a “target queue cycle” between two or more serial queues? Start by creating two serial queues and setting each to target the other. Enqueue a work item on one of the queues and then sleep.

Write and test the function dispatch_time_add:

dispatch_time_t dispatch_time_add (dispatch_time_t time,
                                   int seconds);

dispatch_time() and NSEC_PER_SEC.)

Write and test the function dispatch_groups_async:

void dispatch_groups_async (dispatch_group_t groups[],
                            const size_t group_count,
                            dispatch_queue_t queue,
                            dispatch_block_t work)

This should enqueue and asynchronously run a single work block. This block should be entered into all of the provided groups before it is dispatched to the queue and should be made to leave all of the groups after its work is complete.

Use a timer source to implement sleep_ns():

void sleep_ns (uint64_t nanos)

This function should block execution of the current thread for the provided number of nanoseconds. (You might find a dispatch group or semaphore helpful.)

Put the nodepool from Chapter 7: Memory behind a queue-based interface. Compare the performance to the thread-safe nodepool from the exercises in Chapter 20: Multithreading.

23
Accessing the Keychain

A keychain is a file that holds passwords and certificates as well as information about those passwords and certificates. For the purposes of this chapter, “password” is used interchangeably with “password or certificate.” Each user can have several keychains, but most people only have one: ~/Library/Keychains/login.keychain. There is a set of functions and data structures in the Security framework that allow you to read and write passwords and their associated data. A password and its associated data is known as a keychain item.

Users can inspect their keychains using the application Keychain Access, as shown in Figure 23.1:

Figure 23.1  The Keychain Access application

Some data inside the keychain is encrypted and can only be accessed if the keychain is unlocked. The user unlocks a keychain by typing a password into a keychain panel, as shown in Figure 23.2

Figure 23.2  Unlocking the keychain

Using the Keychain Access application, users can control access to individual keychain items, as shown in Figure 23.3

Figure 23.3  Keychain access control

Items and Attribute Lists

Keychains can hold a number of different types of items. Internet passwords are associated with a particular user on a server domain and protocol. AppleShare passwords are used for remote access credentials to AppleShare servers. Certificate items hold certificates, not passwords. Private and Public keys, such as those used in the iOS development portal, are also kept in the keychain.

Password items have the user’s password as its data item, as well as a collection of attributes. The attributes are identified by a four-byte code. There are a few dozen of these codes. Refer to /System/Library/Frameworks/​Security.framework/​Headers/​SecKeychainItem.h Here are a few examples of four-byte codes:

kSecCreationDateItemAttr

('cdat') Identifies the creation date attribute. You use this tag to set or get a value of type UInt32 that indicates the date the item was created.

kSecDescriptionItemAttr

('desc') Identifies the description attribute. You use this tag to set or get a value of type string that represents a user-visible string describing this particular kind of item (for example, “disk image password”).

kSecLabelItemAttr

('labl') Identifies the label attribute. You use this tag to set or get a value of type string that represents a user-editable string containing the label for this item.

kSecCustomIconItemAttr

('cusi') Identifies the custom icon attribute. You use this tag to set or get a value of type Boolean that indicates whether the item has an application-specific icon. To do this, you must also set the attribute value identified by the tag kSecTypeItemAttr to a file type, for which there is a corresponding icon in the desktop database, and set the attribute value identified by the tag kSecCreatorItemAttr to an appropriate application creator type. If a custom icon corresponding to the item’s type and creator can be found in the desktop database, it will be displayed by Keychain Access. Otherwise, default icons are used.

kSecAccountItemAttr

('acct') Identifies the account attribute. You use this tag to set or get a string that represents the user account. It also applies to generic and AppleShare passwords.

kSecSecurityDomainItemAttr

('sdmn') Identifies the security domain attribute. You use this tag to set or get a value that represents the Internet security domain. This is unique to Internet password attributes.

kSecServerItemAttr

('srvr') Identifies the server attribute. You use this tag to set or get a value of type string that represents the Internet server name or IP address. This is unique to Internet password attributes.

Use a SecKeychainAttributeList to read and write these attributes:

s​t​r​u​c​t​ ​S​e​c​K​e​y​c​h​a​i​n​A​t​t​r​i​b​u​t​e​L​i​s​t​ ​{​
 ​ ​ ​ ​U​I​n​t​3​2​ ​c​o​u​n​t​;​
 ​ ​ ​ ​S​e​c​K​e​y​c​h​a​i​n​A​t​t​r​i​b​u​t​e​ ​*​a​t​t​r​;​
}​;​

count is the number of attributes in the list. And attr is a pointer to the first one.

s​t​r​u​c​t​ ​S​e​c​K​e​y​c​h​a​i​n​A​t​t​r​i​b​u​t​e​ ​ ​{​
 ​ ​ ​ ​S​e​c​K​e​y​c​h​a​i​n​A​t​t​r​T​y​p​e​ ​t​a​g​;​
 ​ ​ ​ ​U​I​n​t​3​2​ ​l​e​n​g​t​h​;​
 ​ ​ ​ ​v​o​i​d​ ​*​d​a​t​a​;​
}​;​

The tag is the four-byte code listed above. The length is the number of bytes in the data buffer. Usually, the data buffer is just a string.

Figure 23.4 shows a keychain attribute list with three attributes. The first attribute is the account name, with a value of “aaron”, the second is description of the item, and the last is when the keychain item was created.

Figure 23.4  Keychain attribute list

Searching for Items

Attribute lists are used in three ways:

to specify a search for items.

to read data from an item.

to set the data for an item.

For example, to create a search for all generic passwords that have bignerdranch as the account name, you would create an attribute list and invoke SecKeychainSearchCreateFromAttributes().

OSStatus SecKeychainSearchCreateFromAttributes (CFTypeRef  keychainOrArray,
                                             SecItemClass  itemClass,
                           const SecKeychainAttributeList *attrList,
                                     SecKeychainSearchRef *searchRef);

Most of the functions in the Security framework deal well with NULLs. They do what you would hope. For example, if you pass NULL as the first argument, the search will check all the user’s normal keychains. Usually, this is what you want. For the second argument, you will pass one of the following:

kSecInternetPasswordItemClass

kSecGenericPasswordItemClass

kSecAppleSharePasswordItemClass

kSecCertificateItemClass

If you pass NULL as the attribute list, all items of that class will be returned. The final argument is a pointer to a search-specifying structure.

Example 23.1 is a search that counts the number of Internet passwords on your keychain for the account specified on the command line. We will use just SecKeychainItemRef here and fill in the details in the next section:

Example 23.1. dumpem.m

/​/​ ​d​u​m​p​e​m​.​m​ ​-​-​ ​l​o​o​k​ ​i​n​t​o​ ​t​h​e​ ​k​e​y​c​h​a​i​n​

/​/​ ​g​c​c​ ​-​s​t​d​=​c​9​9​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​S​e​c​u​r​i​t​y​ ​-​f​r​a​m​e​w​o​r​k​ ​F​o​u​n​d​a​t​i​o​n​ ​-​o​ ​d​u​m​p​e​m​ ​d​u​m​p​e​m​.​m​

#​i​m​p​o​r​t​ ​<​S​e​c​u​r​i​t​y​/​S​e​c​u​r​i​t​y​.​h​>​
#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

#​i​m​p​o​r​t​ ​<​s​t​d​l​i​b​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​E​X​I​T​_​S​U​C​C​E​S​S​
#​i​m​p​o​r​t​ ​<​s​t​d​i​o​.​h​>​ ​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​ ​a​n​d​ ​f​r​i​e​n​d​s​
#​i​m​p​o​r​t​ ​<​s​t​r​i​n​g​.​h​>​ ​ ​ ​ ​ ​ ​/​/​ ​f​o​r​ ​s​t​r​n​c​p​y​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​*​p​o​o​l​ ​=​ ​[​[​N​S​A​u​t​o​r​e​l​e​a​s​e​P​o​o​l​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​i​f​ ​(​a​r​g​c​ ​!​=​ ​2​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​u​s​a​g​e​:​ ​%​s​ ​a​c​c​o​u​n​t​-​n​a​m​e​\​n​"​,​ ​a​r​g​v​[​0​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​F​A​I​L​U​R​E​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​B​u​i​l​d​ ​a​n​ ​a​t​t​r​i​b​u​t​e​ ​l​i​s​t​ ​w​i​t​h​ ​j​u​s​t​ ​o​n​e​ ​a​t​t​r​i​b​u​t​e​.​
 ​ ​ ​ ​S​e​c​K​e​y​c​h​a​i​n​A​t​t​r​i​b​u​t​e​ ​a​t​t​r​i​b​u​t​e​;​
 ​ ​ ​ ​a​t​t​r​i​b​u​t​e​.​t​a​g​ ​=​ ​k​S​e​c​A​c​c​o​u​n​t​I​t​e​m​A​t​t​r​;​
 ​ ​ ​ ​a​t​t​r​i​b​u​t​e​.​l​e​n​g​t​h​ ​=​ ​s​t​r​l​e​n​(​a​r​g​v​[​1​]​)​;​
 ​ ​ ​ ​a​t​t​r​i​b​u​t​e​.​d​a​t​a​ ​=​ ​a​r​g​v​[​1​]​;​

 ​ ​ ​ ​/​/​ ​M​a​k​e​ ​a​ ​l​i​s​t​ ​t​o​ ​p​o​i​n​t​ ​t​o​ ​t​h​i​s​ ​n​e​w​ ​a​t​t​r​i​b​u​t​e​.​
 ​ ​ ​ ​S​e​c​K​e​y​c​h​a​i​n​A​t​t​r​i​b​u​t​e​L​i​s​t​ ​l​i​s​t​;​
 ​ ​ ​ ​l​i​s​t​.​c​o​u​n​t​ ​=​ ​1​;​
 ​ ​ ​ ​l​i​s​t​.​a​t​t​r​ ​=​ ​&​a​t​t​r​i​b​u​t​e​;​

 ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​a​ ​s​e​a​r​c​h​ ​h​a​n​d​l​e​ ​w​i​t​h​ ​t​h​e​ ​a​t​t​r​i​b​u​t​e​ ​l​i​s​t​.​
 ​ ​ ​ ​S​e​c​K​e​y​c​h​a​i​n​S​e​a​r​c​h​R​e​f​ ​s​e​a​r​c​h​;​
 ​ ​ ​ ​O​S​E​r​r​ ​r​e​s​u​l​t​ ​=​ ​S​e​c​K​e​y​c​h​a​i​n​S​e​a​r​c​h​C​r​e​a​t​e​F​r​o​m​A​t​t​r​i​b​u​t​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​(​N​U​L​L​,​ ​k​S​e​c​I​n​t​e​r​n​e​t​P​a​s​s​w​o​r​d​I​t​e​m​C​l​a​s​s​,​ ​&​l​i​s​t​,​ ​&​s​e​a​r​c​h​)​;​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​!​=​ ​n​o​E​r​r​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​r​e​s​u​l​t​ ​%​d​ ​f​r​o​m​ ​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​"​S​e​c​K​e​y​c​h​a​i​n​S​e​a​r​c​h​C​r​e​a​t​e​F​r​o​m​A​t​t​r​i​b​u​t​e​s​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​I​t​e​r​a​t​e​ ​o​v​e​r​ ​t​h​e​ ​s​e​a​r​c​h​ ​r​e​s​u​l​t​s​
 ​ ​ ​ ​i​n​t​ ​c​o​u​n​t​ ​=​ ​0​;​
 ​ ​ ​ ​S​e​c​K​e​y​c​h​a​i​n​I​t​e​m​R​e​f​ ​i​t​e​m​;​
 ​ ​ ​ ​w​h​i​l​e​ ​(​S​e​c​K​e​y​c​h​a​i​n​S​e​a​r​c​h​C​o​p​y​N​e​x​t​ ​(​s​e​a​r​c​h​,​ ​&​i​t​e​m​)​ ​!​=​ ​e​r​r​S​e​c​I​t​e​m​N​o​t​F​o​u​n​d​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​C​F​R​e​l​e​a​s​e​ ​(​i​t​e​m​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​u​n​t​+​+​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​d​ ​i​t​e​m​s​ ​f​o​u​n​d​\​n​"​,​ ​c​o​u​n​t​)​;​
 ​ ​ ​ ​C​F​R​e​l​e​a​s​e​ ​(​s​e​a​r​c​h​)​;​
 ​ ​ ​ ​[​p​o​o​l​ ​d​r​a​i​n​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

Before building and running the program, launch Keychain Access, which lives in /Applications/Utilities/. Look around and find an item with an account property that you can display. Then run dumpem.

$​ ​.​/​d​u​m​p​e​m​ ​b​o​r​k​w​a​r​e​
3​ ​i​t​e​m​s​ ​f​o​u​n​d​

Reading Data From an Item

Of course, once you have fetched the item, you can do all sorts of nifty things with it. For example, if you wanted to read the password, the account name, the description, and the modification date from an item, you would create an attribute list containing those attributes and call SecKeychainItemCopyContent():

OSStatus SecKeychainItemCopyContent (SecKeychainItemRef  itemRef,
                                           SecItemClass *itemClass,
                               SecKeychainAttributeList *attrList,
                                                 UInt32 *length,
                                                   void **outData);

Note that here the attrList is specifying what data you want and is also acting as a receptacle. Add these functions to dumpem.m:

/​/​ ​G​i​v​e​n​ ​a​ ​c​a​r​b​o​n​-​s​t​y​l​e​ ​f​o​u​r​ ​c​h​a​r​a​c​t​e​r​ ​c​o​d​e​,​ ​m​a​k​e​ ​a​ ​C​ ​s​t​r​i​n​g​ ​t​h​a​t​ ​c​a​n​
/​/​ ​b​e​ ​g​i​v​e​n​ ​t​o​ ​p​r​i​n​t​f​.​
c​o​n​s​t​ ​c​h​a​r​ ​*​f​o​u​r​B​y​t​e​C​o​d​e​S​t​r​i​n​g​ ​(​U​I​n​t​3​2​ ​c​o​d​e​)​ ​{​
 ​ ​ ​ ​/​/​ ​S​p​l​a​t​ ​t​h​e​ ​b​y​t​e​s​ ​o​f​ ​t​h​e​ ​f​o​u​r​ ​c​h​a​r​a​c​t​e​r​ ​c​o​d​e​ ​i​n​t​o​ ​a​ ​c​h​a​r​a​c​t​e​r​ ​a​r​r​a​y​.​
 ​ ​ ​ ​t​y​p​e​d​e​f​ ​u​n​i​o​n​ ​t​h​e​C​h​e​a​t​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​U​I​n​t​3​2​ ​c​o​d​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​s​t​r​i​n​g​[​4​]​;​
 ​ ​ ​ ​}​ ​t​h​e​C​h​e​a​t​;​

 ​ ​ ​ ​/​/​ ​C​ ​s​t​r​i​n​g​ ​t​h​a​t​ ​g​e​t​s​ ​r​e​t​u​r​n​e​d​.​
 ​ ​ ​ ​s​t​a​t​i​c​ ​c​h​a​r​ ​s​t​r​i​n​g​[​5​]​;​

 ​ ​ ​ ​/​/​ ​B​y​t​e​-​s​w​a​p​,​ ​o​t​h​e​r​w​i​s​e​ ​s​t​r​i​n​g​ ​w​i​l​l​ ​b​e​ ​b​a​c​k​w​a​r​d​s​ ​o​n​ ​l​i​t​t​l​e​ ​e​n​d​i​a​n​ ​s​y​s​t​e​m​s​.​
 ​ ​ ​ ​(​(​t​h​e​C​h​e​a​t​*​)​s​t​r​i​n​g​)​-​>​c​o​d​e​ ​=​ ​n​t​o​h​l​(​c​o​d​e​)​;​
 ​ ​ ​ ​s​t​r​i​n​g​[​4​]​ ​=​ ​'​\​0​'​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​(​s​t​r​i​n​g​)​;​

}​ ​/​/​ ​f​o​u​r​B​y​t​e​C​o​d​e​S​t​r​i​n​g​

/​/​ ​D​i​s​p​l​a​y​ ​e​a​c​h​ ​a​t​t​r​i​b​u​t​e​ ​i​n​ ​t​h​e​ ​l​i​s​t​.​
v​o​i​d​ ​s​h​o​w​L​i​s​t​ ​(​S​e​c​K​e​y​c​h​a​i​n​A​t​t​r​i​b​u​t​e​L​i​s​t​ ​l​i​s​t​)​ ​{​
 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​l​i​s​t​.​c​o​u​n​t​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​S​e​c​K​e​y​c​h​a​i​n​A​t​t​r​i​b​u​t​e​ ​a​t​t​r​ ​=​ ​l​i​s​t​.​a​t​t​r​[​i​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​b​u​f​f​e​r​[​1​0​2​4​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​a​t​t​r​.​l​e​n​g​t​h​ ​<​ ​s​i​z​e​o​f​(​b​u​f​f​e​r​)​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​m​a​k​e​ ​a​ ​c​o​p​y​ ​o​f​ ​t​h​e​ ​d​a​t​a​ ​s​o​ ​w​e​ ​c​a​n​ ​s​t​i​c​k​ ​o​n​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​a​ ​t​r​a​i​l​i​n​g​ ​z​e​r​o​ ​b​y​t​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​r​n​c​p​y​ ​(​b​u​f​f​e​r​,​ ​a​t​t​r​.​d​a​t​a​,​ ​a​t​t​r​.​l​e​n​g​t​h​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​u​f​f​e​r​[​a​t​t​r​.​l​e​n​g​t​h​]​ ​=​ ​'​\​0​'​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​t​%​d​:​ ​'​%​s​'​ ​=​ ​\​"​%​s​\​"​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​,​ ​f​o​u​r​B​y​t​e​C​o​d​e​S​t​r​i​n​g​(​a​t​t​r​.​t​a​g​)​,​ ​b​u​f​f​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​a​t​t​r​i​b​u​t​e​ ​%​s​ ​i​s​ ​m​o​r​e​ ​t​h​a​n​ ​1​K​\​n​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​u​r​B​y​t​e​C​o​d​e​S​t​r​i​n​g​(​a​t​t​r​.​t​a​g​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

}​ ​/​/​ ​s​h​o​w​L​i​s​t​

/​/​ ​D​i​s​p​l​a​y​ ​a​ ​k​e​y​c​h​a​i​n​ ​i​t​e​m​'​s​ ​i​n​f​o​.​
v​o​i​d​ ​d​u​m​p​I​t​e​m​ ​(​S​e​c​K​e​y​c​h​a​i​n​I​t​e​m​R​e​f​ ​i​t​e​m​,​ ​b​o​o​l​ ​d​i​s​p​l​a​y​P​a​s​s​w​o​r​d​)​ ​{​
 ​ ​ ​ ​/​/​ ​B​u​i​l​d​ ​t​h​e​ ​a​t​t​r​i​b​u​t​e​s​ ​w​e​'​r​e​ ​i​n​t​e​r​e​s​t​e​d​ ​i​n​ ​e​x​a​m​i​n​i​n​g​.​
 ​ ​ ​ ​S​e​c​K​e​y​c​h​a​i​n​A​t​t​r​i​b​u​t​e​ ​a​t​t​r​i​b​u​t​e​s​[​3​]​;​
 ​ ​ ​ ​a​t​t​r​i​b​u​t​e​s​[​0​]​.​t​a​g​ ​=​ ​k​S​e​c​A​c​c​o​u​n​t​I​t​e​m​A​t​t​r​;​
 ​ ​ ​ ​a​t​t​r​i​b​u​t​e​s​[​1​]​.​t​a​g​ ​=​ ​k​S​e​c​D​e​s​c​r​i​p​t​i​o​n​I​t​e​m​A​t​t​r​;​
 ​ ​ ​ ​a​t​t​r​i​b​u​t​e​s​[​2​]​.​t​a​g​ ​=​ ​k​S​e​c​M​o​d​D​a​t​e​I​t​e​m​A​t​t​r​;​

 ​ ​ ​ ​S​e​c​K​e​y​c​h​a​i​n​A​t​t​r​i​b​u​t​e​L​i​s​t​ ​l​i​s​t​;​
 ​ ​ ​ ​l​i​s​t​.​c​o​u​n​t​ ​=​ ​3​;​
 ​ ​ ​ ​l​i​s​t​.​a​t​t​r​ ​=​ ​a​t​t​r​i​b​u​t​e​s​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​i​t​e​m​'​s​ ​i​n​f​o​r​m​a​t​i​o​n​,​ ​i​n​c​l​u​d​i​n​g​ ​t​h​e​ ​p​a​s​s​w​o​r​d​.​
 ​ ​ ​ ​U​I​n​t​3​2​ ​l​e​n​g​t​h​ ​=​ ​0​;​
 ​ ​ ​ ​c​h​a​r​ ​*​p​a​s​s​w​o​r​d​ ​=​ ​N​U​L​L​;​
 ​ ​ ​ ​O​S​E​r​r​ ​r​e​s​u​l​t​;​
 ​ ​ ​ ​i​f​ ​(​d​i​s​p​l​a​y​P​a​s​s​w​o​r​d​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​S​e​c​K​e​y​c​h​a​i​n​I​t​e​m​C​o​p​y​C​o​n​t​e​n​t​ ​(​i​t​e​m​,​ ​N​U​L​L​,​ ​&​l​i​s​t​,​ ​&​l​e​n​g​t​h​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​v​o​i​d​ ​*​*​)​&​p​a​s​s​w​o​r​d​)​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​s​u​l​t​ ​=​ ​S​e​c​K​e​y​c​h​a​i​n​I​t​e​m​C​o​p​y​C​o​n​t​e​n​t​ ​(​i​t​e​m​,​ ​N​U​L​L​,​ ​&​l​i​s​t​,​ ​N​U​L​L​,​ ​N​U​L​L​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​i​f​ ​(​r​e​s​u​l​t​ ​!​=​ ​n​o​E​r​r​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​d​u​m​p​I​t​e​m​:​ ​e​r​r​o​r​ ​r​e​s​u​l​t​ ​o​f​ ​%​d​\​n​"​,​ ​r​e​s​u​l​t​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​i​f​ ​(​p​a​s​s​w​o​r​d​ ​!​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​o​p​y​ ​t​h​e​ ​p​a​s​s​w​o​r​d​ ​i​n​t​o​ ​a​ ​b​u​f​f​e​r​ ​a​n​d​ ​a​t​t​a​c​h​ ​a​ ​t​r​a​i​l​i​n​g​ ​z​e​r​o​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​b​y​t​e​ ​s​o​ ​w​e​ ​c​a​n​ ​p​r​i​n​t​ ​i​t​ ​o​u​t​ ​w​i​t​h​ ​p​r​i​n​t​f​.​
 ​ ​ ​ ​ ​ ​ ​ ​c​h​a​r​ ​*​p​a​s​s​w​o​r​d​B​u​f​f​e​r​ ​=​ ​m​a​l​l​o​c​(​l​e​n​g​t​h​ ​+​ ​1​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​t​r​n​c​p​y​ ​(​p​a​s​s​w​o​r​d​B​u​f​f​e​r​,​ ​p​a​s​s​w​o​r​d​,​ ​l​e​n​g​t​h​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​p​a​s​s​w​o​r​d​B​u​f​f​e​r​[​l​e​n​g​t​h​]​ ​=​ ​'​\​0​'​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​P​a​s​s​w​o​r​d​ ​=​ ​%​s​\​n​"​,​ ​p​a​s​s​w​o​r​d​B​u​f​f​e​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​r​e​e​ ​(​p​a​s​s​w​o​r​d​B​u​f​f​e​r​)​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​s​h​o​w​L​i​s​t​ ​(​l​i​s​t​)​;​
 ​ ​ ​ ​S​e​c​K​e​y​c​h​a​i​n​I​t​e​m​F​r​e​e​C​o​n​t​e​n​t​ ​(​&​l​i​s​t​,​ ​p​a​s​s​w​o​r​d​)​;​

}​ ​/​/​ ​d​u​m​p​I​t​e​m​

When you call SecKeychainItemCopyContent(), if the keychain requires authentication, it will automatically bring up an authentication panel. If you only read publicly available data, no authentication panel will appear. The code above will probably trigger the panel for each item. If you use a different call that does not fetch the password, the panel will not appear at all.

 ​ ​ ​S​e​c​K​e​y​c​h​a​i​n​I​t​e​m​C​o​p​y​C​o​n​t​e​n​t​(​i​t​e​m​,​ ​N​U​L​L​,​ ​&​l​i​s​t​,​ ​N​U​L​L​,​ ​N​U​L​L​)​;​

The displayPassword argument to dumpItem() controls which call to use.

Add a call to dumpItem() in main():

 ​ ​ ​ ​w​h​i​l​e​ ​(​S​e​c​K​e​y​c​h​a​i​n​S​e​a​r​c​h​C​o​p​y​N​e​x​t​ ​(​s​e​a​r​c​h​,​ ​&​i​t​e​m​)​ ​=​=​ ​n​o​E​r​r​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​d​u​m​p​I​t​e​m​ ​(​i​t​e​m​,​ ​t​r​u​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​C​F​R​e​l​e​a​s​e​ ​(​i​t​e​m​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​+​+​;​
 ​ ​ ​ ​}​

This is what the final version looks like:

$​ ​.​/​d​u​m​p​e​m​ ​b​o​r​k​w​a​r​e​
P​a​s​s​w​o​r​d​ ​=​ ​a​b​4​d​0​b​0​3​
 ​ ​ ​ ​ ​ ​ ​ ​0​:​ ​'​a​c​c​t​'​ ​=​ ​"​b​o​r​k​w​a​r​e​"​
 ​ ​ ​ ​ ​ ​ ​ ​1​:​ ​'​d​e​s​c​'​ ​=​ ​"​"​
 ​ ​ ​ ​ ​ ​ ​ ​2​:​ ​'​m​d​a​t​'​ ​=​ ​"​2​0​1​0​0​6​2​3​2​0​2​9​0​7​Z​"​
P​a​s​s​w​o​r​d​ ​=​ ​d​s​c​h​-​4​a​c​g​s​
 ​ ​ ​ ​ ​ ​ ​ ​0​:​ ​'​a​c​c​t​'​ ​=​ ​"​b​o​r​k​w​a​r​e​"​
 ​ ​ ​ ​ ​ ​ ​ ​1​:​ ​'​d​e​s​c​'​ ​=​ ​"​W​e​b​ ​f​o​r​m​ ​p​a​s​s​w​o​r​d​"​
 ​ ​ ​ ​ ​ ​ ​ ​2​:​ ​'​m​d​a​t​'​ ​=​ ​"​2​0​1​0​0​6​2​3​2​1​0​7​2​5​Z​"​
P​a​s​s​w​o​r​d​ ​=​ ​g​r​u​m​b​l​e​c​a​k​e​
 ​ ​ ​ ​ ​ ​ ​ ​0​:​ ​'​a​c​c​t​'​ ​=​ ​"​b​o​r​k​w​a​r​e​"​
 ​ ​ ​ ​ ​ ​ ​ ​1​:​ ​'​d​e​s​c​'​ ​=​ ​"​W​e​b​ ​f​o​r​m​ ​p​a​s​s​w​o​r​d​"​
 ​ ​ ​ ​ ​ ​ ​ ​2​:​ ​'​m​d​a​t​'​ ​=​ ​"​2​0​1​0​0​7​1​3​1​7​3​2​0​3​Z​"​
3​ ​i​t​e​m​s​ ​f​o​u​n​d​

Build and run the program. Note that if you choose Always Allow, the program will have access to the keychain item until you log out. However, if you recompile the program, you will have to re-authenticate. The Security framework keeps checksums of the applications that have access.

Editing the Keychain

With the item, you can also make changes using an attribute list:

OSStatus SecKeychainItemModifyContent (SecKeychainItemRef  itemRef,
                           const SecKeychainAttributeList *attrList,
                                                   UInt32  newPasswordLength,
                                               const void *newPassword)

The new attribute values would go into the attrList. A new password would go into newPassword.

To delete an item:

OSStatus SecKeychainItemDelete (SecKeychainItemRef itemRef)

Note that you would still have to call CFRelease() on the item to prevent a memory leak.

Getting Specific Keychains

As an argument to many of the keychain functions, you can specify a particular keychain. Usually, you will simply use the user’s default keychain. For all of these functions, if you just supply NULL as the keychain, it will use the default keychain. However, if you want to explicitly get the default keychain you use:

OSStatus SecKeychainCopyDefault (SecKeychainRef *keychain)

If you wanted to specify a different keychain (remember that a keychain is just a file), you could use:

OSStatus SecKeychainOpen (const char *pathName,
                          SecKeychainRef *keychain)

When you are done with a keychain, make sure that you call CFRelease() to free it.

Keychain Access

Each keychain item defines how it may be accessed. The information about access privileges is kept in a SecAccessRef structure. To get the access structure for a particular keychain item, you use the following function:

OSStatus SecKeychainItemCopyAccess (SecKeychainItemRef  item,
                                    SecAccessRef *access);

To change the access on a keychain item, you edit the SecAccessRef and write it to the keychain item using:

OSStatus SecKeychainItemSetAccess (SecKeychainItemRef item,
                                   SecAccessRef access);

What, then, is a SecAccessRef? You can get a list of access control lists (ACLs) from it. Each access list has a list of trusted applications that are allowed to access the keychain item. Some applications can access the keychain item only after the user types in the keychain password.

Figure 23.5 shows the different types involved in keychain access. The single-headed arrows indicate a to-one relationship. A SecKeychainItem has a single SecAccess. The double-headed arrows indicate a to-many relationship. A SecKeychain can contain multiple SecKeychainItems.

Figure 23.5  Keychain data structures

As an example of reading the ACLs for a keychain item, add the following code to main() in dumpem.m:

 ​ ​ ​ ​w​h​i​l​e​ ​(​S​e​c​K​e​y​c​h​a​i​n​S​e​a​r​c​h​C​o​p​y​N​e​x​t​ ​(​s​e​a​r​c​h​,​ ​&​i​t​e​m​)​ ​!​=​ ​e​r​r​S​e​c​I​t​e​m​N​o​t​F​o​u​n​d​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​S​e​c​I​t​e​m​N​o​t​F​o​u​n​d​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​d​u​m​p​I​t​e​m​ ​(​i​t​e​m​,​ ​t​r​u​e​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​S​e​c​A​c​c​e​s​s​
 ​ ​ ​ ​ ​ ​ ​ ​S​e​c​A​c​c​e​s​s​R​e​f​ ​a​c​c​e​s​s​;​
 ​ ​ ​ ​ ​ ​ ​ ​S​e​c​K​e​y​c​h​a​i​n​I​t​e​m​C​o​p​y​A​c​c​e​s​s​ ​(​i​t​e​m​,​ ​&​a​c​c​e​s​s​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​h​o​w​A​c​c​e​s​s​ ​(​a​c​c​e​s​s​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​C​F​R​e​l​e​a​s​e​ ​(​a​c​c​e​s​s​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​C​F​R​e​l​e​a​s​e​ ​(​i​t​e​m​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​u​n​t​+​+​;​
 ​ ​ ​ ​}​

Add the following function:

v​o​i​d​ ​s​h​o​w​A​c​c​e​s​s​ ​(​S​e​c​A​c​c​e​s​s​R​e​f​ ​a​c​c​e​s​s​R​e​f​)​ ​{​
 ​ ​ ​ ​C​F​A​r​r​a​y​R​e​f​ ​a​c​l​L​i​s​t​;​
 ​ ​ ​ ​S​e​c​A​c​c​e​s​s​C​o​p​y​A​C​L​L​i​s​t​(​a​c​c​e​s​s​R​e​f​,​ ​&​a​c​l​L​i​s​t​)​;​

 ​ ​ ​ ​i​n​t​ ​c​o​u​n​t​ ​=​ ​C​F​A​r​r​a​y​G​e​t​C​o​u​n​t​(​a​c​l​L​i​s​t​)​;​
 ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​%​d​ ​l​i​s​t​s​\​n​"​,​ ​c​o​u​n​t​)​;​

 ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​c​o​u​n​t​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​S​e​c​A​C​L​R​e​f​ ​a​c​l​ ​=​ ​(​S​e​c​A​C​L​R​e​f​)​ ​C​F​A​r​r​a​y​G​e​t​V​a​l​u​e​A​t​I​n​d​e​x​(​a​c​l​L​i​s​t​,​ ​i​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​C​F​A​r​r​a​y​R​e​f​ ​a​p​p​l​i​c​a​t​i​o​n​L​i​s​t​;​
 ​ ​ ​ ​ ​ ​ ​ ​C​F​S​t​r​i​n​g​R​e​f​ ​d​e​s​c​r​i​p​t​i​o​n​;​
 ​ ​ ​ ​ ​ ​ ​ ​C​S​S​M​_​A​C​L​_​K​E​Y​C​H​A​I​N​_​P​R​O​M​P​T​_​S​E​L​E​C​T​O​R​ ​p​r​o​m​p​t​S​e​l​e​c​t​o​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​S​e​c​A​C​L​C​o​p​y​S​i​m​p​l​e​C​o​n​t​e​n​t​s​ ​(​a​c​l​,​ ​&​a​p​p​l​i​c​a​t​i​o​n​L​i​s​t​,​ ​&​d​e​s​c​r​i​p​t​i​o​n​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​&​p​r​o​m​p​t​S​e​l​e​c​t​o​r​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​p​r​o​m​p​t​S​e​l​e​c​t​o​r​.​f​l​a​g​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​&​ ​C​S​S​M​_​A​C​L​_​K​E​Y​C​H​A​I​N​_​P​R​O​M​P​T​_​R​E​Q​U​I​R​E​_​P​A​S​S​P​H​R​A​S​E​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​t​%​d​:​ ​A​C​L​ ​%​s​ ​-​ ​R​e​q​u​i​r​e​s​ ​p​a​s​s​p​h​r​a​s​e​\​n​"​,​ ​i​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​(​N​S​S​t​r​i​n​g​ ​*​)​d​e​s​c​r​i​p​t​i​o​n​ ​U​T​F​8​S​t​r​i​n​g​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​t​%​d​:​ ​A​C​L​ ​%​s​ ​-​ ​D​o​e​s​ ​n​o​t​ ​r​e​q​u​i​r​e​ ​p​a​s​s​p​h​r​a​s​e​\​n​"​,​ ​i​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​(​N​S​S​t​r​i​n​g​ ​*​)​d​e​s​c​r​i​p​t​i​o​n​ ​U​T​F​8​S​t​r​i​n​g​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​C​F​R​e​l​e​a​s​e​(​d​e​s​c​r​i​p​t​i​o​n​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​a​p​p​l​i​c​a​t​i​o​n​L​i​s​t​ ​=​=​ ​N​U​L​L​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​t​\​t​N​o​ ​a​p​p​l​i​c​a​t​i​o​n​ ​l​i​s​t​ ​%​d​\​n​"​,​ ​i​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​t​i​n​u​e​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​i​n​t​ ​a​p​p​C​o​u​n​t​ ​=​ ​C​F​A​r​r​a​y​G​e​t​C​o​u​n​t​(​a​p​p​l​i​c​a​t​i​o​n​L​i​s​t​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​t​\​t​%​d​ ​a​p​p​l​i​c​a​t​i​o​n​s​ ​i​n​ ​l​i​s​t​ ​%​d​\​n​"​,​ ​a​p​p​C​o​u​n​t​,​ ​i​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​j​ ​=​ ​0​;​ ​j​ ​<​ ​a​p​p​C​o​u​n​t​;​ ​j​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​e​c​T​r​u​s​t​e​d​A​p​p​l​i​c​a​t​i​o​n​R​e​f​ ​a​p​p​l​i​c​a​t​i​o​n​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​D​a​t​a​R​e​f​ ​a​p​p​D​a​t​a​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​p​p​l​i​c​a​t​i​o​n​ ​=​ ​(​S​e​c​T​r​u​s​t​e​d​A​p​p​l​i​c​a​t​i​o​n​R​e​f​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​A​r​r​a​y​G​e​t​V​a​l​u​e​A​t​I​n​d​e​x​(​a​p​p​l​i​c​a​t​i​o​n​L​i​s​t​,​ ​j​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​e​c​T​r​u​s​t​e​d​A​p​p​l​i​c​a​t​i​o​n​C​o​p​y​D​a​t​a​(​a​p​p​l​i​c​a​t​i​o​n​,​ ​&​a​p​p​D​a​t​a​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​\​t​\​t​\​t​%​s​\​n​"​,​ ​C​F​D​a​t​a​G​e​t​B​y​t​e​P​t​r​(​a​p​p​D​a​t​a​)​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​C​F​R​e​l​e​a​s​e​(​a​p​p​D​a​t​a​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​C​F​R​e​l​e​a​s​e​(​a​p​p​l​i​c​a​t​i​o​n​L​i​s​t​)​;​
 ​ ​ ​ ​}​
}​ ​/​/​ ​s​h​o​w​A​c​c​e​s​s​

And here is the access info for one of the keychain items:

$​ ​.​/​d​u​m​p​e​m​ ​b​o​r​k​w​a​r​e​
.​.​.​
P​a​s​s​w​o​r​d​ ​=​ ​d​s​c​h​-​4​a​c​g​s​
 ​ ​ ​ ​0​:​ ​'​a​c​c​t​'​ ​=​ ​"​b​o​r​k​w​a​r​e​"​
 ​ ​ ​ ​1​:​ ​'​d​e​s​c​'​ ​=​ ​"​W​e​b​ ​f​o​r​m​ ​p​a​s​s​w​o​r​d​"​
 ​ ​ ​ ​2​:​ ​'​m​d​a​t​'​ ​=​ ​"​2​0​1​0​0​7​1​3​1​7​3​2​0​3​Z​"​
3​ ​l​i​s​t​s​
 ​ ​ ​ ​0​:​ ​A​C​L​ ​t​w​i​t​t​e​r​.​c​o​m​ ​(​b​o​r​k​w​a​r​e​)​ ​-​ ​D​o​e​s​ ​n​o​t​ ​r​e​q​u​i​r​e​ ​p​a​s​s​p​h​r​a​s​e​
 ​ ​ ​ ​ ​ ​ ​ ​N​o​ ​a​p​p​l​i​c​a​t​i​o​n​ ​l​i​s​t​ ​0​
 ​ ​ ​ ​1​:​ ​A​C​L​ ​t​w​i​t​t​e​r​.​c​o​m​ ​(​b​o​r​k​w​a​r​e​)​ ​-​ ​D​o​e​s​ ​n​o​t​ ​r​e​q​u​i​r​e​ ​p​a​s​s​p​h​r​a​s​e​
 ​ ​ ​ ​ ​ ​ ​ ​3​ ​a​p​p​l​i​c​a​t​i​o​n​s​ ​i​n​ ​l​i​s​t​ ​1​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​U​s​e​r​s​/​m​a​r​k​d​/​W​r​i​t​i​n​g​/​c​o​r​e​-​o​s​x​/​k​e​y​c​h​a​i​n​-​c​h​a​p​/​P​r​o​j​e​c​t​s​/​d​u​m​p​e​m​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​L​o​c​a​l​/​A​p​p​s​/​G​o​o​g​l​e​ ​C​h​r​o​m​e​.​a​p​p​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​A​p​p​l​i​c​a​t​i​o​n​s​/​S​a​f​a​r​i​.​a​p​p​
 ​ ​ ​ ​2​:​ ​A​C​L​ ​t​w​i​t​t​e​r​.​c​o​m​ ​(​b​o​r​k​w​a​r​e​)​ ​-​ ​D​o​e​s​ ​n​o​t​ ​r​e​q​u​i​r​e​ ​p​a​s​s​p​h​r​a​s​e​
 ​ ​ ​ ​ ​ ​ ​ ​0​ ​a​p​p​l​i​c​a​t​i​o​n​s​ ​i​n​ ​l​i​s​t​ ​2​
.​.​.​

Here you can see this is a web form, and Safari, Chrome, and dumpem have been given access to it.

The keychain access API is not 100% complete. You can do everything an application needs to do on a day-to-day basis. The keychain API is implemented using the CSSM, Common Security Services Manager, which is the programming interface to the CDSA, the Common Data Security Architecture. Here we needed to dip into the CSSM to see if the keychain item requires a prompt. When this bit is set, the system will ask for the user’s password before trusting the application with the keychain item.

Making a New Keychain Item

To create a new keychain item, you use this function:

OSStatus SecKeychainItemCreateFromContent (SecItemClass  itemClass,
                               SecKeychainAttributeList *attrList,
                                                 UInt32  passwdLength,
                                             const void *password,
                                         SecKeychainRef  keychainRef,
                                           SecAccessRef  initialAccess,
                                     SecKeychainItemRef *itemRef);

where the itemClass is kSecInternetPasswordItemClass, kSecGenericPasswordItemClass, kSecAppleSharePasswordItemClass, or kSecCertificateItemClass. If you supply NULL as the keychainRef, the item will be added to the default keychain. itemRef will be set to point to the newly created item. To use the default access, just supply NULL as the initialAccess.

As an example, here is a short program that will insert a new item into your default keychain.

Example 23.2. add_item.m

/​/​ ​a​d​d​i​t​e​m​.​m​ ​-​-​ ​a​d​d​ ​a​ ​n​e​w​ ​i​t​e​m​ ​t​o​ ​t​h​e​ ​k​e​y​c​h​a​i​n​

/​/​ ​g​c​c​ ​-​g​ ​-​W​a​l​l​ ​-​f​r​a​m​e​w​o​r​k​ ​S​e​c​u​r​i​t​y​ ​-​o​ ​a​d​d​i​t​e​m​ ​a​d​d​i​t​e​m​.​m​

#​i​m​p​o​r​t​ ​<​S​e​c​u​r​i​t​y​/​S​e​c​u​r​i​t​y​.​h​>​

#​i​n​c​l​u​d​e​ ​<​s​t​d​i​o​.​h​>​ ​/​/​ ​f​o​r​ ​p​r​i​n​t​f​(​)​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​h​a​r​ ​*​a​r​g​v​[​]​)​ ​{​
 ​ ​ ​ ​S​e​c​K​e​y​c​h​a​i​n​A​t​t​r​i​b​u​t​e​ ​a​t​t​r​i​b​u​t​e​s​[​2​]​;​
 ​ ​ ​ ​a​t​t​r​i​b​u​t​e​s​[​0​]​.​t​a​g​ ​=​ ​k​S​e​c​A​c​c​o​u​n​t​I​t​e​m​A​t​t​r​;​
 ​ ​ ​ ​a​t​t​r​i​b​u​t​e​s​[​0​]​.​d​a​t​a​ ​=​ ​"​f​o​o​z​"​;​
 ​ ​ ​ ​a​t​t​r​i​b​u​t​e​s​[​0​]​.​l​e​n​g​t​h​ ​=​ ​4​;​

 ​ ​ ​ ​a​t​t​r​i​b​u​t​e​s​[​1​]​.​t​a​g​ ​=​ ​k​S​e​c​D​e​s​c​r​i​p​t​i​o​n​I​t​e​m​A​t​t​r​;​
 ​ ​ ​ ​a​t​t​r​i​b​u​t​e​s​[​1​]​.​d​a​t​a​ ​=​ ​"​I​ ​s​e​e​m​ ​t​o​ ​b​e​ ​a​ ​v​e​r​b​"​;​
 ​ ​ ​ ​a​t​t​r​i​b​u​t​e​s​[​1​]​.​l​e​n​g​t​h​ ​=​ ​1​9​;​

 ​ ​ ​ ​S​e​c​K​e​y​c​h​a​i​n​A​t​t​r​i​b​u​t​e​L​i​s​t​ ​l​i​s​t​;​
 ​ ​ ​ ​l​i​s​t​.​c​o​u​n​t​ ​=​ ​2​;​
 ​ ​ ​ ​l​i​s​t​.​a​t​t​r​ ​=​ ​a​t​t​r​i​b​u​t​e​s​;​

 ​ ​ ​ ​S​e​c​K​e​y​c​h​a​i​n​I​t​e​m​R​e​f​ ​i​t​e​m​;​
 ​ ​ ​ ​O​S​S​t​a​t​u​s​ ​s​t​a​t​u​s​ ​=​ ​S​e​c​K​e​y​c​h​a​i​n​I​t​e​m​C​r​e​a​t​e​F​r​o​m​C​o​n​t​e​n​t​
 ​ ​ ​ ​ ​ ​ ​ ​(​k​S​e​c​G​e​n​e​r​i​c​P​a​s​s​w​o​r​d​I​t​e​m​C​l​a​s​s​,​ ​&​l​i​s​t​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​5​,​ ​"​b​u​d​d​a​"​,​ ​N​U​L​L​,​ ​N​U​L​L​,​ ​&​i​t​e​m​)​;​

 ​ ​ ​ ​i​f​ ​(​s​t​a​t​u​s​ ​!​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​p​r​i​n​t​f​ ​(​"​E​r​r​o​r​ ​c​r​e​a​t​i​n​g​ ​n​e​w​ ​i​t​e​m​:​ ​%​d​\​n​"​,​ ​(​i​n​t​)​s​t​a​t​u​s​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​E​X​I​T​_​S​U​C​C​E​S​S​;​

}​ ​/​/​ ​m​a​i​n​

After running this program, look at the keychain item in Keychain Access, as shown in Figure 23.6

Figure 23.6  Looking at the new keychain item

Also, note what “default access” is, as shown in Figure 23.7.

Figure 23.7  Keychain item default access

Convenience Functions

That’s the whole story on keychains. There are some convenience functions that make common activities possible, but they simply use the functions that we have talked about already.

These functions allow you to create a new item without creating an attribute list:

OSStatus SecKeychainAddInternetPassword (SecKeychainRef keychain,
                            UInt32       serverNameLength,
                            const char  *serverName,
                            UInt32       securityDomainLength,
                            const char  *securityDomain,
                            UInt32       accountNameLength,
                            const char  *accountName,
                            UInt32       pathLength,
                            const char  *path,
                            UInt16       port,
                            SecProtocolType       protocol,
                            SecAuthenticationType authenticationType,
                            UInt32       passwordLength,
                            const void  *passwordData,
                            SecKeychainItemRef    *itemRef)

OSStatus SecKeychainAddGenericPassword (SecKeychainRef keychain,
                           UInt32       serviceNameLength,
                           const char  *serviceName,
                           UInt32       accountNameLength,
                           const char  *accountName,
                           UInt32       passwordLength,
                           const void  *passwordData,
                           SecKeychainItemRef *itemRef)

Note that neither of these can be used to change a password in an existing item. If you try this, the function will complain that the item already exists.

These methods allow you to find items without creating an attribute list:

OSStatus SecKeychainFindInternetPassword (CFTypeRef keychainOrArray,
                           UInt32       serverNameLength,
                           const char  *serverName,
                           UInt32       securityDomainLength,
                           const char  *securityDomain,
                           UInt32       accountNameLength,
                           const char  *accountName,
                           UInt32       pathLength,
                           const char  *path,
                           UInt16       port,
                           SecProtocolType protocol,
                           SecAuthenticationType authenticationType,
                           UInt32      *passwordLength,
                           void       **passwordData,
                           SecKeychainItemRef *itemRef)

OSStatus SecKeychainFindGenericPassword (CFTypeRef keychainOrArray,
                           UInt32       serviceNameLength,
                           const char  *serviceName,
                           UInt32       accountNameLength,
                           const char  *accountName,
                           UInt32      *passwordLength,
                           void       **passwordData,
                           SecKeychainItemRef *itemRef)

Notice that the protocol and authentication types are not strings. Here are the constants for the commonly used protocols:

kSecProtocolTypeFTP

kSecProtocolTypeFTPAccount

kSecProtocolTypeHTTP

kSecProtocolTypeIRC

kSecProtocolTypeNNTP

kSecProtocolTypePOP3

kSecProtocolTypeSMTP

kSecProtocolTypeSOCKS

kSecProtocolTypeIMAP

kSecProtocolTypeLDAP

kSecProtocolTypeAppleTalk

kSecProtocolTypeAFP

kSecProtocolTypeTelnet

kSecProtocolTypeSSH

There are several types of authentication, but you will almost certainly use kSecAuthenticationTypeDefault.

If you are writing a daemon or something else that should not be interrupting the user, you can prevent the authentication panel from appearing:

S​e​c​K​e​y​c​h​a​i​n​S​e​t​U​s​e​r​I​n​t​e​r​a​c​t​i​o​n​A​l​l​o​w​e​d​ ​(​N​O​)​;​

Code Signing

You probably noticed that the keychain redisplays its authorization dialog when you run a newer version of dumpem was run. This can be annoying for the user every time they upgrade your software.

Starting in Mac OS X 10.5, Apple introduced code signing, which is a way to digitally sign application code and resources. When the user OKs a keychain authorization dialog, the system sees if the application was signed. If it is, this fact is noted. If a newer version of the application accesses the keychain, the system sees if the app is signed by the same entity that signed the prior version. If so, the system knows that both applications came from the same place and allows the keychain access to happen without interruption. Keychain is not the only system service that uses code signing to identify applications. The system firewall does, as well as parental controls.

Signing is a way to verify the integrity of something, like an application. A digital certificate, along with a privately generated key, is used to sign the code. The certificate contains a public key associated with the private key and is included in the signed application, along with a hash of the application’s contents. The system looks at the certificate, calculates the application’s hash, and uses the public key to verify that this set of bits matches the set of bits created by the application’s author. Your application’s identity is safe so long as your private key is kept secret.

Certificates can also include a chain of trust, a set of identities of Certificate Authorities like VeriSign. This chain of trust can be verified by the system. If your certificate is trusted by another entity, which is trusted by yet another entity, the system can use that to determine if it should trust the identity of the signer.

You can generate a self-signed certificate using the Keychain Access utility, and it can be used to sign your applications. So long as the private key is kept private, an attacker cannot masquerade a malicious application as the one that you have signed. Self-signed certificates cannot be used to verify the identity of the signer. All the system knows is BigShow 2.0 was signed by the same certificate as BigShow 1.0. The system firewall does not trust self-signed certificates, so if your application requires holes in the firewall, the user will have to re-authenticate on each app upgrade.

If you purchase a signing certificate from a signing authority, the identity of the owner of the certificate can be trusted, again as long as the private key is kept secret. If the firewall can verify the identity of the application’s signature, then it will behave like Keychain Access and not disturb the user when the software is upgraded.

In MacOS, code signing is optional, although in the future Apple may require it to access OS services. Everything is code signed in iOS. To run anything on a device, you need to purchase a signing certificate from Apple.

To generate a self-signed certificate, use the certificate assistant in Keychain Access to create a new self-signed keychain, as shown in Figure 23.8.

Figure 23.8  Creating a self-signed certificate

Once the certificate has been created, Keychain Access displays detailed information. Figure 23.9 shows that the certificate is untrusted.

Figure 23.9  The self-signed certificate is untrusted.

Now that you have a certificate, you can sign your program using the codesign tool, giving it the name of the certificate:

$​ ​c​o​d​e​s​i​g​n​ ​-​s​ ​"​C​o​d​e​S​i​g​n​i​n​g​"​ ​d​u​m​p​e​m​

codesign will look in your keychain for the named certificate. It will look for substrings if it cannot find a complete match. You can use codesign to verify your program was codesigned:

$​ ​c​o​d​e​s​i​g​n​ ​-​d​ ​d​u​m​p​e​m​
E​x​e​c​u​t​a​b​l​e​=​/​U​s​e​r​s​/​m​a​r​k​d​/​W​r​i​t​i​n​g​/​c​o​r​e​-​o​s​x​/​k​e​y​c​h​a​i​n​-​c​h​a​p​/​P​r​o​j​e​c​t​s​/​d​u​m​p​e​m​

You can ask for more verbose output, too:

%​ ​c​o​d​e​s​i​g​n​ ​-​d​v​ ​d​u​m​p​e​m​
E​x​e​c​u​t​a​b​l​e​=​/​U​s​e​r​s​/​m​a​r​k​d​/​W​r​i​t​i​n​g​/​c​o​r​e​-​o​s​x​/​k​e​y​c​h​a​i​n​-​c​h​a​p​/​P​r​o​j​e​c​t​s​/​d​u​m​p​e​m​
I​d​e​n​t​i​f​i​e​r​=​d​u​m​p​e​m​
F​o​r​m​a​t​=​M​a​c​h​-​O​ ​t​h​i​n​ ​(​x​8​6​_​6​4​)​
C​o​d​e​D​i​r​e​c​t​o​r​y​ ​v​=​2​0​1​0​0​ ​s​i​z​e​=​1​7​5​ ​f​l​a​g​s​=​0​x​0​(​n​o​n​e​)​ ​h​a​s​h​e​s​=​4​+​2​ ​l​o​c​a​t​i​o​n​=​e​m​b​e​d​d​e​d​
S​i​g​n​a​t​u​r​e​ ​s​i​z​e​=​1​3​4​6​
S​i​g​n​e​d​ ​T​i​m​e​=​A​u​g​ ​1​0​,​ ​2​0​1​0​ ​1​2​:​4​6​:​3​9​ ​P​M​
I​n​f​o​.​p​l​i​s​t​=​n​o​t​ ​b​o​u​n​d​
S​e​a​l​e​d​ ​R​e​s​o​u​r​c​e​s​=​n​o​n​e​
I​n​t​e​r​n​a​l​ ​r​e​q​u​i​r​e​m​e​n​t​s​ ​c​o​u​n​t​=​1​ ​s​i​z​e​=​1​0​4​

Freshly compiled programs are not signed. If you rebuild dumpem and try to verify it, codesign will tell you it is unsigned:

$​ ​c​o​d​e​s​i​g​n​ ​-​d​v​ ​d​u​m​p​e​m​
d​u​m​p​e​m​:​ ​c​o​d​e​ ​o​b​j​e​c​t​ ​i​s​ ​n​o​t​ ​s​i​g​n​e​d​

You can also sign application bundles:

$​ ​c​o​d​e​s​i​g​n​ ​-​s​ ​"​C​o​d​e​S​i​g​n​i​n​g​"​ ​B​i​g​S​h​o​w​.​a​p​p​

This signs the contents of the bundle, such as resources, the Info.plist, and executable code. If any part of the bundle is modified, the signing will become invalid. You cannot sign text files, such as shell scripts individually, but you can include them as part of a signed bundle. Be sure to sign as the last stage of your build. You will invalidate your signing if you strip your application after the signing phase.

Xcode will sign your programs as part of its regular build process if you put the name of the signing certificate into the Code Signing Identity build setting.

You will want to use different signing certificates for development and release builds of your application. A signed application, especially one signed with a trusted certificate, says to end-users that this application is a tested representative of this company or individual and should be trusted with your keychain or firewall.

Exercises

Write an app that saves a username and password to the default keychain.

Write an app that can read and change the saved password.

Extend chatterd to require a password. Add password solicitation to a Chatter client, and store the password into the keychain

Code-Sign your Chatter client so that upgrades do not trigger the keychain dialog.

Index

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

Symbols

! (bitwise operator), Bitwise operators
##__VA_ARGS__, Variadic macros
#define, Preprocessor symbols, Handy Flags

contraindications, Preprocessor symbols

#else, Conditional compilation
#endif, Conditional compilation
#if, Conditional compilation
#ifdef, Conditional compilation
#ifndef, Conditional compilation
#import, File inclusion
#include, File inclusion
#pragma, Pragmas
#pragma once, File inclusion
%zu format specifier, Varargs gotchas
& (bitwise operator), Bitwise operators
*_FILENO constants, Opening a file
-- (argument terminator), getopt_long()
-arch compiler flag, Fat binaries from the command line
-D compiler flag, Preprocessor symbols
-DNDEBUG, For the More Curious: Assertions
-F linker flag, Frameworks
-fast compiler flag, The Optimizer
-fastcp compiler flag, The Optimizer
-fastf compiler flag, The Optimizer
-flat_namespace, Shared Libraries
-fobjc-exceptions, Objective-C @synchronized blocks
-fobjc-gc, How to use it
-fobjc-gc-only, How to use it
-O compiler flag, Compiler Optimization
-Wconversion compiler flag, 64-bit cleanliness
-Wformat, Variable argument lists
-Wno-protocol compiler flag, Even More Compiler Flags
-Wno-unused, Warnings
-Wundeclared-selector compiler flag, Even More Compiler Flags
. (dot operator), Properties
.DS_Store, .DS_Store
.fseventsd, History
.gdbinit, Handy Tricks
/dev/fsevents, fsevents
64-bit computing

description, 64-Bit Computing

interoperability limitations, The 64-bit programming model

and Objective-C runtime, Runtime enhancements and garbage collection, New Objective-C runtime, 64-bit Objective-C runtime

programming model, The 64-bit programming model

: (return value), getopt_long()
<< (bitwise operator), Bitwise operators
>> (bitwise operator), Bitwise operators
? (return value), getopt_long()
@catch, Native exception handling
@finally, Native exception handling
@optional directive, Protocols
@required directive, Protocols
@synchronized, Objective-C @synchronized blocks
@throw, Native exception handling
@try, Native exception handling
^ (bitwise operator), Bitwise operators
_exit(), fork
_longjmp(), Reentrancy
_setjmp(), Reentrancy
__APPLE_CC__, Predefined symbols
__APPLE__, Predefined symbols
__attribute((packed)), GCC Extensions
__attribute__((format_arg)), Variable argument lists
__attribute__((sentinel)), Variable argument lists
__BIG_ENDIAN__, Predefined symbols
__block, Changing Enclosing Scope, __block variables
__cplusplus, Predefined symbols
__DATE__, Predefined symbols
__FILE__, Predefined symbols
__FUNCTION__, Predefined symbols
__func__, Predefined symbols
__GNUC__, Testing the compiler version
__i386__, Fat binaries from the command line
__LINE__, Predefined symbols
__LITTLE_ENDIAN__, Predefined symbols
__LP64__, Predefined symbols, Fat binaries from the command line
__MACH__, Predefined symbols
__OBJC__, Predefined symbols
__ppc64__, Fat binaries from the command line
__ppc__, Fat binaries from the command line
__PRETTY_FUNCTION__, Predefined symbols
__strong, Strong and weak references
__TIME__, Predefined symbols
__VA_ARGS__, Variadic macros
__x86_64__, Fat binaries from the command line
| (bitwise operator), Bitwise operators
| (pipeline character), Command-Line Programs
~ expansion, NSString path utilities

A

a.out, The Basic Program
abandoned memory, More Memory: Heapshots
abandoned objects, More Memory: Heapshots
abort(), For the More Curious: Assertions
accept(), accept, Multithreading, Condition variables
access control lists, Access control lists
actions (DTrace), Actions
Activity Monitor

described, Activity Monitor

finding high CPU usage with, CPU

finding leaks with, Memory leaks

in Instruments, System Details

sampling processes in, sample

address translation, Virtual Memory
addresses

64-bit, 64-Bit Computing

binding network, For the More Curious: Datagrams

network, Addresses, Address Conversions, Constructing an address

add_item.m, Making a New Keychain Item
AF_INET, IPv4 address structures, Server coding
AF_INET6, IPv6 address structures
aggregates (DTrace), Aggregates
aliases, Memory is the New I/O, Mac OS aliases, Bookmarks
alloca(), alloca()
allocating collectable memory, Non Objective-C objects
allocation history, Looking at memory leaks
Allocations instrument, Looking at memory leaks
Altivec, malloc()
ambush script, Pragmas
AND (bitwise operator), Bitwise operators
append-only mode, Opening a file
append.m, Opening a file
__APPLE__, Predefined symbols
Apple System Log (ASL), ASL
AppleShare passwords, Items and Attribute Lists, Searching for Items
Application Support directory, Finding Standard Directories
ar command, Static Libraries
-arch compiler flag, Fat binaries from the command line
arg0, Built-in variables
argc, getopt_long()
argparse.m, getopt_long()
args[], Built-in variables
arguments

command-line, Parsing the Command Line

variable lists of, Variable argument lists

argv, Changing Behavior By Name, getopt_long()
arrays

associative, Arrays

and blocks, Block Syntax

in D language, Arrays, C arrays

displaying in gdb, Displaying Data

in Dtrace, Arrays

iterating through, Fast enumeration, Iteration

and locality of reference, Locality of Reference

of option structures, getopt_long()

variable length automatic, GCC Extensions

ASL (Apple System Log), ASL
ASL messages, ASL messages
ASL queries, ASL queries
asl-list.m, ASL queries
asl-log-n-query.m, ASL queries
aslmsg, ASL messages
aslresponse, ASL queries
asl_get(), ASL messages
asl_key(), ASL messages
ASL_KEY_* constants, ASL messages
asl_log(), ASL messages, ASL queries
asl_new(), ASL messages
ASL_QUERY_OP_* constants, ASL queries
asl_search(), ASL queries
asl_send(), ASL messages
asl_set(), ASL messages
asl_set_query(), ASL queries
asl_vlog(), ASL messages
asprintf(), Formatted I/O
assembly code, Seeing the Generated Assembly Code
assert(), For the More Curious: Assertions
assertions, For the More Curious: Assertions
assign (property attribute), Properties
atomic (property attribute), Properties
atomic operations, Atomic operations, Other random calls
atos command, Debugging, For the More Curious: Stripping
attribute lists (keychain), Items and Attribute Lists
__attribute((packed)), GCC Extensions
attributes

extended, Extended attributes

file, getattrlist(), Extended attributes

property, Properties

__attribute__((format_arg)), Variable argument lists
__attribute__((sentinel)), Variable argument lists
attrlist (struct), Describing the attributes
attrreference_t (type), Parsing the buffer
ATTR_* constants, Describing the attributes
authentication, Reading Data From an Item, Convenience Functions
Automation instrument, Automation
autovectorization, Vectorization
AUTO_LOG_COLLECTIONS, Debugging
avg(), Aggregates
avgquant.d, Aggregates
avgread-1.d, Aggregates
avgread-2.d, Aggregates

B

background reads, Non-blocking reads
badgers, Return Values
base conversions, Displaying Data
basics.m (threads), Creating threads
battery state, Exercises
beer.d, Variables
BEGIN provider, BEGIN and END providers
begin-end.d, BEGIN and END providers
Bell Labs, Mac OS X: Built to Evolve
Berkeley Computer Systems Research Group, Mac OS X: Built to Evolve
Berkeley Sockets, Network Programming With Sockets
big endian, Predefined symbols, Network Byte Order
Big Wheel, File permissions
Bill the Cat, Debugging Techniques
binaries, universal, Fat binaries from the command line
binary files, Opening files
binary I/O, Binary I/O
bind(), bind
binding

external functions, dlopen()

network addresses, bind, For the More Curious: Datagrams

bitflags, 64-bit, 64-bit cleanliness
Bitmap, Bitmap
Bitmap.h, Bitmap
Bitmap.m, Bitmap
BitmapView.h, BitmapView
BitmapView.m, BitmapView
bitmask.m, Setting and clearing bits
bitmasks, Setting and clearing bits, 64-bit cleanliness
bitwise operations, Bitwise operations
bitwise operators, Bitwise operators

64-bit bitflags, 64-bit cleanliness

__block, Changing Enclosing Scope, __block variables
blocking

avoiding, Multiplexing connections, Non-blocking reads

I/O, Blocking I/O

signals, Blocking signals

blocks, Blocks

arguments to, Block Syntax

array enumeration, New API Using Blocks

in collections, Blocks in Collections

compiler’s implementation of, Implementation

copying, Blocks as Objects

debugging, Debugging

dictionary enumeration, New API Using Blocks

dump-block-byref debugger command, Dumping runtime information

dump-block-literal debugger command, Dumping runtime information

implementation, Implementation

invoke-block debugger command, Debugging

invoking, Block Variables

new functions/methods for, New API Using Blocks

object capture, Changing Enclosing Scope

Objective-C classes, Block literals

as objects, Blocks as Objects

predicates, New API Using Blocks

releasing, Blocks as Objects

and retain cycles, Block Retain Cycles

return values in, Return Values

runtime information, Dumping runtime information

scope of, Accessing Enclosing Scope

syntax for, Block Syntax

variable capture, Accessing Enclosing Scope, Variable Capture Redux

as variables, Block Variables

Block_copy(), Blocks as Objects
Block_release(), Blocks as Objects
blorf, For the More Curious: Memory-Mapped Files
Bonjour, Bonjour

browsing services, Browsing Net Services

overview, Bonjour

publishing services, Publishing an NSNetService

resolving net services with, Make ChatterClient browse for servers

service names, Publishing an NSNetService

SRV record, Bonjour

TXT records, For the More Curious: TXT Records

bookmarks, Bookmarks
breakpoints, A sample GDB session, Breakpoints
BSD, Mac OS X: Built to Evolve, Static Libraries, File permissions
bsearch_b(), New API Using Blocks
bss segment, Program Memory Model
buffer overruns, Memory corruption
buffered I/O, Buffered I/O

after fork(), fork

buffers

memory management of, Memory Ownership Issues, Objective-C Garbage Collection

on call stack, Program Memory Model

overrunning, Memory corruption

buffread.m, Text I/O
bugs vs. errors, Debugging Techniques
building frameworks, Frameworks
built-in variables, Built-in variables
Bumgarner, Bill, Block Syntax
BundlePrinter, Bundles in Cocoa
bundleprinter command, BundlePrinter
bundleprinter.m, BundlePrinter
bundles

directory behavior, Differences between HFS+ and UFS

in Cocoa, Bundles in Cocoa

loading caveats, Bundles in Cocoa

vs. shared libraries, BundlePrinter

BusyBox, Changing Behavior By Name
byte order, Network Byte Order
byte swapping, Network Byte Order

C

C language, The Compiler pipeline

(see also preprocessor)

dialects, The Compiler, Handy Flags

C++

exception model, 64-bit Objective-C runtime

name mangling, Name Mangling

C99, Variadic macros
cache line, Locality of Reference
caches, Caches
CalcOperation.m, CalcOperation
call stack, Variable argument lists, Program Memory Model
call trees, Time Profiler
callbacks, Objective-C

in FSEvents, The FSEvents callback

calloc(), calloc()
cancellation, Simple-Lifetime Operations
Carbon

for aliases, API for links

File operation tracing, fs_usage and sc_usage

metadata, stat()

@catch, Native exception handling
catch.m, Handling a signal
catchblock.m, Blocking signals
caveman debugging, Logging, Debugger techniques
CDSA, Keychain Access
CFAbsoluteTime, Miscellaneous calls
CFChatterClientAppDelegate.h, GUI Chatter Client
CFChatterClientAppDelegate.m, GUI Chatter Client
CFHost, CFHost
CFMakeCollectable(), Non Objective-C objects, External reference counts, CFSocket
CFRetain(), External reference counts
CFRunLoop, CFRunLoop
CFRunLoopAddSource(), CFSocket
CFRunLoopObserverCreate(), For the More Curious: Run Loop Observers
CFRunLoopSource, CFSocket
CFSocket, CFSocket, kqueues and Runloops
CFSocketCallBack (type), CFSocket
CFSocketContext (type), CFSocket
CFSocketCreateRunLoopSource(), CFSocket
CFSocketCreateWithNative(), CFSocket
CFSwapInt, Network Byte Order
CFUUID, Miscellaneous calls
CFUUIDCreateFromString(), Miscellaneous calls
CFUUIDCreateString(), Miscellaneous calls
CFXML, C callbacks in Objective-C
CGFLoat, 64-bit cleanliness
chatterclient.m, Message boundaries

runloop-based, GUI Chatter Client

chatterserver.m, Message boundaries
chdir(), Current working directory
cheapls.m, The opendir() Family
chflags command, Other random programs
chgrp command, Users and groups
child processes, fork
chmod command, File permissions, Access control lists
chmod(), File permissions
choose C dialect, Handy Flags
chown command, Users and groups
chown(), Users and groups
circular dependencies (in operations), Simple-Lifetime Operations
clang, Predefined symbols, The Compiler, Implementation
class extensions, Class extensions

adopting protocols in, MandelOpperAppDelegate

class_addMethod(), Exercises
class_getInstanceSize(), New Objective-C runtime
clause-scoped Dtrace variables, Scoped variables
clear(), Aggregate-related functions
clearerr(), Text I/O
client-server (see networking)
clock frequency, The End of Free Performance
close on exec, Other random calls
close(), Closing files
closures, Changing Enclosing Scope
Cocoa

bundles, Bundles in Cocoa

drawing in threads, NSThread

exception handling, Exception Handling in Cocoa

and file system, NSFileManager - Cocoa and the File System

memory leaks in, Memory leaks in Cocoa

multiprocessing gotcha, fork() Gotchas

multithreading in, Cocoa and Threading

new collection classes, The "new" collection classes

plug-ins, Bundles in Cocoa

thread safety, Cocoa and thread safety

CocoaDirWatcherAppDelegate.h, kqueues and Runloops
CocoaDirWatcherAppDelegate.m, kqueues and Runloops
code smells, Debugging Techniques
code-signing, Code Signing
codesign command, Code Signing
collectable memory, Non Objective-C objects
collections, Fast enumeration
color-blended layers, Graphics
command-line argument styles, getopt_long()
command-line arguments, Parsing the Command Line
Common Data Securty Architecture, Keychain Access
Common Security Services Manager, Keychain Access
common source file extensions, The Compiler
compile-time assert, Message boundaries
compiler assembly code, Seeing the Generated Assembly Code
compiler flags, Handy Flags
compiler optimization, Compiler Optimization
compiler pipeline, The Compiler pipeline
compiler warnings, Warnings
compilers

clang, Predefined symbols, The Compiler, Implementation

gcc (see gcc)

complex-lifetime operations, Complex-Lifetime Operations
ComplexMessage.m, Bundles in Cocoa
concatenation, Stringization and concatenation
concurrent enumeration, New API Using Blocks
concurrent operations, Operations
conditional breakpoints, Breakpoints
conditional compilation, Conditional compilation
conditional logging, Variable argument lists
conditional variables, Condition variables
conditional-compilation.m, Conditional compilation
configd, Architecture
configuration agents, Architecture
connect(), connect
const, Const and volatile variables
convenience functions, Convenience Functions

keychain, Convenience Functions

copy (property attribute), Properties
copy on write, fork
Core Animation instrument, Graphics
core files, For the More Curious: Assertions, For the More Curious: Core Files
count(), Aggregates
countread.d, Aggregates
COW, fork
cp command, Resource forks
__cplusplus, Predefined symbols
CPU

optimization, CPU, time

parallelism of, The End of Free Performance

resource limits, Resource limits

vs. RAM, Memory is the New I/O

creat(), creat()
critical sections, Blocking signals, Mutexes
CRLF, Opening files
CSSM, Keychain Access
cttfilt, Name Mangling
current event ID, Miscellaneous calls
current working directory, Current working directory, exec
custom icons, Items and Attribute Lists
custom sources, Custom sources
cwd, Built-in variables
Cycling Fusion, Looking at memory leaks

D

-D compiler flag, Preprocessor symbols
D language, The D language, Actions, The C preprocessor
daemon processes, Parent and Child Lifetimes
daemon threads, Creating threads
daemons, syslog(), Memory
dangling pointers, Memory corruption
Darwin, Mac OS X: Built to Evolve, Basic API
data alignment, The 64-bit programming model
data blocks, Inside The File System
data buffering, Buffered I/O vs. Unbuffered I/O
data synchronization, Synchronization
datagram sockets, For the More Curious: Datagrams
dataseg.m, Program Memory Model
datasource.m, Protocols
__DATE__, Predefined symbols
deadlocks, Deadlocks
debugging

(see also error handling, gdb)

with assertions, For the More Curious: Assertions

blocks, Debugging

caveman-style, Logging

garbage collection, Debugging

with logging, Logging

memory problems, Debugging Memory Problems

remote, Handy Tricks

symbols, Handy Flags, Debugging, What Is a Debugger?, For the More Curious: Stripping

techniques, Debugging Techniques

with breakpoints, Breakpoints

debuglog-macro.m, Variadic macros
debuglog.m, Variable argument lists
DEBUG_ASSERT_MESSAGE, AssertMacros.h
defaults command, Example
#define, Preprocessor symbols, Handy Flags

contraindications, Preprocessor symbols

define on command-line, Handy Flags
defunct processes, Parent and Child Lifetimes
denormalize(), Aggregate-related functions
dependencies, Simple-Lifetime Operations
describeObjects.m, Variable argument lists
/dev/fsevents, fsevents
dev_t (type), Creating the stream
directories

dropbox, Watching Directories

enumerating, Directory Enumeration

iterating, The opendir() Family

overview, Files, Part 2: Directories, File Systems, and Links

standard, Finding Standard Directories

structure, Inside The File System

watching, Watching Directories

directory enumeration

classic Unix, The opendir() Family

Cocoa, Enumeration with paths, Enumeration with URLs

getdirentriesattr(), Metadata in batches

directory paths, Finding Standard Directories
directory permissions, Directory Permissions
directory search domains, Finding Standard Directories
dirent (struct), The opendir() Family
DirEntry.h, Create the DirEntry class
DirEntry.m, Create the DirEntry class
dirwatcher.m, kqueues for File System Monitoring
disassembly, For the More Curious: otool, Program Listings
disk optimization, Disk
disk partitions, Inside The File System
Disk Utility, Differences between HFS+ and UFS
dispatch groups, Dispatch Groups
dispatch queues, Queues, Queues
dispatch sources, Dispatch Sources
dispatch.h, Grand Central Dispatch
dispatch_after(), Time, Time, Time
dispatch_apply(), Iteration
dispatch_async(), Dispatching
dispatch_debug(), For the More Curious: The dispatch_debug() Function
dispatch_get_context(), Memory management
dispatch_get_global_queue(), Queues
dispatch_get_main_queue(), Queues
dispatch_group_async(), Dispatch Groups
dispatch_group_create(), Dispatch Groups
dispatch_group_enter(), Dispatch Groups
dispatch_group_leave(), Dispatch Groups
dispatch_group_notify(), Dispatch Groups
dispatch_group_t (type), Dispatch Groups
dispatch_group_wait(), Dispatch Groups
dispatch_main(), Dispatching
dispatch_once(), Safe Global Initialization
dispatch_queue_create(), Queues
DISPATCH_QUEUE_PRIORITY_* constants, Queues
dispatch_release(), Memory management
dispatch_resume(), Memory management

and dispatch sources, Dispatch Sources

dispatch_retain(), Memory management
dispatch_semaphore_create(), Semaphores
dispatch_semaphore_signal(), Semaphores
dispatch_semaphore_wait(), Semaphores
dispatch_set_context(), Memory management
dispatch_set_finalizer_f(), Memory management
dispatch_source_cancel(), Dispatch Sources
dispatch_source_create(), Dispatch Sources
dispatch_source_get_handle(), Dispatch Sources
dispatch_source_get_mask(), Dispatch Sources
dispatch_source_set_event_handler(), Dispatch Sources
dispatch_source_testcancel(), Dispatch Sources
DISPATCH_SOURCE_TYPE_* constants, Dispatch Sources
dispatch_suspend(), Memory management
dispatch_sync(), Dispatching
dispatch_time(), Time, Time, Time
DISPATCH_TIME_* constants, Time, Time, Time
dispatch_walltime(), Time, Time, Time
displaying data, Displaying Data
Distributed Objects, NSThread
dlclose(), dlopen()
dlerror(), dlopen()
dlopen(), dlopen()
dlsym(), dlsym()
-DNDEBUG, For the More Curious: Assertions
DNS, Domain Name Lookup
Documents directory, Finding Standard Directories
domain name lookup, Domain Name Lookup
dot operator (.), Properties
dotted-quad, Addresses
dropbox directories, Watching Directories
dscacheutil command, Users and groups
dsch (see Shostakovich, Dmitri)
dscl command, Users and groups
.DS_Store, .DS_Store
DTrace, DTrace

actions, Actions

aggregates, Aggregates

ambush script, Pragmas

arrays, Arrays

built-in variables, Built-in variables

and D language, The D language, Actions, The C preprocessor

dtrace command, Scripts

dtruss command, dtruss

errno, Built-in variables

exit(), Functions

functions, Functions, Aggregates, Aggregate-related functions

normalizing aggregates, Aggregate-related functions

and Objective-C, Objective-C

pid, Built-in variables

pragmas, Pragmas

predicates, Predicates

probes, Overview, Probes

providers, Overview, Probes, Providers

scoped variables, Scoped variables

scripts, Scripts, The C preprocessor

timer, profile provider

and Trace Symbol instrument, Symbol Trace and DTrace

variables, Variables

dtruss command, dtruss
dump-block-byref command, Dumping runtime information
dump-block-literal command, Dumping runtime information
dumpargs.m, Parsing the Command Line
dumpem.m (keychain), Searching for Items
dup(), Other random calls
dup2(), Opening a file, Other random calls
dyld, Shared Libraries
DYLD_FALLBACK_FRAMEWORK_PATH, For the More Curious: Runtime Environment Variables
DYLD_FALLBACK_LIBRARY_PATH, dlopen(), For the More Curious: Runtime Environment Variables
DYLD_FRAMEWORK_PATH, For the More Curious: Runtime Environment Variables
DYLD_IMAGE_SUFFIX, fs_usage and sc_usage
DYLD_INSERT_LIBRARIES, Guard Malloc
DYLD_LIBRARY_PATH, Shared Libraries, dlopen(), For the More Curious: Runtime Environment Variables
DYLD_PRINT_APIS, For the More Curious: Runtime Environment Variables
DYLD_PRINT_INITIALIZERS, For the More Curious: Runtime Environment Variables
DYLD_PRINT_LIBRARIES, Shared Libraries, Frameworks
DYLD_PRINT_SEGMENTS, For the More Curious: Runtime Environment Variables
DYLD_PRINT_STATISTICS, For the More Curious: Runtime Environment Variables
dylib, Shared Libraries
@dynamic directive, Properties
dynamic memory, Dynamic Memory Allocation
dynamic store, Architecture
dynamic tracing (see DTrace)

E

edge-triggered events, kqueues for Socket Monitoring
EEXIST, errno
EFAULT, errno
effective user ID, File permissions
EISDIR, errno
#else, Conditional compilation
emacs, What Is a Debugger?
enable warnings, Handy Flags
ENAMETOOLONG, errno
end of file, Reading from a file, Text I/O
END provider, BEGIN and END providers
endian-ness, Network Byte Order
#endif, Conditional compilation
Energy Diagnostics, Power
ENFILE, errno
ENOENT, errno
ENOSPC, errno
ENOTDIR, errno
environment variables, Looking at the Environment, Memory corruption, Debugging, exec
EOF, Reading from a file, Text I/O
errno, errno, Built-in variables
error handling, Parsing the Command Line, Exceptions, Error Handling, and Signals

(see also debugging, exception handling)

errno, errno

exceptions, Exception Handling in Cocoa

longjmp(), setjmp, longjmp

setjmp(), setjmp, longjmp

ERROR provider, BEGIN and END providers
event database, Events
event filters, Events
event flags, Events
event IDs, Events
event streams, FSEvents API
everything is a file, Files, Part 1: I/O and Permissions
EVFILT_PROC, Events
EVFILT_READ, Events, kqueues for Socket Monitoring
EVFILT_SIGNAL, Events, kqueues for Signal Handling
EVFILT_VNODE, Events, kqueues for Socket Monitoring, kqueues for File System Monitoring
EVFILT_WRITE, Events, kqueues for Socket Monitoring
EV_ADD, Events, Registering and Handling Events
EV_CLEAR, Events, Registering and Handling Events
EV_DELETE, Events
EV_DISABLE, Events
EV_ENABLE, Events, Registering and Handling Events
EV_EOF, kqueues for Socket Monitoring
EV_ERROR, Registering and Handling Events
EV_ONESHOT, Events
EV_SET(), Events
EV_SET64(), Events
exception handling

and 64-bit runtime, 64-bit Objective-C runtime

@synchronized(), Objective-C @synchronized blocks

in Cocoa, Exception Handling in Cocoa

exception.m, Classic exception handling
EXC_ARITHMETIC, Semaphores
exec(), exec, fork() Gotchas
execname, Built-in variables
execs.d, proc provider
exit state macros, Parent and Child Lifetimes
exit(), Functions
exit() vs. _exit(), fork
EXIT_* constants, Parsing the Command Line
expansion.m, Preprocessor symbols
experiment, Overview
export, Looking at the Environment
extended attributes, Extended attributes
extensions

class, Class extensions

file, Making and Manipulating Paths

gcc, GCC Extensions

path, Making and Manipulating Paths, NSURL path utilities

external reference counts, External reference counts

F

-F linker flag, Frameworks
-fast compiler flag, The Optimizer
fast enumeration, Fast enumeration
-fastcp compiler flag, The Optimizer
-fastf compiler flag, The Optimizer
fat binaries (see universal binaries)
fat-macro-warn.c, Fat binaries from the command line
fbinaryio.m, Binary I/O
fbt provider, fbt provider
fchdir(), Current working directory
fchmod(), File permissions
fchown(), Users and groups
fclose(), Closing files
fcntl(), Other random calls, Multiplexing connections
fd (see file descriptors)
fdopen(), Opening files
FD_* functions, Multiplexing connections
fd_set (type), Multiplexing connections
fence-post error, Memory corruption
feof(), Text I/O
ferror(), Text I/O
fgetc(), Text I/O
fgetpos(), Positioning
fgets(), Text I/O
Fibonacci sequence, Fast enumeration
__FILE__, Predefined symbols, Buffered I/O
file access times, stat()
File Activity instrument, File System
file attributes category, Metadata through paths
file descriptors

and CFRunLoop, CFRunLoop

close on exec, Other random calls

described, Opening a file, Changing the read/write offset

in networking, Server coding, Multiplexing connections

numbering, Opening a file

and select(), Multiplexing connections

sets of, Multiplexing connections

file extensions, Making and Manipulating Paths
file holes, Differences between HFS+ and UFS
file IDs, Differences between HFS+ and UFS
file metadata

accessing, File Metadata, File Metadata

batch operations, Metadata in batches, Metadata through URLs

in inodes, Inside The File System

naming differences, Differences between HFS+ and UFS

and NSFileManager, File Metadata

via URLs, Metadata through URLs

file offset, Writing to a file, Changing the read/write offset
file operations, File Operations, Path operations, URL operations
file paths, Making and Manipulating Paths
file permissions, File Permissions
file reference URLs, File references
*_FILENO constants, Opening a file
fileno(), Misc Functions
files

aliases for, Bookmarks

attributes of, getattrlist(), Extended attributes

bookmarks in, Bookmarks

closing, Closing files, Closing files

core, For the More Curious: Core Files

including, File inclusion

links to, Links

memory-mapped, For the More Curious: Memory-Mapped Files

naming differences, Differences between HFS+ and UFS

opening, Opening a file, Opening files

read/write offset, Writing to a file, Changing the read/write offset

reading, Reading from a file

reference URLs for, File references

referencing, Links, File References and Bookmarks

removing, Removing Files

temporary, Temporary Files

truncating, Removing Files

writing, Writing to a file

filesystems, Inside The File System, Differences between HFS+ and UFS
finalize method, How to use it, Finalize methods
@finally, Native exception handling
flags

compiler, Handy Flags

event, Events, Creating the stream, The FSEvents callback

-flat_namespace, Shared Libraries
flow analysis, Compiler Optimization
fnmatch(), Changing Behavior By Name
-fobjc-exceptions, Objective-C @synchronized blocks
-fobjc-gc, How to use it
-fobjc-gc-only, How to use it
foldersize.m, Metadata in batches
fopen(), Opening files
for...in enumeration syntax, Fast enumeration
fork(), fork

gotchas, fork, fork() Gotchas

fork.m, fork
formatted I/O, Formatted I/O
FORTRAN, Memory is the New I/O
fpathconf(), Other random calls
fpos_t (type), 64-bit cleanliness
fprintf(), Formatted I/O
fputc(), Text I/O
fputs(), Text I/O
fractal geometry, MandelOpper
fragile base class problem, New Objective-C runtime
frameworks, Frameworks, Differences between HFS+ and UFS
fread(), Inside the Central Loop, Binary I/O
free(), Dynamic Memory Allocation, free()
freopen(), Opening files
fsaclctl command, Access control lists
fscanf(), Formatted I/O
fsck command, Inside The File System
fseek(), Positioning
fseeko(), Positioning
fsetpos(), Positioning
fsevent runloop, Hook up to the runloop
FSEvents, fsevents

callbacks, The FSEvents callback

change history, History, Creating the stream

daemon, fseventsd

device UUID, Miscellaneous calls

event IDs, Events, Miscellaneous calls

event streams, FSEvents API

historical events completed, The FSEvents callback

vs. kqueues, fsevents

latency, Creating the stream

preventing logging, History

scheduled in runloop, Hook up to the runloop

stream flags, Creating the stream, The FSEvents callback

volume mounting, The FSEvents callback

watched path notifications, Creating the stream

fsevents.m, Example
FSEventsCopyUUIDForDevice(), Miscellaneous calls
fseventsd, fseventsd, History
FSEventsStreamRef, FSEvents API
FSEventsStreamStart(), Hook up to the runloop
FSEventStreamCreate(), Creating the stream
FSEventStreamRelease(), Creating the stream
FSEventStreamRetain(), Creating the stream
FSEventStreamStop(), Hook up to the runloop
FSGetCatalogInfo(), stat()
fstat(), stat()
fs_usage command, fs_usage and sc_usage
ftell(), Positioning
ftello(), Positioning
ftruncate(), Other random calls
__func__, Predefined symbols
__FUNCTION__, Predefined symbols
function call stack, Program Memory Model
function name, Predefined symbols
function trace provider, fbt provider
functions

(see also individual function names)

convenience, Convenience Functions

DTrace, Functions, Aggregates, Aggregate-related functions

non-reentrant, Reentrancy

reentrant, Reentrancy

variadic, Variable argument lists

fwrite(), Binary I/O
F_* constants, Other random calls

G

G5 processor, The Optimizer, 64-Bit Computing, Memory is the New I/O
Garage Band, Window layout
garbage collection, Objective-C Garbage Collection

and Core Foundation, CFSocket

debugging, Debugging

in Objective-C, Runtime enhancements and garbage collection

and threads, GC and threads

GC memory model, How it works
gc-sample.m, How to use it
gcc, The Compiler

autovectorizer, Vectorization

extensions, GCC Extensions

flags, Handy Flags

optimization, Compiler Optimization

seeing assembly code, Seeing the Generated Assembly Code

vectorization, Vectorization

version testing, Testing the compiler version

warnings in, Warnings

GCD (Grand Central Dispatch), Grand Central Dispatch

absolute time timeout, Time, Time, Time

context pointers, Memory management

dispatch groups, Dispatch Groups

dispatch sources, Dispatch Sources

dispatch_debug(), For the More Curious: The dispatch_debug() Function

and memory management, Memory management

vs. NSOperation, GCD or NSOperation?

object-oriented design of, Object-Oriented Design

overview, Grand Central Dispatch

queues, Queues, Queues

and runloops, Grand Central Dispatch

safe global initialization, Safe Global Initialization

semaphores, Semaphores

suspend and resume, Memory management

synchronizing tasks with, For the More Curious: Synchronizing Tasks

target queue, Queues, Queues

tasks, GCD Terminology

and time, Time, Time, Time

work items, GCD Terminology

_f function versions, Dispatching

gdb, What Is a Debugger?

(see also debugging)

from command-line, Using GDB from the Command Line

commands (see gdb commands)

described, What Is a Debugger?

halting exceptions, Classic exception handling

and threads, Threads

gdb commands

apropos, Help

break, A sample GDB session, Breakpoints

bt, Stack Traces

call, Displaying Data

cond, Breakpoints

continue, A sample GDB session

disable, A sample GDB session

disassemble, Program Listings

displaying arrays, Displaying Data

displaying data, Displaying Data

displaying formats, Displaying Data

down, Stack Traces

dump-block-byref, Dumping runtime information

dump-block-literal, Dumping runtime information

fb (future break), Handy Tricks

finish, Changing Execution Flow

frame, Stack Traces

help, Help

ignore, Breakpoints

info args, Stack Traces

info breakpoints, A sample GDB session

info catch, Stack Traces

info classes, Displaying Data

info locals, Stack Traces

info selectors, Displaying Data

info threads, Threads

invoke-block, Debugging

list, A sample GDB session, Program Listings

next, A sample GDB session, Breakpoints

po (print object), Displaying Data

print, Displaying Data

ptype, A sample GDB session

rbreak, Breakpoints

return, Changing Execution Flow

set, Changing Data

set confirm, A sample GDB session

set scheduler-locking, Threads

show scheduler-locking, Threads

stack traces, A sample GDB session

step, Breakpoints

thread, Threads

thread apply, Threads

until, A sample GDB session, Changing Execution Flow

up, Stack Traces

whatis, A sample GDB session

where, A sample GDB session, Stack Traces

.gdbinit, Handy Tricks
generated assembly code, Handy Flags
getattrlist(), getattrlist()
getc(), Text I/O
getchar(), Text I/O
getcwd(), Current working directory
getdirentriesattr(), Metadata in batches
getenv(), Looking at the Environment
gethostbyname2(), Domain Name Lookup
getopt_long(), getopt_long()

options, getopt_long()

getpeername(), Runloop Chatter Server
getpwent(), Users and groups
getpwnam(), Users and groups
getpwuid(), Users and groups
getrlimit(), Resource limits
gets(), Text I/O
getw(), Misc Functions
getxattr(), Extended attributes
global variables, Memory is the New I/O
glob_b(), New API Using Blocks
__GNUC__, Testing the compiler version
gotchas

64-bit long bitmasks, 64-bit cleanliness

fork() and buffered I/O, fork

fork() race conditions, fork

forking in Cocoa, fork() Gotchas

kqueue recycle race, kqueues and Runloops

namespace collisions, BitmapView

notifications on background threads, Threading issues

NSOperationQueue and blocked operations, CalcOperation

NSOperations in Mac OS X 10.6, Operations

varargs, Varargs gotchas

goto, errno

considered awesome, Parsing the Command Line

Grand Central Dispatch (see GCD (Grand Central Dispatch))
graphics

optimizing, Graphics

and performance problems, Graphics

group ID, Users and groups
group.m, Dispatch Groups
groups, Users and groups
guard malloc, Guard Malloc
GUID, Events, Miscellaneous calls, NSProcessInfo
g​r​o​u​s​e​, dtruss

H

hard limit, Resource limits
hard links, Hard links
Hasselhoff, David, Properties
heap command, Miscellaneous tools
heap memory, Program Memory Model, Dynamic Memory Allocation
heapshots, More Memory: Heapshots
hexdump command, Writing to a file, Binary I/O
HFS+, The opendir() Family, Inside The File System, Differences between HFS+ and UFS, Other random programs
home directory, Finding Standard Directories
hostent (struct), Domain Name Lookup
hostnames, CFHost
hstrerror(), Domain Name Lookup
htonl(), Network Byte Order
htons(), Network Byte Order
hyperthreading, The End of Free Performance
h_errno, Domain Name Lookup

I

I/O

binary, Binary I/O

blocking, Blocking I/O

buffered, Buffered I/O

buffered vs. unbuffered, Buffered I/O vs. Unbuffered I/O

formatted, Formatted I/O

scatter/gather, Scatter / Gather I/O

standard streams, Command-Line Programs, Opening a file

text, Text I/O

unbuffered, Unbuffered I/O

__i386__, Fat binaries from the command line
id command, Users and groups
#if, Conditional compilation
#ifdef, Conditional compilation
#ifndef, Conditional compilation
ILP32, Network Byte Order
ImageCanvas.h, ImageCanvas
ImageCanvas.m, ImageCanvas
ImageSnarferAppDelegate.h, ImageSnarferAppDelegate
ImageSnarferAppDelegate.m, ImageSnarferAppDelegate
immutable objects, Cocoa and thread safety
#import, File inclusion
#include, File inclusion
include guard, File inclusion
include-guard.h, File inclusion
including files, File inclusion
inet_aton(), IPv4-specific functions
inet_ntoa(), IPv4-specific functions
inet_ntop(), IPv4- and IPv6-compatible functions
inet_pton(), IPv4- and IPv6-compatible functions
info pointer, C callbacks in Objective-C
informal protocols, Protocols
initialized data segment, Program Memory Model
inodes, Inside The File System
Instruments, Instruments

Activity Monitor, System Details

Allocations, Looking at memory leaks, More Memory: Heapshots

Automation, Automation

Bluetooth, Power

Core Animation, Graphics

CPU Activity, Power

and data mining, Time Profiler

details panel, Looking at memory leaks

Directory I/O, File System

Disk Monitor, File System

Dispatch, System Details

Display Brightness, Power

Energy Usage, Power

extended detail panel, Looking at memory leaks

for file system, File System

File Activity, File System

File Attributes, File System

GPS, Power

for graphics, Graphics

for heapshots, More Memory: Heapshots

highlight view, System Details

inspection range controls, The Toolbar

jump bar, Looking at memory leaks

Leaks, Looking at memory leaks

for memory, Looking at memory leaks, Memory

navigating, Instruments

Network Activity, System Details

OpenGL, Graphics

for power consumption, Power

radio, Power

for system details, System Details

System Calls, System Details

system-wide profiling with, Time Profiler

target menu, The Toolbar

Time Profiler, Time Profiler

time/run control, The Toolbar

Trace Symbol, Symbol Trace and DTrace

VM Operations, System Details

WiFi, Power

Intel architecture, Universally Fat Binaries
interrupt.m, Reentrancy
inverted call trees, Time Profiler
invoke-block command, Debugging
in_addr (struct), IPv4 address structures
iOS

availability of kevent64_s, Events

blocking UI thread, CFRunLoop

blocks availability, Blocks

code signing, Code Signing

definition of universal, Fat binaries from the command line

exception model, 64-bit Objective-C runtime

frameworks, Libraries or Frameworks?

garbage collection, Objective-C Garbage Collection

GCD availability, Grand Central Dispatch

informal protocols, Class extensions

lack of DTrace, Symbol Trace and DTrace

lack of swap, Virtual Memory

low-memory warnings, Common API issues

memory leaks, Memory leaks

modern Objective-C runtime, New Objective-C runtime, 64-bit Objective-C runtime

nonatomic properties, Properties

NSOperation lifetime behavior, Operations

power consumption instruments, Power

unavailability of URL-based metadata, File Metadata

iOS 4.0, Blocks
iovec (struct), Scatter / Gather I/O
IP addresses, Addresses, Address Conversions, Constructing an address
IP change notification, The System Configuration Framework
IPv4, Addresses, IPv4 address structures
IPv6, Addresses, IPv6 address structures, Make chatterserver Zeroconf-compliant
islands of serialization, Grand Central Dispatch
ivar_getOffset(), New Objective-C runtime

J

JavaScript, Automation
jmp_buf, setjmp, longjmp
journaled file system, Inside The File System

K

kCFAbsoluteTimeIntervalSince1970, Miscellaneous calls
kCFAllocatorDefault, CFSocket
kCFSocket* constants, CFSocket
kernel boundary, Unbuffered I/O
kevent (struct), Events
kevent(), dtruss, kqueue(), Registering and Handling Events
kevent64_s (struct), Events
kevents, Events
keventwatch.d, Scripts
Key-Value Observing, Operations, KVO properties
Keychain Access application, Accessing the Keychain
keychain items

accessing, Keychain Access

creating, Making a New Keychain Item

described, Items and Attribute Lists

reading data from, Reading Data From an Item

searching for, Searching for Items

keychains, Accessing the Keychain

access control lists, Keychain Access

and attribute lists, Items and Attribute Lists

and certificates, Items and Attribute Lists, Searching for Items

and code-signing, Code Signing

convenience functions, Convenience Functions

editing, Editing the Keychain

items in (see keychain items)

and passwords, Items and Attribute Lists

retrieving specific, Getting Specific Keychains

searching, Searching for Items

kFSEventStreamCreateFlag* constants, Creating the stream
kFSEventStreamEventFlag* constants, The FSEvents callback
kqueue(), kqueue()
kqueues

for file system monitoring, kqueues for File System Monitoring

and kevents, Events

overview, kqueue and FSEvents

and runloops, kqueues and Runloops

for signal handling, kqueues for Signal Handling

for socket monitoring, kqueues for Socket Monitoring

kSec* constants, Items and Attribute Lists
ktrace command, DTrace
KVO, Operations, KVO properties

L

lambdas, Changing Enclosing Scope
latency, Creating the stream
layers, color-blended, Graphics
lchown(), Users and groups, Symbolic links
ld command, Shared Libraries
LD_LIBRARY_PATH, dlopen()
leaks command, Memory leaks
Leaks instrument, Looking at memory leaks
leaks, memory, free(), Memory leaks, Looking at memory leaks
left shift (bitwise operator), Bitwise operators
Leopard (see Mac OS X 10.5)
level-triggered events, kqueues for Socket Monitoring
libgmalloc, Guard Malloc
libraries

defined, Libraries

vs. frameworks, Frameworks, Libraries or Frameworks?

and libtool, For the More Curious: libtool

and otool, For the More Curious: otool

shared, Shared Libraries, Shared Libraries and dlopen

static, Static Libraries

Library directory, Finding Standard Directories
libSystem, Shared Libraries
libtool, For the More Curious: libtool
limits, resource, Resource limits, For the More Curious: Core Files, fork
limits.m, Resource limits
__LINE__, Predefined symbols
line buffering, Buffered I/O
line continuation, Multiline macros
link local addressing, Bonjour
link(), API for links
linker, Shared Libraries
links, Links

API for, API for links

hard, Hard links

symbolic, Changing Behavior By Name, Symbolic links, Symbolic links

Linux, Conditional compilation, Changing Behavior By Name, Frameworks, Process Scheduling
lipo command, Fat binaries from the command line
list open files, Removing Files
listen(), listen
listxattr(), Extended attributes
__LITTLE_ENDIAN__, Predefined symbols
little endian, Network Byte Order
LLP64, The 64-bit programming model
llvm, The Compiler
llvm gcc, The Compiler
ln command, Hard links, Symbolic links
load average, Process Scheduling
loadaverage.d, Built-in variables
loader, Shared Libraries
localException variable, Classic exception handling
locality of reference, malloc(), Locality of Reference, Disk
locality.m, Locality of Reference
locking

(see also race conditions)

memory, Virtual Memory

mutexes, Deadlocks

read/write access, For the More Curious: Read/Write Locks

logging

as debugging technique, Logging

conditional, Variable argument lists

power, Power

quietly, QuietLog

LOG_* constants, syslog()
longjmp(), Const and volatile variables, setjmp, longjmp, Reentrancy
longjmp.m, setjmp, longjmp
LONG_BIT, 64-bit cleanliness
look of disapproval, Grand Central Dispatch
look-up tables, Approaches To Performance, Memory is the New I/O
loops

do/while, Multiline macros

for, Fast enumeration

run, CFRunLoop

lotus blossom, Properties
low memory conditions, Common API issues
low-memory warnings, Common API issues
__LP64__, Predefined symbols, The 64-bit programming model, Fat binaries from the command line, Network Byte Order
lquantize(), Aggregates
lseek(), Changing the read/write offset
lsof command, Removing Files
lstat(), stat()
lubdub.d, profile provider

M

Mac OS 9, Differences between HFS+ and UFS
Mac OS X

10.3 (Panther)

kqueue availability, kqueue and FSEvents

native exceptions available, Exception Handling in Cocoa, Native exception handling

NSError available, NSError

NS_HANDLER binary compatibility, Native exception handling

10.4 (Tiger)

/dev/fsevents, fsevents

64-bit availability, 64-Bit Computing

ACL availability, Access control lists

extended attribute availability, Extended attributes

getattrlist() availability, getattrlist()

ktrace availability, dtruss

resource fork command-line support, Resource forks

10.5 (Leopard)

64-bit Objective-C Runtime, 64-bit Objective-C runtime

code signing, Code Signing

DTrace availability, DTrace

dtruss availability, dtruss

extended 64-bit support, 64-bit cleanliness

FSEvents availability, fsevents

garbage collection availability, Objective-C Garbage Collection

hard link support, Differences between HFS+ and UFS

NSOperation availability, Operations

NSOperationQueue behavior, NSOperationQueue

Objective-C 2.0 availability, Objective-C 2.0

Rosetta, Fat binaries from the command line

[non]concurrent operations, Operations

10.6 (Snow Leopard)

availability of kevent64_s, Events

blocks availability, Blocks

directory enumeration depth, Enumeration with paths

expanded GC heap, How it works

GCD availability, Grand Central Dispatch

GCD runloops and fork(), fork() Gotchas

informal protocols, Class extensions

NSOperation lifetime behavior, Operations

NSOperationQueue behavior, NSOperationQueue

NSURL file identifiers, NSFileManager - Cocoa and the File System

NSURL Path utilities, NSURL path utilities

Rosetta removal, Fat binaries from the command line

definition of universal, Fat binaries from the command line

development tools versions, The Compiler

specific filesystem weirdness, Mac OS X Specific Weirdness

__MACH__, Predefined symbols
Mach, Precise Timing with mach_absolute_time()
mach ports, fork() Gotchas
machtime.m, Precise Timing with mach_absolute_time()
mach_absolute_time(), Precise Timing with mach_absolute_time()
mach_timebase_info(), Precise Timing with mach_absolute_time()
mach_timebase_info_t (type), Precise Timing with mach_absolute_time()
macros

compiler version testing, Testing the compiler version

eating the comma, Variadic macros

exit state, Parent and Child Lifetimes

expanding, Preprocessor symbols

hygiene, Macro hygiene

multiline, Multiline macros

preprocessor, Macro hygiene

side-effects of, Macro hygiene

variadic, Variadic macros

main thread, Creating threads
malloc guard, Guard Malloc
malloc(), Dynamic Memory Allocation, malloc(), Common API issues

debugging tools, Memory corruption

thread safety, malloc()

malloc-pid.d, pid provider
mallocalign.m, malloc()
MallocCheckHeapStart, MallocStackLogging and MallocStackLoggingNoCompact
MallocGuardEdges, MallocGuardEdges
MallocHelp, Memory corruption
mallochistory.m, Miscellaneous tools
MallocScribble, MallocScribble
mallocscribble.m, MallocScribble
mallocsize.m, malloc()
MallocStackLogging, MallocStackLogging and MallocStackLoggingNoCompact
MallocStackLoggingNoCompact, MallocStackLogging and MallocStackLoggingNoCompact
malloc_history command, Miscellaneous tools
malloc_size(), malloc()
Mandelbrot set, MandelOpper
MandleOpperAppDelegate.h, MandelOpperAppDelegate
mangling, name, Name Mangling
MAP_* constants, For the More Curious: Memory-Mapped Files
Martin, Robert C., Debugging Techniques
max(), Aggregates
MAXPATHLEN, Current working directory
mDNSResponder process, Bonjour
memerror.m, Exercises
memory

abandoned, More Memory: Heapshots

alignment, malloc()

allocation history, Looking at memory leaks

cache line, Locality of Reference

Core Foundation, CFRunLoop

corruption, Memory corruption

debugging problems, Debugging Memory Problems

dynamic, Dynamic Memory Allocation

heap, Dynamic Memory Allocation

heap command, Program Memory Model

heapshots, More Memory: Heapshots

leaks, Memory leaks, Memory leaks in Cocoa, Looking at memory leaks

leaks command, free()

lifetime, Memory Lifetime

locality of reference, Locality of Reference

locking, Virtual Memory

management errors, Debugging Memory Problems

ownership, Memory Ownership Issues

and performance problems, Memory

pools, Nodepools

processor cache, Memory is the New I/O

program model, Program Memory Model

ps command, ps

RAM latency, Memory is the New I/O

resource limits, Resource limits

virtual, Virtual Memory

warm pages, Locality of Reference

memory-mapped files, Disk, For the More Curious: Memory-Mapped Files
message boundaries, Message boundaries
message reading scenarios, Message boundaries
metadata (see file metadata)
methods

(see also individual method names, Objective-C)

block-oriented, New API Using Blocks

no good as C callbacks, C callbacks in Objective-C

variable argument, Variable argument lists

min(), Aggregates
mixed mode, Fat binary considerations
mkdir(), Creation and destruction
mkdtemp(), Temporary Files
mkstemp(), Temporary Files
mkstemps, Temporary Files
mktemp(), Temporary Files
mktime(), Time, Time, Time
mlock(), Virtual Memory
mmap(), For the More Curious: Memory-Mapped Files
mmap-rot13.m, For the More Curious: Memory-Mapped Files
MobileMusicPlayer, Time Profiler
Moore’s Law, The End of Free Performance
msync(), For the More Curious: Memory-Mapped Files
MS_* constants, For the More Curious: Memory-Mapped Files
Multicast DNS, Bonjour
multicore processors, The End of Free Performance
multiline macros, Multiline macros
multilineMacro.m, Multiline macros
multiplexing network connections, Multiplexing connections
multiprocessing, Multiprocessing
multithreading, Multithreading

and fork(), fork() Gotchas

in Cocoa, Cocoa and Threading

conditional variables, Condition variables

deadlocks, Deadlocks

garbage collection, GC and threads

gdb, Threads

mutexes, Mutexes

and operations issues, Threading issues

overview, Multithreading

Posix, Posix Threads

race conditions, Synchronization

read/write locks, For the More Curious: Read/Write Locks

synchronization, Synchronization

thread local storage, For the More Curious: Thread Local Storage

thread safety, Current working directory

worker threads, Condition variables

munmap(), For the More Curious: Memory-Mapped Files
mutable objects, Cocoa and thread safety
mutex.m, Mutexes
mutexes, Mutexes

N

name mangling, Name Mangling
namedfork, Resource forks
namespace collisions, BitmapView
NAME_MAX, Opening a file
NAT, Addresses
network address conversions, Address Conversions
network addresses, Addresses, Address Conversions, Constructing an address
network byte order, Network Byte Order
network ports, IPv4 address structures
network scalability, kqueues for Socket Monitoring
networking

accepting connections, accept

binding addresses, bind

client programming, Client Coding

constructing an address, Constructing an address

converting addresses, Address Conversions

hostname lookup, Domain Name Lookup, CFHost

listening for connections, listen

making connections, connect

message boundaries, Message boundaries

multiplexing connections, Multiplexing connections

server programming, Server coding

newfs command, Inside The File System
nice command, Process Scheduling
nm command, Static Libraries, Shared Libraries
nodepool.m, Nodepools
non-blocking I/O, Multiplexing connections
non-blocking reads, Multiplexing connections, Non-blocking reads
non-concurrent operations, Operations, Simple-Lifetime Operations
non-local goto, setjmp, longjmp
non-reentrant functions, Reentrancy
nonatomic (property attribute), Properties
normalize(), Aggregate-related functions
NOT (bitwise operator), Bitwise operators
NOTE_ATTRIB, kqueues for File System Monitoring
NOTE_DELETE, kqueues for File System Monitoring
NOTE_EXTEND, kqueues for File System Monitoring
NOTE_LINK, kqueues for File System Monitoring
NOTE_RENAME, kqueues for File System Monitoring
NOTE_REVOKE, kqueues for File System Monitoring
NOTE_WRITE, kqueues for File System Monitoring
notifications

(see also FSEvents, kqueues)

and multithreading, NSThread

NSFileHandle, Non-blocking reads

NSTask, NSTask

NSWorkspace, NSWorkspace

no_argument, getopt_long()
no_log, History
NSAllocateCollectable(), Non Objective-C objects
NSApplication, subclassing, Subclassing NSApplication to catch exceptions
NSAssert(), For the More Curious: Assertions
NSAssertionHandler, For the More Curious: Assertions
NSAutoreleasePool, NSThread
NSBlockOperation, Block Syntax, NSBlockOperation
NSBrowser, Edit the nib file adding NSTreeController
NSCAssert(), For the More Curious: Assertions
NSConditionLock, NSThread
NSDictionary file attributes category, Metadata through paths
NSEC_PER_SEC, Time, Time, Time
NSEnumerationConcurrent, Setting and clearing bits
NSEnumerationOptions, Setting and clearing bits
NSEnumerationReverse, Setting and clearing bits
NSError, NSError
NSException, Classic exception handling
NSFastEnumeration, Fast enumeration
NSFastEnumerationState, Fast enumeration
NSFileHandle, NSFileHandle

background reads, Non-blocking reads

NSFileManager, stat(), NSFileManager - Cocoa and the File System, Make a File Browser

delegate methods, Path operations

NSFileType* constants, Metadata through paths
NSGarbageCollector, How to use it, How it works, External reference counts
NSHashTable, The "new" collection classes
NSHomeDirectory(), Finding Standard Directories
NSInteger, 64-bit cleanliness
NSLock, NSThread
NSLog, QuietLog
NSLogv(), Variable argument lists
NSMapTable, The "new" collection classes
NSModule, Shared Libraries and dlopen
NSNetService, Publishing an NSNetService
NSNetServiceBrowser, Browsing Net Services, Make ChatterClient browse for servers
NSOperation, Operations

vs. GCD, GCD or NSOperation?

gotchas, Operations

NSOperationQueue, Operations, NSOperationQueue
NSOperationQueuePriority* constants, Simple-Lifetime Operations
NSOutlineView, Edit the nib file adding NSTreeController
NSPipe, NSPipe
NSPointerArray, The "new" collection classes
NSProcessInfo, NSProcessInfo
NSRunLoop, CFRunLoop, NSThread
NSSearchPathForDirectoriesInDomains(), Path utilities
NSSetUncaughtExceptionHandler(), Subclassing NSApplication to catch exceptions
NSStringPathExtensions, NSString path utilities
NSTask, NSTask
NSTemporaryDirectory(), Temporary Files, Finding Standard Directories
NSThread, CFRunLoop, NSThread, For the More Curious: Thread Local Storage
NSTreeController, Edit the nib file adding NSTreeController
NSUInteger, 64-bit cleanliness
NSURL, NSURL path utilities, URL utilities
NSURLBookmarkCreation* constants, Bookmarks
NSURLBookmarkResolution* constants, Bookmarks
NSURLConnection, ImageSnarfer, NSURLConnection delegate methods
NSUserDefaults, User Defaults
NSView, Graphics, NSThread
NSVolumeEnumeration* constants, Metadata through URLs
NSWillBecomeMultiThreadedNotification, NSThread
NSWorkspace, NSWorkspace
NS_BUILD_32_LIKE_64, 64-bit cleanliness
NS_DURING, Classic exception handling
NS_ENDHANDLER, Classic exception handling
NS_HANDLER, Classic exception handling, Native exception handling
NS_REQUIRES_NIL_TERMINATION, Variable argument lists
NS_VALUERETURN, Classic exception handling
NS_VOIDRETURN, Classic exception handling
ntohl(), Network Byte Order
ntohs(), Network Byte Order

O

-O compiler flag, Compiler Optimization
obiwan, Memory corruption
__OBJC__, Predefined symbols
objc provider, Objective-C
objc-auto.h, Strong and weak references
objc_assign_ivar(), Strong and weak references
OBJC_DISABLE_GENERATIONAL, Debugging
objc_exception_throw(), Native exception handling, Handy Tricks
objc_memmove_collectable(), Non Objective-C objects
objc_msgSend(), C callbacks in Objective-C, Time Profiler
OBJC_PRINT_GC, Debugging
OBJC_USE_TLC, Debugging
object capture, Changing Enclosing Scope
object resurrection, Finalize methods
Objective-C, Objective-C

2.0 additions, Objective-C 2.0

and 64-bit programming, Runtime enhancements and garbage collection, 64-bit Objective-C runtime

adopting protocols, Class extensions

and 64-bit programming, New Objective-C runtime

C callbacks, Objective-C

callbacks in, C callbacks in Objective-C

class extensions, Class extensions

closures, Changing Enclosing Scope

and DTrace, Objective-C

exception handling in, 64-bit Objective-C runtime

fast enumeration, Fast enumeration

forward references, Class extensions

garbage collection in, Runtime enhancements and garbage collection

garbage collection in, Objective-C Garbage Collection

informal protocols, Protocols

properties, Properties

protocols, Protocols

retain cycles in, How to use it

runtime enhancements, Runtime enhancements and garbage collection, New Objective-C runtime

stack-based objects, Blocks as Objects

thread synchronization, Objective-C @synchronized blocks

Objective-C++, Even More Compiler Flags
objectleak.m, Memory leaks in Cocoa
octet, Bitwise operations
offsetof(), New Objective-C runtime
off_t (type), 64-bit cleanliness
online materials, Online Materials
open(), Opening a file
open.m, errno
opendir(), The opendir() Family
openfiles.m, Resource limits
openlog(), syslog()
operations, Operations

complex-lifetime, Complex-Lifetime Operations

concurrent, Operations

dependencies, Simple-Lifetime Operations

vs. GCD, GCD or NSOperation?

and KVO, KVO properties

and multithreading issues, Threading issues

non-concurrent, Operations, Simple-Lifetime Operations

and NSBlockOperation, NSBlockOperation

priority, Simple-Lifetime Operations

simple-lifetime, Simple-Lifetime Operations

optarg, getopt_long()
optimizer (gcc), The Optimizer
optimizing

compiler, Compiler Optimization, The Optimizer

graphics, Graphics

prematurely, Before using any of the profiling tools

optind, getopt_long()
option (struct), getopt_long()
option flowindent, Pragmas
option quiet, Pragmas
@optional directive, Protocols
optional_argument, getopt_long()
OR (bitwise operator), Bitwise operators
otool, Seeing the Generated Assembly Code, For the More Curious: otool
O_* constants, Opening a file
O_NONBLOCK, Multiplexing connections

P

page permissions, Virtual Memory
page zero, Program Memory Model
paging, Virtual Memory
Pakleds, How it works
Panther (see Mac OS X 10.3)
pants, Visibility
parallel programming, The End of Free Performance, Operations
parent pid, Parent and Child Lifetimes
parent processes, fork
passwd (struct), Users and groups
passwords

AppleShare, Items and Attribute Lists, Searching for Items

generic, Items and Attribute Lists

Internet, Items and Attribute Lists

path components, Making and Manipulating Paths
path extensions, Making and Manipulating Paths, NSURL path utilities
path separators, Differences between HFS+ and UFS, Making and Manipulating Paths
path utilities, NSString path utilities, Path utilities
pathconf(), Other random calls
PATH_MAX, Opening a file, Inside The File System
pcal.m, Convenience Functions
pclose(), Convenience Functions
per-clause scoped Dtrace variables, Scoped variables
per-thread scoped Dtrace variables, Scoped variables
performance problems, causes of, Major Causes of Performance Problems
performance tuning, Performance Tuning

with Activity Monitor, Activity Monitor

approaches to, Approaches To Performance, Before using any of the profiling tools

caches, Caches

CPU, CPU, Time Profiler

disk, Disk, fs_usage and sc_usage

file system, File System

graphics, Graphics, Graphics

with GUI tools, GUI Tools

I/O, Memory is the New I/O

with Instruments, Instruments

memory, Memory

power use, Power

and precise timing, Precise Timing with mach_absolute_time()

with Time Profiler, Time Profiler

with command-line tools, Command-Line Tools

permission algorithms, Permission-Check Algorithms
permission bits, File permissions
permissions

directory, Directory Permissions

file, File Permissions

set-uid, File permissions

S_I* constants, File permissions

permtype-getattrlist.m, Parsing the buffer
permtype.m, stat()
pid, Built-in variables
pid provider, pid provider
Pinkerton, Mike, Visibility
pipe(), Pipes
pipeline, compiler, The Compiler pipeline
pipeline.m, Pipes
pipelines, Command-Line Programs
pipes, Command-Line Programs, Pipes
plug-ins, Shared Libraries

writing, Writing Plug-ins

pointer aliasing, Memory is the New I/O
poll(), Multiplexing connections
popen(), Convenience Functions

security issues with, fork

ports, IPv4 address structures
Posix threads, Posix Threads
posix_spawn(), Fat binaries from the command line
power consumption, Power
Power instruments, Power
power logging, Power
__ppc__, Fat binaries from the command line
__ppc64__, Fat binaries from the command line
ppc970 architecture flag, Fat binaries from the command line
ppid, Parent and Child Lifetimes
#pragma, Pragmas
#pragma once, File inclusion
pragmas in DTrace, Pragmas
precise timing, Precise Timing with mach_absolute_time()
predef.m, Predefined symbols
predicates in DTrace, Predicates
premature optimization, Before using any of the profiling tools
preprocessor, The C preprocessor

concatenation, Stringization and concatenation

conditional compilation, Conditional compilation

function name, Predefined symbols

macros in, Macro hygiene

output from, Handy Flags, Seeing Preprocessor Output

symbols, Preprocessor symbols, Predefined symbols, Fat binaries from the command line

testing compiler version, Testing the compiler version

text manipulation, Stringization and concatenation

with DTrace, The C preprocessor

__LP64__, Fat binaries from the command line

preprocTest.m, Seeing Preprocessor Output
__PRETTY_FUNCTION__, Predefined symbols
printa(), Functions, Aggregates
printf(), Variable argument lists, Functions, Formatted I/O
priority, Simple-Lifetime Operations
private stack, Creating threads
probefunc, Built-in variables
probemod, Built-in variables
probename, Built-in variables
probes (DTrace), Overview, Probes
proc provider, proc provider
process priorities, Process Scheduling
processes

defunct, Parent and Child Lifetimes

forking, fork, fork() Gotchas

in Cocoa, fork() Gotchas

lifetimes of, Parent and Child Lifetimes

and pipelines, Pipes

reaping, Parent and Child Lifetimes

scheduling, Process Scheduling

zombie, Parent and Child Lifetimes

producer / consumer, Condition variables
profile provider, profile provider
profiling tools, Approaches To Performance, Before using any of the profiling tools, Stochastic profiling, Time Profiler
program arguments, Changing Behavior By Name
program call stack, Program Memory Model
program memory model, Program Memory Model
project headers, File inclusion
propeprov, Built-in variables
properties, Properties

@dynamic directive, Properties

@property directive, Properties

@synthesize directive, Properties

API control, Properties

attributes, Properties

concurrency, Properties

dot operator (.), Properties

memory management, Properties

mutability, Properties

@property directive, Properties
protocols, Protocols
PROT_* constants, For the More Curious: Memory-Mapped Files
providers (DTrace), Overview, Probes, Providers
ps command, ps, Parent and Child Lifetimes
psort_b(), New API Using Blocks
pthreads, Posix Threads
pthread_cond_broadcast(), Condition variables
pthread_cond_destroy(), Condition variables
pthread_cond_init(), Condition variables
PTHREAD_COND_INITIALIZER, Condition variables
pthread_cond_signal(), Condition variables
pthread_cond_t (type), Condition variables
pthread_cond_timedwait(), Condition variables
pthread_cond_wait(), Condition variables
pthread_create(), Creating threads
pthread_join(), Creating threads
pthread_key_t (type), For the More Curious: Thread Local Storage
PTHREAD_MUTEX_INITIALIZER, Mutexes
pthread_mutex_lock(), Mutexes
pthread_mutex_t (type), Mutexes
pthread_mutex_trylock(), Mutexes
pthread_mutex_unlock(), Mutexes
pthread_once(), Safe Global Initialization
pthread_rwlock_t (type), For the More Curious: Read/Write Locks
pthread_self(), Creating threads
pthread_setspecific(), For the More Curious: Thread Local Storage
ptontoa.m, IPv4- and IPv6-compatible functions
ptrdiff_t (type), 64-bit cleanliness
pure static programs, Shared Libraries
putc(), Text I/O
putchar(), Text I/O
puts(), Text I/O
putw(), Misc Functions

Q

qsort(), Blocks
qsort_b(), Blocks, New API Using Blocks
quadword integers, GCC Extensions
quantize(), Aggregates
Quartz graphics, Graphics
queues

dispatch, Queues, Queues

priority, Simple-Lifetime Operations

QuietLog(), QuietLog
quietlog.m, QuietLog

R

race conditions, Race conditions, Atomic operations, fork, Synchronization
raise(), Handling a signal
raise-classic.m, Classic exception handling
raise-native.m, Native exception handling
ranlib command, Static Libraries
read barrier, Strong and weak references
read position, Writing to a file
read(), Reading from a file
read/write locks (threads), For the More Curious: Read/Write Locks
readir(), The opendir() Family
readlink(), Symbolic links
readonly (property attribute), Properties
readstring.m, Reading from a file
readtime.d, Arrays
readv(), Scatter / Gather I/O
readvecstring.m, Scatter / Gather I/O
readwrite (property attribute), Properties
real user ID, File permissions
realloc(), Dynamic Memory Allocation, realloc()
reallocf(), Dynamic Memory Allocation, realloc()
reaping, Parent and Child Lifetimes
record-oriented storage, Inside the Central Loop
recursive inclusion, File inclusion
recvfrom(), For the More Curious: Datagrams
Reddit, Grand Central Dispatch
reentrancy, Reentrancy, Synchronization
reentrant functions, Reentrancy
reference counts, external, External reference counts
reference URLs, File references
regular expressions, ASL queries, Basic API
remote debugging, Handy Tricks
RemoverAppDelegate.h, Edit the nib file adding NSTreeController
removexattr(), Extended attributes
removing files, Removing Files
rename(), Other random calls
renice command, Process Scheduling
reportException:, Subclassing NSApplication to catch exceptions
@required directive, Protocols
required_argument, getopt_long()
require_noerr, AssertMacros.h
resident set, Virtual Memory
resolve.m, Domain Name Lookup
resolving net services, Make ChatterClient browse for servers
resource forks, Resource forks
resource limits, Resource limits, For the More Curious: Core Files, fork
retain (property attribute), Properties
retain cycles, How to use it

with blocks, Block Retain Cycles

reusing network addresses, bind
rewind(), Positioning
rewinddir(), The opendir() Family
right shift (bitwise operator), Bitwise operators
Ritchie, Dennis, Mac OS X: Built to Evolve
rlimit (struct), Resource limits
RLIMIT_* constants, Resource limits
rmdir(), Creation and destruction
rock, hiding data under, C callbacks in Objective-C
rot-13 encryption, For the More Curious: Memory-Mapped Files
RTLD_* constants, dlopen()
rubber ducking, Debugging Techniques
run loop observers, For the More Curious: Run Loop Observers
runloop source, FSEvents API
runloops, CFRunLoop

and kqueues, kqueues and Runloops

runtime, New Objective-C runtime
runtime environment variables, For the More Curious: Runtime Environment Variables
rusage (struct), Parent and Child Lifetimes

S

safe global initialization, Safe Global Initialization
sample command, sample
save temporary files, Handy Flags
saved set user ID, File permissions
scandir_b(), New API Using Blocks
scanf(), Formatted I/O
scatter/gather I/O, Scatter / Gather I/O
SCDynamicStoreCopyKeyList(), Basic API
SCDynamicStoreCopyMultiple(), Basic API
SCDynamicStoreCopyValue(), Basic API
SCDynamicStoreCreate(), Basic API
SCDynamicStoreSetNotificationKeys(), Basic API
scf-dump.m, Seeing all values
SCFMonitorAppDelegate.h, Creating SCFMonitor
SCFMonitorAppDelegate.m, Creating SCFMonitor
scheduler, Process Scheduling
scoped variables, Scoped variables
scripts (DTrace), Scripts, The C preprocessor
sc_usage command, fs_usage and sc_usage
SecAccessCopyACLList(), Keychain Access
SecACLCopySimpleContents(), Keychain Access
SecKeychainAddGenericPassword(), Convenience Functions
SecKeychainAddInternetPassword(), Convenience Functions
SecKeychainAttributeList, Items and Attribute Lists
SecKeychainCopyDefault(), Getting Specific Keychains
SecKeychainFindGenericPassword(), Convenience Functions
SecKeychainFindInternetPassword(), Convenience Functions
SecKeychainItemCopyAccess(), Keychain Access
SecKeychainItemCopyContent(), Reading Data From an Item
SecKeychainItemDelete(), Editing the Keychain
SecKeychainItemModifyContent(), Editing the Keychain
SecKeychainItemSetAccess(), Keychain Access
SecKeychainOpen(), Getting Specific Keychains
SecKeychainSearchCopyNext(), Searching for Items
SecKeychainSearchCreateFromAttributes(), Searching for Items
SecKeychainSearchRef, Searching for Items
SecKeychainSetUserInteractionAllowed(), Convenience Functions
SecTrustedApplicationCopyData(), Keychain Access
SecTrustedApplicationRef, Keychain Access
Security Framework NULL arguments, Searching for Items
seekdir(), The opendir() Family
seeking, Changing the read/write offset, Positioning
SEEK_* constants, Changing the read/write offset
select(), Multiplexing connections, CFRunLoop, kqueue(), kqueues for Signal Handling, kqueues for Socket Monitoring, Multithreading

consuming and modifying fd_sets, Multiplexing connections

self->, Scoped variables
self-signed certificates, Code Signing
semaphores, Semaphores
sendto(), For the More Curious: Datagrams
sentinel.m, Variable argument lists
set optimization levels, Handy Flags
set-uid, File permissions
SetFile command, Other random programs
setjmp(), setjmp, longjmp, Reentrancy, Classic exception handling
setrlimit(), Resource limits, For the More Curious: Core Files
setsockopt(), bind
setxattr(), Extended attributes
set_target_queue(), Queues
shared libraries, Shared Libraries, Shared Libraries and dlopen
shell pipes, Command-Line Programs
shift (bitwise operators), Bitwise operators
SIGABRT, For the More Curious: Assertions
sigaction(), Signals, Handling signals with sigaction(), kqueues for Signal Handling
sigaddset(), Blocking signals
SIGALRM, Signals
SIGBUS, Signals
SIGCHLD, Signals, Parent and Child Lifetimes
sigdelset(), Blocking signals
sigemptyset(), Blocking signals
sigfillset(), Blocking signals
SIGFPE, Semaphores
SIGHUP, Signals
sigismember(), Blocking signals
sigjmp_buf, Reentrancy
SIGKILL, Handling a signal
siglongjmp(), Reentrancy
signal masks, Blocking signals
signal(), Signals, Handling a signal, kqueues for Signal Handling
signals, Signals

blocking, Blocking signals

delivery, Signals, Blocking signals

and GCD, Signal sources

handling, Handling a signal, kqueues for Signal Handling

kqueue, kqueues for Signal Handling

and race conditions, Race conditions

and reentrancy, Reentrancy

signal(), Handling a signal

WIFSIGNALED, Parent and Child Lifetimes

WTERMSIG, Parent and Child Lifetimes

sigpending(), Blocking signals
sigprocmask(), Blocking signals
SIGSEGV, Signals
sigsetjmp(), Reentrancy
sigset_t (type), Blocking signals
SIGSTOP, Handling a signal
SIGTERM, Reentrancy
SIGUSR, Handling a signal
SIGUSR1, Handling a signal
SIGUSR2, Handling a signal
sigwatcher.m, kqueues for Signal Handling
SIG_* constants, Handling a signal
sig_atomic_t (type), Race conditions
SIG_BLOCK, Blocking signals
SIG_IGN, kqueues for Signal Handling
SIG_SETMASK, Blocking signals
sig_t (type), Handling a signal
SIG_UNBLOCK, Blocking signals
simple-lifetime operations, Simple-Lifetime Operations
simpleclient.m, connect
SimpleMessage.m, Bundles in Cocoa
simpleserver.m, accept
size command, Program Memory Model
sizeof, 64-bit cleanliness
sizeof(), New Objective-C runtime, malloc()
sizeprinter.c, Fat binaries from the command line
size_t (type), Varargs gotchas, 64-bit cleanliness
sleep, Disk I/O and sleep
slinging bits, Bitwise operations
SnarfOperation.h, SnarfOperation
SnarfOperation.m, SnarfOperation
Snow Leopard (see Mac OS X 10.6)
snowman, Block Syntax, Grand Central Dispatch
snprintf(), Formatted I/O
sockaddr (struct), Sockets Address Data Structures
sockaddr_in (struct), IPv4 address structures
sockaddr_in6 (struct), IPv6 address structures
sockaddr_storage (struct), IPv6 address structures
socket(), Server coding
sockets, Network Programming With Sockets

datagram, For the More Curious: Datagrams

non-blocking, Multiplexing connections

structure of, Sockets Address Data Structures

SOCK_DGRAM, For the More Curious: Datagrams
SOCK_STREAM, Server coding
soft limit, Resource limits
soup or salad, Bitwise operators
SO_REUSEADDR, bind
SPoD, WordCounter
Spotlight, fsevents
sprintf(), Formatted I/O
spurious wakeups, Condition variables
SRV record, Bonjour
SSE, malloc()
stack, Program Memory Model
stack allocation, Program Memory Model
stack traces, A sample GDB session, Stack Traces, Looking at memory leaks
stackdepth, Built-in variables
standard directories, Finding Standard Directories
standard I/O streams, Command-Line Programs, Opening a file, Opening files
stat (struct), stat()
stat(), stat(), Disk I/O and sleep, Differences between HFS+ and UFS, Creating the stream
static assertions, Static assertions
static libraries, Static Libraries
status.m, Parent and Child Lifetimes
STDERR_FILENO, Opening a file
STDIN_FILENO, Opening a file
stdio.h, Opening files
STDOUT_FILENO, Opening a file
stochastic profiling, Stochastic profiling
strace command, dtruss
stream sockets, Server coding
strerror(), errno
stringization, Stringization and concatenation
stringization.m, Stringization and concatenation
strip command, For the More Curious: Stripping
strlen(), CPU
__strong, Strong and weak references
strong references, Strong and weak references
struct attrlist, Describing the attributes
struct dirent, The opendir() Family
struct hostent, Domain Name Lookup
struct in_addr, IPv4 address structures
struct iovec, Scatter / Gather I/O
struct kevent, Events
struct kevent64_s, Events
struct option, getopt_long()
struct passwd, Users and groups
struct rlimit, Resource limits
struct rusage, Parent and Child Lifetimes
struct sockaddr, Sockets Address Data Structures
struct sockaddr_in, IPv4 address structures
struct sockaddr_in6, IPv6 address structures
struct sockaddr_storage, IPv6 address structures
struct stat, stat()
struct timespec, Registering and Handling Events, Condition variables, Time, Time, Time
struct timeval, Multiplexing connections
struct tm, Time, Time, Time
suid, File permissions
sum(), Aggregates
superblocks, Inside The File System
swapping, Virtual Memory
symbol resolution, Shared Libraries
symbolic links, Changing Behavior By Name, Symbolic links, Symbolic links
symbols

debugging, Debugging, For the More Curious: Stripping

names of, Static Libraries

preprocessor, Preprocessor symbols, Predefined symbols, Fat binaries from the command line

unmangling, Name Mangling

symlink(), API for links
sync(), Buffered I/O vs. Unbuffered I/O
@synchronized, Objective-C @synchronized blocks
synchronizing tasks, For the More Curious: Synchronizing Tasks
syntax check, Warnings
@synthesize directive, Properties
syscall provider, syscall provider
syscalls.d, Built-in variables
sysctl(), Other random calls
sysexits.h, Parsing the Command Line
syslog(), Logging, syslog()
syslog.m, syslog()
syslogd, ASL
system call tracing, dtruss, fs_usage and sc_usage
system calls, Unbuffered I/O
System Configuration Framework, The System Configuration Framework
system headers, File inclusion
system(), Convenience Functions

security issues with, fork

S_I* constants, File permissions
S_IS* macros, stat()

T

tail command, Disk I/O and sleep
tar command, Resource forks
target queue, Queues, Queues
TCP sockets, Network Programming With Sockets, Server coding
telldir(), The opendir() Family
telnet command, accept, Condition variables
tempfun.m, Temporary Files
temporal milestones, Time, Time, Time
temporary directory, Finding Standard Directories
temporary files, Removing Files, Temporary Files
text I/O, Text I/O
text line endings, Opening files
text segment, Program Memory Model
this->, Scoped variables
Thompson, Ken, Mac OS X: Built to Evolve, creat()
thread local storage, For the More Curious: Thread Local Storage
thread safety, Current working directory
thread-scoped Dtrace variables, Scoped variables
threads (see multithreading)
@throw, Native exception handling
Tiger (see Mac OS X 10.4)
tilde expansion, NSString path utilities
__TIME__, Predefined symbols
time command, time, Precise Timing with mach_absolute_time(), Metadata in batches
Time Machine, fseventsd
Time Profiler instrument, Time Profiler
time-slice, Multiprocessing
timer provider, profile provider
timer sources, Timer sources
timespec (struct), Registering and Handling Events, Condition variables, Time, Time, Time
timestamp, Built-in variables
timeval (struct), Multiplexing connections
timing, precise, Precise Timing with mach_absolute_time()
TLS, For the More Curious: Thread Local Storage
tm (struct), Time, Time, Time
toll-free bridging, Basic API
top command, Memory leaks, CPU, top
touch command, Users and groups
Trace Symbol instrument, Symbol Trace and DTrace
trace(), Functions
trace-ls.d, Pragmas
tracing, dynamic (see DTrace)
trampoline, C callbacks in Objective-C
treat warnings as errors, Handy Flags
trunc(), Aggregate-related functions
truncate(), Other random calls
truncating files, Removing Files
truss command, dtruss
@try, Native exception handling
tv_nsec, Registering and Handling Events
tv_sec, Registering and Handling Events
two-level namespaces, Shared Libraries
TXT records, For the More Curious: TXT Records
typographical conventions, Typographical Conventions

U

Ubuntu, Frameworks
UDP sockets, For the More Curious: Datagrams
UFS, Differences between HFS+ and UFS
uid.m, File permissions
uintptr_t (type), 64-bit cleanliness
ulimit command, For the More Curious: Core Files
umask command, File permissions, Creation and destruction, fork, exec
umask(), File permissions
unbuffered I/O, Unbuffered I/O
ungetc(), Text I/O
uninitialized data segment, Program Memory Model
uninverted call trees, Time Profiler
universal binaries, Fat binaries from the command line
Unix, history of, Mac OS X: Built to Evolve
unlink(), Removing Files, kqueues for File System Monitoring
unmangling symbols, Name Mangling
unset command, Memory corruption
unsetenv command, Memory corruption
user defaults, User Defaults
user ID, Users and groups, fork
userData, C callbacks in Objective-C
users and groups, Users and groups
ustack(), Functions
utimes(), stat()
UUID, Events, Miscellaneous calls, NSProcessInfo

V

vararg.m, Variable argument lists
variable argument lists, Variable argument lists
variable capture, Accessing Enclosing Scope
variable length automatic arrays, GCC Extensions
variables

conditional, Condition variables

constant, Const and volatile variables

DTrace, Variables

environment, Looking at the Environment, For the More Curious: Runtime Environment Variables, Debugging, exec

read-only, Const and volatile variables

runtime environment, For the More Curious: Runtime Environment Variables

scoped, Scoped variables

volatile, Const and volatile variables, setjmp, longjmp

__block, __block variables

variadic functions, Variable argument lists
variadic macros, Variadic macros
##__VA_ARGS__, Variadic macros
va_arg(), Variable argument lists
__VA_ARGS__, Variadic macros
va_end(), Variable argument lists
va_list, Variable argument lists
va_start(), Variable argument lists
vectorization, Vectorization
virtual memory, Virtual Memory, Disk
virtual set, Virtual Memory
visibility, Visibility
vm_stat command, vm_stat
vnode, kqueues for File System Monitoring
volatile, Const and volatile variables, setjmp, longjmp
volatile void, Subclassing NSApplication to catch exceptions
volume mounting notification, The FSEvents callback
VoodooPad application, Debugging Techniques
vprintf(), Variable argument lists, QuietLog
vsnprintf(), Variable argument lists
vtimestamp, Built-in variables

W

wait(), Parent and Child Lifetimes
wait3(), Parent and Child Lifetimes
wait4(), Parent and Child Lifetimes
waitpid(), Parent and Child Lifetimes, Creating threads
warning hygiene, Warnings
warning.m, Warnings
warnings

compiler, Warnings

format, Variable argument lists

-Wconversion compiler flag, 64-bit cleanliness
WCOREDUMP(), Parent and Child Lifetimes
weak references, How it works, Strong and weak references, The "new" collection classes
web server, Condition variables
webserv-thread.m, Condition variables
weirdness, Mac OS X Specific Weirdness
WEXITSTATUS(), Parent and Child Lifetimes
-Wformat, Variable argument lists
wheel group, File permissions
WIFEXITED(), Parent and Child Lifetimes
WIFSIGNALED(), Parent and Child Lifetimes
-Wno-protocol compiler flag, Even More Compiler Flags
-Wno-unused, Warnings
WNOHANG, Parent and Child Lifetimes
WordCounterAppDelegate.h, WordCounter
WordCounterAppDelegate.m, WordCounter
write barrier, Strong and weak references
write position, Writing to a file
write(), Writing to a file
writestring.m, Writing to a file
writev(), Scatter / Gather I/O
writevecstring.m, Scatter / Gather I/O
WTERMSIG(), Parent and Child Lifetimes
-Wundeclared-selector compiler flag, Even More Compiler Flags
WUNTRACED, Parent and Child Lifetimes

X

__x86_64__, Fat binaries from the command line
Xcode

creating frameworks, Frameworks

enabling garbage collection, How to use it

Foundation Tool template, The Basic Program

framework public header, Frameworks

gdb command-line in, Using GDB from the Command Line

per-file compiler flags, Warnings

preprocessor macros, Preprocessor symbols

preprocessor output, Handy Flags

profiling from, Instruments

symbol definition, Opening a file

universal binaries, Fat binaries in Xcode

using static libraries, Static Libraries

XOR (bitwise operator), Bitwise operators

Y

Y500,000K problem, Events

Z

zero-filled page zero, Program Memory Model
Zeroconf, Bonjour
zombie processes, Parent and Child Lifetimes
%zu format specifier, Varargs gotchas

More From Big Nerd Ranch...

Big Nerd Ranch exists to broaden the minds of our students and the businesses of our clients. Whether we are training talented individuals or developing a company's mobile strategy, our core philosophy is integral to everything we do.

The Big Nerd Story

The brainchild of CEO Aaron Hillegass, Big Nerd Ranch has hosted more than 2,000 students at the Ranch since its inception in 2001. Over the past ten years, we have had the opportunity to work with some of the biggest companies in the world such as Apple, Samsung, Nokia, Google, AOL, Los Alamos National Laboratory and Adobe, helping them realize their programming goals. Our team of software engineers are among the brightest in the business and it shows in our work. We have developed dozens of innovative and flexible solutions for our clients.

The Story Behind The Hat

Back in 2001, Big Nerd Ranch founder, Aaron Hillegass, showed up at WWDC (World Wide Developers Conference) to promote the Big Nerd Ranch brand. Without the money to buy an expensive booth, Aaron donned a ten-gallon cowboy hat to draw attention while passing out Big Nerd literature to prospective students and clients. A week later, we landed our first big client and the cowboy hat has been synonymous with the Big Nerd brand ever since. Already easily recognizable at 6'5, Aaron can be spotted wearing his cowboy hat at speaking engagements and conferences all over the world.

The New Ranch - Opening 2012

In the continuing effort to perfect the student experience, Big Nerd Ranch is building its own facility. Located just 20 minutes from the Atlanta airport, the new Ranch will be a monastic learning center that encompasses Aaron Hillegass' vision for technical education featuring a state-of-the-art classroom, fine dining and exercise facilities.

Training: Achieve Nerdvana

Since 2001, Big Nerd Ranch has offered intensive computer programming courses taught by our expert instructors in a retreat environment. It is at our Ranch where we think our students flourish. Classes, accommodations and dining all take place within the same building, freeing you to learn, code and discuss with your programming peers and instructors. At Big Nerd Ranch, we take care of the details; your only job is to learn.

Our Teachers

Our teachers are leaders in their respective fields. They offer deep understanding of the technologies they teach, as well as a broad spectrum of development experience, allowing them to address the concerns you encounter as a developer. Big Nerd Ranch instructors provide the necessary combination of knowledge and outstanding teaching skills, enabling our students to leave the Ranch with a vastly improved set of skills.

The Big Nerd Way

We have developed “The Big Nerd Ranch Way”. This methodology guides the development and presentation of our classes. The style is casual but focused, with brief lectures followed by hands-on exercises designed to give you immediate, relevant understanding of each piece of the technology you are learning.

Your Stay at the Ranch

One fee covers tuition, meals, lodging and transportation to and from the airport. At the Big Nerd Ranch, we remove the distractions inherent in standard corporate training by offering classes in quiet, comfortable settings in Atlanta, Georgia and Frankfurt, Germany.

Available Classes

Advanced Mac OS X

Android

Beginning Cocoa

Beginning iOS (iPhone/iPad)

Beginning Ruby on Rails

Cocoa Commuter Class in Spanish

Cocoa I

Cocoa II

Commuter iOS Class

Django

iOS (iPhone/iPad)

OpenGL

Python Mastery

Ruby on Rails I

Ruby on Rails II

On-site Training: Our Nerds, Your Location

Through our on-site training program you can affordably and conveniently have our renowned classes come to you. Our expert instructors will help your team advance through nerd-based instructional support that is fresh, engaging and allows for unencumbered hands-on learning.

Clients around the globe have praised our on-site instruction for some of the following reasons:

Flexibility

Classes can be booked when the timing is right for your team.

We can tailor our existing syllabi to ensure our training meets your organization's unique needs.

Post-class mentorship is available to support your team as they work on especially challenging projects.

Affordability

No need for planes, trains and automobiles for all of your staff; our Nerds come to you.

Train up to 22 students at a significant discount over open-enrollment training.

Nerd Know-how

Our instructors are highly practiced in both teaching and programming. They move beyond theory by bringing their real-life experiences to your team.

On-site training includes post-class access to our Nerds, our extensive Alumni Network, and our Big Nerd Ranch Forums. Learning support doesn't end just because your class does.

For your on-site training, we provide an instructor, all Big Nerd Ranch copyrighted class materials, gifts, certificates of completion and access to our extensive Alumni Network. You'll provide the classroom set up, computers and related devices for all students, a projector and a screen.

Consulting: Achieve Nerdvana In-house & On-site

When you contract with Big Nerd Ranch, we'll work directly with you to turn your needs into a full-fledged desktop and/or mobile solution. Our developers and designers have consistently created some of the iPhone App Store's most intriguing applications.

Management Philosophy

Big Nerd Ranch holistically manages every client relationship. Our goal is to communicate and educate our clients from project initiation to completion, while ultimately helping them gain a competitive advantage in their niche marketplace.

Project Strategy

We take a detail-oriented approach to all of our project estimations. We'll work with you to define a strategy, specify product offerings and then build them into software that stands alone.

Our Process

Our consulting process is broken down into three distinct phases: Requirements, Execution and Monitoring/Controlling. Bring your business case to us and we'll develop a plan for a user interface and database design. From there, we'll develop a quote and begin the design and implementation process. Our Nerds will perform many tests, including debugging and performance tuning, ensuring the app does what you want it to do. Finally, we'll beta test your app and get it ready for submission and deployment in the iTunes store and/or the Android Market. Once your app is finished, the Nerds will work with you on subsequent version updates and can even help with the marketing of your app.

Client Testimonials

“tops has worked closely with Big Nerd Ranch for over eight years. Consistently they have delivered high-quality code for our projects; clean and poetic. Thanks to their contributions, we have become a leader in our field.”

Dr. Mark Sanchez
President/Founder
tops Software
topsortho.com

“From the simplest GUI design gig to jobs that plumb the darkest corners of the OS, Big Nerd Ranch should be the first contact in your virtual Rolodex under Mac/ iPhone consulting. It's no exaggeration to say that Aaron Hillegass literally wrote the book on Cocoa programming, and you couldn't possibly do better than to bring his and his team's expertise to bear on your application. I've yet to work with a consulting firm that is as competent and communicative as Big Nerd Ranch. Simply put, these guys deliver.”

Glenn Zelniker
CEO
Z-Systems Audio Engineering
www.z-sys.com

“We turned to Big Nerd Ranch to develop the Teavana concept into an iPhone app. More than just a developer, they partnered with us to make the app better than we could have imagined alone. The final app was bug-free and functioned exactly as expected. I would definitely recommend Big Nerd Ranch and can't speak highly enough about their work.”

Jay Allen
VP of Ecommerce
Teavana Corporation
www.teavana.com

Software: Finely-crafted Applications

Big Nerd Ranch is a leading developer of downloadable mobile and desktop Mac applications. Several of our most intriguing iPhone and desktop apps are available for purchase in the iTunes store.

Mobile Applications

Smartphones have started to take over the mobile phone market. Since the inception of the iPhone, we have created dozens of apps for our clients and now have a roster of our own applications including games, utilities, music and educational apps. As an ever-evolving frontier of technology, Big Nerd Ranch is committed to staying ahead of the curve.

Mobile Apps

The world has gone mobile. If your company doesn't have a mobile application, you are behind the curve. As of early 2011, the iTunes app store has grown to nearly 400,000 apps and the Android market has climbed to more than 250,000 applications. Google has unveiled its Android platform with an app store of its own and dozens of smartphone manufacturers have announced Android-powered devices. RIM has launched App World, Palm has its Palm Store, Nokia launched Ovi (its online store) and Microsoft has unveiled Windows Marketplace.

While still leading the way, the iOS market has put up some staggering statistics:

Total iOS app store downloads: 10.3 billion

iPhone apps are being downloaded at a rate of 30 million per day.

As of early 2011, when the app store hit 10 billion downloads, it did so in half the time (31 months versus 67 months) that it took for songs in the iTunes store to hit the same mark.

The average number of apps downloaded for iPhone/iPad/iPod touch is currently at more than 60.

Books: The Big Nerd Ranch Guide Series

At Big Nerd Ranch, our philosophy and our passion for education extends to our books. Visit www.bignerdranch.com/books for Big Nerd Ranch books currently available.

Contact the Big Nerd Ranch

Visit us online at www.bignerdranch.com/.

For a Big Nerd Ranch on-site training proposal, please contact us at 404.478.9005.