Sei sulla pagina 1di 92

Mac OS X App Programming Guide

General

2011-06-27

Apple Inc. 2011 Apple Inc. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means, mechanical, electronic, photocopying, recording, or otherwise, without prior written permission of Apple Inc., with the following exceptions: Any person is hereby authorized to store documentation on a single computer for personal use only and to print copies of documentation for personal use provided that the documentation contains Apples copyright notice. The Apple logo is a trademark of Apple Inc. No licenses, express or implied, are granted with respect to any of the technology described in this document. Apple retains all intellectual property rights associated with the technology described in this document. This document is intended to assist application developers to develop applications only for Apple-labeled computers. Apple Inc. 1 Infinite Loop Cupertino, CA 95014 408-996-1010 .Mac is a registered service mark of Apple Inc. App Store is a service mark of Apple Inc. Apple, the Apple logo, Bonjour, Cocoa, eMac, Finder, Instruments, iPhoto, iTunes, Keychain, Mac, Mac OS, Macintosh, Objective-C, QuickDraw, Sand, Spotlight, Time Machine, and Xcode are trademarks of Apple Inc., registered in the United States and other countries. IOS is a trademark or registered trademark of Cisco in the U.S. and other countries and is used under license. OpenGL is a registered trademark of Silicon Graphics, Inc. UNIX is a registered trademark of The Open Group
Even though Apple has reviewed this document, APPLE MAKES NO WARRANTY OR REPRESENTATION, EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS DOCUMENT, ITS QUALITY, ACCURACY, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. AS A RESULT, THIS DOCUMENT IS PROVIDED AS IS, AND YOU, THE READER, ARE ASSUMING THE ENTIRE RISK AS TO ITS QUALITY AND ACCURACY. IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR

CONSEQUENTIAL DAMAGES RESULTING FROM ANY DEFECT OR INACCURACY IN THIS DOCUMENT, even if advised of the possibility of such damages. THE WARRANTY AND REMEDIES SET FORTH ABOVE ARE EXCLUSIVE AND IN LIEU OF ALL OTHERS, ORAL OR WRITTEN, EXPRESS OR IMPLIED. No Apple dealer, agent, or employee is authorized to make any modification, extension, or addition to this warranty. Some states do not allow the exclusion or limitation of implied warranties or liability for incidental or consequential damages, so the above limitation or exclusion may not apply to you. This warranty gives you specific legal rights, and you may also have other rights which vary from state to state.

Contents
Introduction

About Mac OS X Application Design 9


At a Glance 9 Cocoa Helps You Create Great Applications for Mac OS X 9 The Document Architecture Enables Document-Based Applications 10 Common Behaviors Make Applications Complete 10 Get It Right: Meet System and App Store Requirements 10 Finish Your Application with Performance Tuning 10 How to Use This Document 11 See Also 11

Chapter 1

The Application Runtime Environment 13


An Environment Designed for Ease of Use 13 A Sophisticated Graphics Environment 14 Low-Level Details of the Runtime Environment 14 Based on UNIX 15 Concurrency and Threading 15 The File System 16 Security 19

Chapter 2

The Core Application Design 23


Fundamental Design Patterns 23 The Application Style Determines the Core Architecture 25 The Core Objects for All Cocoa Applications 27 Additional Core Objects for Multiwindow Applications 30 Shoebox Applications Should Not Use NSDocument 31 The Application Life Cycle 31 The main Function is the Application Entry Point 32 The Applications Main Event Loop Drives Interactions 33 Automatic and Sudden Termination of Applications Improve the User Experience 34 Support the Key Runtime Behaviors in Your Applications 35 Automatic Termination 35 Sudden Termination 36 User Interface Preservation 37 Applications Are Built Using Many Different Pieces 42 The User Interface 42 Event Handling 43 Graphics, Drawing, and Printing 45 Text Handling 45 Implementing the Application Menu Bar 46

3
2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CONTENTS

Xcode Templates Provide the Main Menu 46 Connect Menu Items to Your Code or Your First Responder 47 Chapter 3

Implementing a Document-Based Design 49


Three Key Objects Support Document-Based Applications 49 NSDocumentController Creates and Manages Documents 50 NSDocument Presents and Stores Document Data 51 NSWindowController Manages One Document Window 52 Subclassing Objects in the Document Architecture 53 You Must Subclass NSDocument 53 You May Want to Subclass NSWindowController 56 You Rarely Need to Subclass NSDocumentController 58 Xcode Provides a Document-Based Application Template 58 Supported Document Types Are Declared in the Application Property List 59 Multiple Document Types Use Multiple NSDocument Subclasses 60 The Document Architecture Has Built-in Undo Support 61 Implementing Undo 61 Implementing Partial Undo 62 Managing the Change Count 62 Not Supporting Undo 62 The Document Architecture Supports Robust Error Handling 63 Documents Are Automatically Saved 63 Document Saving Is Asynchronous 64 Users Can Browse Document Versions 65 Windows Are Restored Automatically 65

Chapter 4

Implementing the Full-Screen Experience 67


Full-Screen API in NSApplication 67 Full-Screen API in NSWindow 68 Full-Screen API in NSWindowDelegate Protocol 68

Chapter 5

Supporting Common Application Behaviors 71


You Can Prevent the Automatic Relaunch of Your Application 71 Making Your Application Accessible Enables Many Users 71 Provide User Preferences for Customization 74 Integrate Your App With Spotlight Search 74 Use Services to Increase Your Applications Usefulness 75 Make Use of Resolution Independence 75 Prepare for Fast User Switching 76 Take Advantage of the Dock 77

4
2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CONTENTS

Chapter 6

Build-Time Configuration Details 79


Configuring Your Xcode Project 79 The Information Property List File 80 The Mac OS X Application Bundle 81 Internationalizing Your Application 83

Chapter 7

Tuning for Performance and Responsiveness 85


Speed Up Your Applications Launch Time 85 Delay Initialization Code 85 Simplify Your Main Nib File 86 Minimize Global Variables 86 Minimize File Access at Launch Time 86 Dont Block the Main Thread 87 Decrease Your Applications Code Size 87 Compiler-Level Optimizations 87 Use Core Data for Large Data Sets 88 Eliminate Memory Leaks 89 Dead Strip Your Code 89 Strip Symbol Information 89 Build Fixed-Position Application Code 89

Document Revision History 91

5
2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CONTENTS

6
2011-06-27 | 2011 Apple Inc. All Rights Reserved.

Figures, Tables, and Listings


Chapter 1

The Application Runtime Environment 13


Table 1-1 Table 1-2 Listing 1-1 Key directories for Mac OS X applications 16 Attributes for the Mac OS X file system 19 Getting the path to the Application Support directory 18

Chapter 2

The Core Application Design 23


Figure 2-1 Figure 2-2 Figure 2-3 Figure 2-4 Figure 2-5 Figure 2-6 Figure 2-7 Figure 2-8 Figure 2-9 Table 2-1 Table 2-2 Table 2-3 Listing 2-1 Listing 2-2 Listing 2-3 The Calculator single-window utility application 25 The iPhoto single-window application 26 TextEdit document window 27 Key objects in a single-window application 28 Key objects in a multiwindow document application 30 The main event loop 33 Responder objects targeted by Cocoa for preservation 38 Windows and menus in an application 43 Processing events in the main run loop 44 Fundamental design patterns used by Mac OS X applications 23 The core objects used by all Cocoa applications 28 Additional objects used by multiwindow document applications 31 The main function of a Mac OS X application 32 Returning the main window for a single-window application 41 Providing Cocoa with an already restored window 41

Chapter 3

Implementing a Document-Based Design 49


Figure 3-1 Table 3-1 Relationships among NSDocumentController, NSDocument, and NSWindowController objects 50 Document Architecture Objects and Subclasses 53

Chapter 4

Implementing the Full-Screen Experience 67


Table 4-1 Window delegate methods supporting full-screen mode 69

Chapter 5

Supporting Common Application Behaviors 71


Figure 5-1 Figure 5-2 Universal Access system preference dialog 73 Spotlight extracting metadata 75

7
2011-06-27 | 2011 Apple Inc. All Rights Reserved.

FIGURES, TABLES, AND LISTINGS

Chapter 6

Build-Time Configuration Details 79


Figure 6-1 Figure 6-2 Table 6-1 The information property list editor 80 The Language preference view 84 A typical application bundle 81

Chapter 7

Tuning for Performance and Responsiveness 85


Table 7-1 Compiler optimization options 88

8
2011-06-27 | 2011 Apple Inc. All Rights Reserved.

INTRODUCTION

About Mac OS X Application Design

This document is the starting point for learning how to create Mac OS X applications. It contains fundamental information about the Mac OS X environment and how your applications interact with that environment. It also contains important information about the architecture of Mac OS X applications and tips for designing key parts of your application.

At a Glance
Cocoa is the application environment that unlocks the full power of Mac OS X. Cocoa provides APIs, libraries, and runtimes that help you create fast, exciting applications that automatically inherit the beautiful look and feel of Mac OS X, as well as standard behaviors users expect.

Cocoa Helps You Create Great Applications for Mac OS X


You write applications for Mac OS X using Cocoa, which provides a significant amount of infrastructure for your program. Fundamental design patterns are used throughout Cocoa to enable your application to interface seamlessly with subsystem frameworks, and core application objects provide key behaviors to support simplicity and extensibility in application architecture. Key parts of the Cocoa environment are designed particularly to support ease of use, one of the most important aspects of successful Mac OS X applications. High-quality graphics, including animation, provide immediate visual appeal as well as critical user feedback. In addition, the Mac OS X runtime includes many low-level features, such as preemptive multitasking, protected memory, concurrency, powerful networking and file system access, and security.

At a Glance
2011-06-27 | 2011 Apple Inc. All Rights Reserved.

INTRODUCTION

About Mac OS X Application Design

Relevant Chapters: The Application Environment (page 13) and The Core Application Design (page 23)

The Document Architecture Enables Document-Based Applications


A document-based application is one of the more common types of applications. Documents are data containers that are stored in files. Traditional word processors and spreadsheet applications are examples of document-based applications. One application typically handles multiple documents, each in its own window, and often displays more than one document at a time. Cocoa provides excellent support for this design through a subsystem called the document architecture. Relevant Chapter: Implementing a Document-Based Design (page 49)

Common Behaviors Make Applications Complete


During the design phase of creating your application, you need to think about how to implement certain features that users expect in well-formed Mac OS X applications. Integrating these features into your application architecture can have an impact on the user experience: accessibility, preferences, Spotlight, services, resolution independence, fast user switching, and the Dock. Enabling your application to assume full-screen mode, taking over the entire screen, provides users with a more immersive, cinematic experience and enables them to concentrate fully on their content without distractions. Relevant Chapters: Supporting Common Application Behaviors (page 71) and Implementing the Full-Screen Experience (page 67)

Get It Right: Meet System and App Store Requirements


Configuring your application properly is an important part of the development process. Mac OS X applications use a structured directory called a bundle to manage their code and resource files. And although most of the files are custom and exist to support your application, some are required by the system or the App Store and must be configured properly. The application bundle also contains the resources you need to provide to internationalize your application to support multiple languages. Relevant Chapter: Build-Time Configuration Details (page 79)

Finish Your Application with Performance Tuning


As you develop your application and your project code stabilizes, you can begin performance tuning. Of course, you want your application to launch and respond to the users commands as quickly as possible. A responsive application fits easily into the users workflow and gives an impression of being well crafted. You can improve the performance of your application by speeding up launch time and decreasing your applications code footprint.

10

At a Glance
2011-06-27 | 2011 Apple Inc. All Rights Reserved.

INTRODUCTION

About Mac OS X Application Design

Relevant Chapter: Tuning for Performance and Responsiveness (page 85)

How to Use This Document


This guide introduces you to the most important technologies that go into writing an application. In this guide you will see the whole landscape of what's needed to write one. That is, this guide shows you all the "pieces" you need and how they fit together. There are important aspects of application design that this guide does not cover, such as user interface design. However, this guide includes many links to other documents that provide details about the technologies it introduces, as well as links to tutorials that provide a hands-on approach. In addition, this guide emphasizes certain technologies introduced in Mac OS X v10.7, which provide essential capabilities that set your application apart from older ones and give it remarkable ease of use, bringing some of the best features from iOS to Mac OS X.

See Also
The following documents provide additional information about designing Mac OS X applications, as well as more details about topics covered in this document:

To work through a tutorial showing you how to create a Cocoa application, see Your First Mac App. For information about user interface design enabling you to create effective applications using Mac OS X, see Mac OS X Human Interface Guidelines. For information about the design patterns used in Cocoa, see Cocoa Fundamentals Guide. For a general survey of Mac OS X technologies, see Mac OS X Technology Overview.

How to Use This Document


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

11

INTRODUCTION

About Mac OS X Application Design

12

See Also
2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 1

The Application Runtime Environment

Like all modern operating systems, Mac OS X incorporates the latest technologies for creating powerful and fun-to-use applications. But the technologies by themselves are not enough to make every application great. What sets an application apart from its peers is how it helps the user achieve some tangible goal. After all, users are not going to care what technologies an application uses, as long as it helps them do what they need to do. An application that gets in the users way is going to be forgotten, but one that makes work (or play) easier and more fun is going to be remembered. Cocoa is the application environment that you use to write applications for Mac OS X. In its runtime aspect, Cocoa presents the applications user interface and integrates it tightly with the other components of the operating system. Cocoa also has a development aspect, as described in The Core Application Design (page 23). This chapter describes some of the key parts of the Mac OS X runtime environment that help you create great applications. The items described here are not a complete list but are simply a collection of some of the most commonly used techniques and technologies available for your applications. In particular, this chapter describes some of the important ease-of-use technologies introduced in Mac OS X v10.7. For a more thorough list of technologies available in Mac OS X, see Mac OS X Technology Overview.

An Environment Designed for Ease of Use


Mac OS X strives to provide an environment that is transparent to users and as easy to use as possible. By making hard tasks simple and getting out of the way, the system makes it easier for the user to be creative and spend less time worrying about the steps needed to make the computer work. Of course, simplifying tasks means your application has to do more of the work, but Mac OS X provides help in that respect too. As you design your application, you should think about the tasks that users normally perform and find ways to make them easier. Mac OS X v10.7 supports powerful ease-of-use features and design principles that augment those introduced in earlier system versions. For example:

Users should not have to save their work manually. The document model in Cocoa provides support for saving the users file-based documents without user interaction; see Documents Are Automatically Saved (page 63). Applications should restore the users work environment at login time. Cocoa provides support for archiving the current state of the applications interface (including the state of unsaved documents) and restoring that state at launch time; see User Interface Preservation (page 37). Applications should support automatic termination so that the user never has to quit them. Automatic termination means that when the user closes an applications windows, the application appears to quit but actually just moves to the background quietly. The advantage is that subsequent launches are nearly instant as the application simply moves back to the foreground; see Automatic and Sudden Termination of Applications Improves the User Experience (page 34)

An Environment Designed for Ease of Use


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

13

CHAPTER 1

The Application Runtime Environment

You should consider providing your users with an immersive, full-screen experience by implementing a full-screen version of your user interface. The full-screen experience eliminates outside distractions and allows the user to focus on their content; see Implementing the Full-Screen Experience (page 67). Support trackpad gestures for appropriate actions in your application. Gestures provide simple shortcuts for common tasks and can be used to supplement existing controls and menu commands. Mac OS X provides automatic support for reporting gestures to your application through the normal event-handling mechanism; see Cocoa Event-Handling Guide. Consider minimizing or eliminating the users interactions with the raw file system. Rather than expose the entire file system to the user through the open and save panels, some applications, in the manner of iPhoto and iTunes, can provide a better user experience by presenting the users content in a simplified browser designed specifically for the applications content. Mac OS X uses a well-defined file system structure that allows you to place and find files easily and includes many technologies for accessing those files; see The File System (page 16). For applications that support custom document types, provide a Quick Look plug-in so that users can view your documents from outside of your application; see Quick Look Programming Guide.

All of the preceding features are supported by Cocoa and can be incorporated with relatively little effort.

A Sophisticated Graphics Environment


High-quality graphics and animation make your application look great and can convey a lot of information to the user. Animations in particular are a great way to provide feedback about changes to your user interface. So as you design your application, keep the following ideas in mind:

Use animations to provide feedback and convey changes. Cocoa provides mechanisms for creating sophisticated animations quickly in both the AppKit and Core Animation frameworks. For information about creating view-based animations, see Cocoa Drawing Guide. For information about using Core Animation to create your animations, see Core Animation Programming Guide. Include high-resolution versions of your art and graphics. Mac OS X automatically loads high-resolution image resources when an application runs on a screen whose scaling factor is greater than 1.0. Including such image resources makes your applications graphics look even sharper and crisper on those higher-resolution screens.

For information about the graphics technologies available in Mac OS X, see Graphics and Multimedia Technologies in Mac OS X Technology Overview.

Low-Level Details of the Runtime Environment


When you are ready to begin writing actual code, there are a lot of technologies available to make your life easier. Like many modern operating systems, Mac OS X supports all of the basic features such as memory management, file management, networking, and concurrency that you need to write your code. In some cases, though, Mac OS X also provides more sophisticated services (or specific coding conventions) that, when followed, can make writing your code even easier.

14

A Sophisticated Graphics Environment


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 1

The Application Runtime Environment

Based on UNIX
Mac OS X is powered by a 64-bit Mach kernel, which manages processor resources, memory, and other low-level behaviors. On top of the kernel sits a modified version of the Berkeley Software Distribution (BSD) operating system, which provides interfaces that applications can use to interact with the lower-level system. This combination of Mach and BSD provides the following system-level support for your applications:

Preemptive multitaskingAll processes share the CPU efficiently. The kernel schedules processes in a way that ensures they all receive the time they need to run. Even background applications continue to receive CPU time to execute ongoing tasks. Protected memoryEach process runs in its own protected memory space, which prevents processes from accidentally interfering with each other. (Applications can share part of their memory space to implement fast interprocess communication but take responsibility for synchronizing and locking that memory appropriately.) Virtual memory64-bit applications have a virtual address space of approximately 18 exabytes (18 billion billion bytes). (If you create a 32-bit application, the amount of virtual memory is only 4 GB.) When an applications memory usage exceeds the amount of free physical memory, the system transparently writes pages to disk to make more room. Written out pages remain on disk until they are needed in memory again or the application exits. Networking and BonjourMac OS X provides support for the standard networking protocols and services in use today. BSD sockets provide the low-level communication mechanism for applications, but higher-level interfaces also exist. Bonjour simplifies the user networking experience by providing a dynamic way to advertise and connect to network services over TCP/IP.

For detailed information about the underlying environment of Mac OS X, see Kernel and Device Drivers Layer in Mac OS X Technology Overview.

Concurrency and Threading


Each process starts off with a single thread of execution and can create more threads as needed. Although you can create threads directly using POSIX and other higher-level interfaces, for most types of work it is better to create them indirectly using block objects with Grand Central Dispatch (GCD) or operation objects, a Cocoa concurrency technology implemented by the NSOperation class. GCD and operation objects are an alternative to raw threads that simplify or eliminate many of the problems normally associated with threaded programming, such as synchronization and locking. Specifically, they define an asynchronous programming model in which you specify only the work to be performed and the order in which you want it performed. The system then handles the tedious work required to schedule the necessary threads and execute your tasks as efficiently as possible on the current hardware. You should not use GCD or operations for work requiring time-sensitive data processing (such as audio or video playback), but you can use them for most other types of tasks. For more information on using GCD and operation objects to implement concurrency in your applications, see Concurrency Programming Guide.

Low-Level Details of the Runtime Environment


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

15

CHAPTER 1

The Application Runtime Environment

The File System


The file system in Mac OS X is structured to provide a better experience for users. Rather than exposing the entire file system to the user, the Finder hides any files and directories that an average user should not need to use, such as the contents of low-level UNIX directories. This is done to provide a simpler interface for the end user (and only in places like the Finder and the open and save panels). Applications can still access any files and directories for which they have valid permissions, regardless of whether they are hidden by the Finder. When creating applications, you should understand and follow the conventions associated with the Mac OS X file system. Knowing where to put files and how to get information out of the file system ensures a better user experience.

A Few Important Application Directories


The Mac OS X file system is organized in a way that groups related files and data together in specific places. Every file in the file system has its place and applications need to know where to put the files they create. This is especially important if you are distributing your application through the App Store, which expects you to put your applications data files in specific directories. Table 1-1 lists the directories with which applications commonly interact. Directories preceded with a tilde (~) character indicate a path inside the home directory, which can be either the users home directory or the applications container directory if the application is in a sandbox (as described in The App Sandbox (page 19)). Because the actual paths can differ based on the conditions, use the URLsForDirectory:inDomains: method of the NSFileManager class to retrieve the actual directory path. You can then add any custom directory and filename information to the returned URL object to complete the path. Table 1-1 Directory Applications directory Key directories for Mac OS X applications Description This is the installation directory for your application bundle. The path for the global applications directory is /Applications but each user directory may have a local applications directory containing user-specific applications. Regardless, you should not need to use this path directly. To access resources inside your application bundle, use an NSBundle object instead. For more information about the structure of your application bundle and how you locate resources, see The Mac OS X Application Bundle (page 81). Home directory The configuration of your application determines the location of the home directory seen by your application: For applications running in a sandbox in Mac OS X 10.7 and later, the home directory is the applications container directory. For more information about the container directory, see The Container Directory Is Your Applications Home Directory (page 20).

For applications running outside of a sandbox (including those running in versions of Mac OS X before 10.7), the home directory is the user-specific subdirectory of /Users that contains the users files.

To retrieve the path to the home directory, use the NSHomeDirectory function.

16

Low-Level Details of the Runtime Environment


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 1

The Application Runtime Environment

Directory

Description

Library directory The Library directory is the top-level directory for storing private application-related data and preferences. There are several Library directories scattered throughout the system but you should always use the one located inside the current home directory. Do not store files directly at the top-level of the Library directory. Instead, store them in one of the specific subdirectories described in this table. In Mac OS X 10.7 and later, the Finder hides the Library directory in the users home folder by default. Therefore, you should never store files in this directory that you want the user to access. To get the path to this directory use the NSLibraryDirectory search path key with the NSUserDomainMask domain. Application Support directory The Application Support directory is where your application stores any type of file that supports the application but is not required for the application to run, such as document templates or configuration files. The files should be application-specific but should never store user data. This directory is located inside the Library directory. Never store files at the top level of this directory: Always put them in a subdirectory named for your application or company. If the resources apply to all users on the system, such as document templates, place them in /Library/Application Support. To get the path to this directory use the NSApplicationSupportDirectory search path key with the NSLocalDomainMask domain. If the resources are user-specific, such as workspace configuration files, place them in the current users ~/Library/Application Support directory. To get the path to this directory use the NSApplicationSupportDirectory search path key with the NSUserDomainMask domain. Caches directory The Caches directory is where you store cache files and other temporary data that your application can re-create as needed. This directory is located inside the Library directory. Never store files at the top level of this directory: Always put them in a subdirectory named for your application or company. Your application is responsible for cleaning out cache data files when they are no longer needed. The system does not delete files from this directory. To get the path to this directory use the NSCachesDirectory search path key with the NSUserDomainMask domain. Movies directory The Movies directory contains the users video files. To get the path to this directory use the NSMoviesDirectory search path key with the NSUserDomainMask domain. Music directory The Music directory contains the users music and audio files. To get the path to this directory use the NSMusicDirectory search path key with the NSUserDomainMask domain. Pictures directory The Pictures directory contains the users images and photos. To get the path to this directory use the NSPicturesDirectory search path key with the NSUserDomainMask domain.

Low-Level Details of the Runtime Environment


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

17

CHAPTER 1

The Application Runtime Environment

Directory Temporary directory

Description The Temporary directory is where you store files that do not need to persist between launches of your application. You normally use this directory for scratch files or other types of short-lived data files that are not related to your applications persistent data. This directory is typically hidden from the user. Your application should remove files from this directory as soon as it is done with them. The system may also purge lingering files from this directory at system startup. To get the path to this directory use the NSTemporaryDirectory function.

Listing 1-1 shows an example of how to retrieve the base path to the Application Support directory and then append a custom application directory to it. Listing 1-1 Getting the path to the Application Support directory

NSFileManager* fileManager = [NSFileManager defaultManager]; NSURL* appSupportDir = nil; NSArray *urls = [fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask]; if ([paths count] > 0) { appSupportDir = [[urls objectAtIndex:0] URLByAppendingPathComponent:@"com.example.MyApp"]; }

For more information about how to access files in well known system directories, see Low-Level File Management Programming Topics.

Coordinating File Access with Other Processes


In Mac OS X, other processes may have access to the same files that your application does. Therefore, when working with files, you should use the file coordination interfaces introduced in Mac OS X v10.7 to be notified when other processes (including the Finder) attempt to read or modify files your application is currently using. The file coordination APIs allow you to assert ownership over files and directories that your application cares about. Any time another process attempts to touch one of those items, your application is given a chance to respond. For example, when an application attempts to read the contents of a document your application is editing, you can write unsaved changes to disk before the other process is allowed to do its reading. For information about how to use the file coordination interfaces, see Foundation Release Notes.

Interacting with the File System


Disks in Macintosh computers are formatted using the HFS+ file system by default. However, Macintosh computers can interact with disks that use other formats so you should never code specifically to any one file system. Table 1-2 lists some of the basic file system attributes you may need to consider in your application and how you should handle them.

18

Low-Level Details of the Runtime Environment


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 1

The Application Runtime Environment

Table 1-2 Attribute

Attributes for the Mac OS X file system Description The HFS+ file system is case-insensitive but also case-preserving. Therefore, when specifying filenames and directories in your code, it is best to assume case-sensitivity. Construct paths using the methods of the NSURL and NSString classes. The NSURL class is preferred for path construction because of its ability to specify not only paths in the local file system but paths to network resources. Many file-related attributes can be retrieved using the getResourceValue: forKey:error: method of the NSURL class. You can also use an NSFileManager object to retrieve many file-related attributes. File permissions are managed using access control lists (ACLs) and BSD permissions. The system uses ACLs whenever possible to specify precise permissions for files and directories, but it falls back to using BSD permissions when no ACLs are specified. By default, any files your application creates are owned by the current user and given appropriate permissions. Thus, your application should always be able to read and write files it creates explicitly. In addition, the applications sandbox may allow it to access other files in specific situations. For more information about the sandbox, see The App Sandbox (page 19).

Case sensitivity

Path construction

File attributes

File permissions

Tracking file changes

Applications that cannot use the File Coordination interfaces (see Coordinating File Access with Other Processes (page 18)) to track changes to files and directories can use the FSEvents API instead. This API provides a lower-level interface for tracking file system interactions and is available in Mac OS X v10.5 and later. For information on how to use the FSEvents API, see File System Events Programming Guide.

Security
The security technologies in Mac OS X help you secure sensitive data created or managed by your application and also help minimize the damage caused by successful exploits.

The Keychain
A keychain is a secure, encrypted container for storing the users passwords and other secrets. Users often have to manage multiple accounts that require logins with user IDs and passwords. For more on the keychain, see Keychain Services Concepts in Keychain Services Programming Guide.

The App Sandbox


In Mac OS X v10.7 and later, placing your application in an app sandbox is a great way to minimize the potential damage caused by successful exploits. Even the best code can have flaws that lead to potential security breaches. For your application to provide the highest level of security:

Adopting a sandbox is the best practice.

Low-Level Details of the Runtime Environment


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

19

CHAPTER 1

The Application Runtime Environment

An application should adopt the tightest sandbox possible.

An app sandbox prevents the code involved in those breaches from taking advantage of your applications status as a user process to gain access to other parts of the system or to other unrelated (but potentially enticing) user data. It prevents these exploits by limiting access to unrelated files and by limiting access to system resources (such as the network or specific hardware) that your application does not use. An app sandbox comprises two key components:

Entitlements for declaring the resources that the application uses A container directory for storing the applications files

You must specifically request that your application be put into a sandbox by signing it with at least the minimal set of entitlements. When the sandboxing entitlements are present, the system automatically creates a container directory for the application and runs it inside of an appropriately configured sandbox. Entitlements Declare the System Resources Your Application Needs Entitlements let the system know both that you want your application placed in a sandbox and that your application needs access to specific resources while in that sandbox. You specify the entitlements for your application using an entitlements property-list file that you create in Xcode and keep with your project. Before distributing your application (or submitting it to the App Store), you sign your application with this entitlements file. The codesign command-line tool embeds your entitlements into the applications signature. When the user subsequently launches your application, the system retrieves the entitlements from the signature and uses them to configure your applications sandbox. To request that your application be placed in a sandbox, you must include the following key in your entitlements property-list file at a minimum:
com.apple.security.app-sandbox

In addition to including the preceding key, your entitlements property-list file should include the other keys that reflect which resources your application uses. An application can request access to the Open and Save panels, access to the network, access to a camera (when present), and access to the microphone. You must request the items that your application uses but should not request access to items your application does not use. If you attempt to use a feature for which you do not have an appropriate entitlement, the system denies access and logs an appropriate message to the console. You can use these console messages during debugging to help identify features your application uses. For a complete list of entitlement keys and information on how to create an entitlements property-list file and apply it to your application, see Code Signing Guide. The Container Directory Is Your Applications Home Directory A container directory is where a sandboxed application stores all of its private files and data. System frameworks use this directory when writing out application-specific data such as preferences and autosave information. Your application similarly uses this directory to store cache files, temporary data files, or any files that the user would not normally see. The only thing you do not store in the container directory are files that the user explicitly saves using the system Save panel.

20

Low-Level Details of the Runtime Environment


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 1

The Application Runtime Environment

Applications in a sandbox are specifically prohibited from accessing files outside the container directory with some exceptions. An application may work with files opened using the system Open panel or saved using the system Save panel. In such a situation, the app sandbox is modified to allow access to those specific files. Similarly, the use of established system interfaces may also give your application indirect access to data in other parts of the file system. For example, the Address Book framework gives you access to the users contact information, which is stored outside of your application sandbox. The first time your application is launched, the system creates the container directory automatically in the users ~/Library/Containers directory and uses your applications bundle identifier for the directory name. For example, the container directory for the TextEdit application is ~/Library/Containers/com.apple.TextEdit. Note: Although the directory name is derived from the bundle identifier of the application, ownership of the directory is linked to your developer identity for security. Using the developer identity means that malicious applications cannot try to spoof the system into granting access to your container using your bundle identifier. The structure of the sandbox directory loosely parallels the structure of the users home directory. To retrieve the path to directories in your sandbox, you must always use system-provided methods such as the URLsForDirectory:inDomains: method of NSFileManager. Never try to build paths to these directories manually. Doing so will cause your application to break if the location of the paths changes later. For more information about the directories you should be using in your applications, see A Few Important Application Directories (page 16).

Low-Level Details of the Runtime Environment


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

21

CHAPTER 1

The Application Runtime Environment

22

Low-Level Details of the Runtime Environment


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 2

The Core Application Design

To connect with the power of Mac OS X, you develop applications using the Cocoa application environment. Cocoa has a runtime aspect and a development aspect. In its runtime aspect, Cocoa presents the applications user interface and integrates it tightly with the other components of the operating system. In its development aspect, Cocoa for Mac OS X provides an integrated suite of object-oriented software components packaged in two core class libraries, the AppKit and Foundation frameworks, and a number of underlying frameworks providing supporting technologies. Cocoa classes are reusable and extensibleyou can use them as is or extend them for your particular requirements. Cocoa provides program infrastructure to make the creation of applications easy. The extent of this infrastructure is such that you can create a new Cocoa application project in Xcode and without adding any code have a functional application. Such an application is able to display its window (or create new documents) and implements many standard system behaviors. And although the Xcode templates provide some code to make this all happen, the amount of code they provide is minimal. Most of the actual runtime behavior is provided by Cocoa itself. To make your application fully functional, you need to understand the Cocoa infrastructure and know what it expects you to provide. There are many ways to define a Cocoa application, so it is best to understand these basics before you start your implementation. Otherwise, fixing your design later could be much harder.

Fundamental Design Patterns


Cocoa incorporates many specific design patterns in its implementation. You need to understand and use these design patterns to write Cocoa applications successfully. Table 2-1 lists the key design patterns with which you should be familiar. Table 2-1 Fundamental design patterns used by Mac OS X applications Why it is important

Design pattern

Model-View-Controller Use of the Model-View-Controller (MVC) design pattern ensures that the objects you create now can be reused or updated easily in future versions of your application. Cocoa framework provides most of the classes used to build your applications controller and view layers. It is your job to customize the classes you need and provide the necessary data model objects to go with them. Delegation The delegation design pattern allows you to change the runtime behavior of an object without subclassing. Delegate objects conform to a specific protocol that defines the interaction points between the delegate and the object it modifies. At specific points, the master object calls the methods of its delegate to provide it with information or ask what to do. The delegate can then take whatever actions are appropriate.

Fundamental Design Patterns


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

23

CHAPTER 2

The Core Application Design

Design pattern Responder chain

Why it is important The responder chain defines the relationships between event-handling objects in your application. As events arrive, the application dispatches them to the first responder object for handling. If that object does not want the event, it passes it to the next responder, which can either handle the event or send it to its next responder, and so on up the chain. Windows and views are the most common types of responder objects and are always the first responders for mouse events. Other types of objects, such as your applications controller objects, may also be responders.

Target-action

Controls use the target-action design pattern to notify your application of user interactions. When the user interacts with a control in a predefined way (such as by touching a button), the control sends a message (the action) to an object you specify (the target). Upon receiving the action message, the target object can then respond in an appropriate manner. The garbage-collected memory model eliminates the need to retain and release objects and memory explicitly. For more information about the garbage collection system, see Garbage Collection Programming Guide.

Garbage-collected memory

Block objects

Block objects are a convenient way to encapsulate code and local stack variables in a form that can be executed later. Blocks are used in lieu of callback functions by many frameworks and are also used in conjunction with Grand Central Dispatch to perform tasks asynchronously. For more information about using blocks, see Blocks Programming Topics.

Notifications

Notifications are used throughout Cocoa to deliver news of changes to your application. Many objects send notifications at key moments in the objects life cycle. Intercepting these notifications gives you a chance to respond and add custom behavior. KVO tracks changes to a specific property of an object. When that property changes, the change generates automatic notifications for any objects that registered an interest in that property. Those observers then have a chance to respond to the change. Cocoa bindings provide a convenient bridge between the model, view, and controller portions of your application. You bind a view to some underlying data object (which can be static or dynamic) through one of your controllers. Changes to the view are then automatically reflected in the data object, and vice versa. The use of bindings is not required for applications but does minimize the amount of code you have to write. You can set up bindings programmatically or using Interface Builder.

Key-Value Observing (KVO)

Bindings

For a more detailed discussion of Cocoa and the design patterns you use to implement Mac OS X applications, see Cocoa Fundamentals Guide.

24

Fundamental Design Patterns


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 2

The Core Application Design

The Application Style Determines the Core Architecture


The style of your application defines which core objects you must use in its implementation. Cocoa supports the creation of both single-window and multiwindow applications. For multiwindow designs, it also provides a document architecture to help manage the files associated with each application window. Thus, applications can have the following forms:

Single-window utility application Single-window shoebox application Multiwindow document application

You should choose a basic application style early in your design process because that choice affects everything you do later. The single-window styles are preferred in many cases, especially for developers bringing applications over from iOS. The single-window style typically yields a more streamlined user experience, and it also makes it easier for your application to support a full-screen mode. However, if your application works extensively with complex documents, the multiwindow style may be preferable because it provides more document-related infrastructure to help you implement your application. The Calculator application provided with Mac OS X, shown in Figure 2-1, is an example of a single-window utility application. Utility applications typically handle ephemeral data or manage system processes. Calculator does not create or deal with any documents or persistent user data but simply processes numerical data entered by the user into the text field in its single window, displaying the results of its calculations in the same field. When the user quits the application, the data it processed is simply discarded. Figure 2-1 The Calculator single-window utility application

Single-window shoebox applications do handle persistent user data. One of the most prominent examples of a shoebox application is iPhoto, shown in Figure 2-2. The user data handled by iPhoto are photos (and associated metadata), which the application edits, displays, and stores. All user interaction with iPhoto happens in a single window. Although iPhoto stores its data in files, it doesnt present the files to the user. The application presents a simplified interface so that users dont need to manage files in order to use the application. Instead, they work directly with their photos. Moreover, iPhoto hides its files from regular

The Application Style Determines the Core Architecture


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

25

CHAPTER 2

The Core Application Design

manipulation in the Finder by placing them within a single package. In addition, the application saves the users editing changes to disk at appropriate times. So, users are relieved of the need to manually save, open, or close documents. This simplicity for users is one of the key advantages of the shoebox application design. Figure 2-2 The iPhoto single-window application

A good example of a multiwindow document application is TextEdit, which creates, displays, and edits documents containing plain or styled text and images. TextEdit does not organize or manage its documentsusers do that with the Finder. Each TextEdit document opens in its own window, multiple documents can be open at one time, and the user interacts with the frontmost document using controls in the windows toolbar and the applications menu bar. Figure 2-3 shows a document created by TextEdit.

26

The Application Style Determines the Core Architecture


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 2

The Core Application Design

Figure 2-3

TextEdit document window

In Mac OS X v10.7, TextEdit implements automatic saving, an idea borrowed from the single-window design. Users no longer need to save their editing changesthe document architecture writes them to disk at appropriate times. Although users can manually save a version of the document if they wish, doing so is optional. Implementing a Document-Based Design (page 49) explains how to create a multiwindow, document-based application.

The Core Objects for All Cocoa Applications


Regardless of whether you are using a single-window or multiwindow application style, all applications use the same core set of objects. Cocoa provides the default behavior for most of these objects. However, you are expected to provide a certain amount of customization of these objects to implement your applications custom behavior. Figure 2-4 shows the relationships among the core objects for the single-window application styles. The objects in this figure are separated according to whether they are part of the model, view, or controller portions of the application. As you can see from the figure, the Cocoaprovided objects provide much of the controller and view layer for your application.

The Application Style Determines the Core Architecture


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

27

CHAPTER 2

The Core Application Design

Figure 2-4

Key objects in a single-window application


Model Data Model Objects Data Model Objects Data Model Objects

Controller NSApplication Application Delegate

View

Event Loop

Window Controller

NSWindow

Data Model Objects Data Model Objects View controllers


(optional)

Data Model Objects Data Model Objects Views and UI Objects

Table 2-2 (page 28) describes the roles played by the objects in the diagram. Table 2-2 Object The core objects used by all Cocoa applications Description

NSApplication (Required) All applications have a single application object to run the event loop and

object

manage interactions between your application and the system. You typically use the NSApplication class as is, putting any custom application-object-related code in your application delegate object. (Expected) The application delegate object is a custom object that you provide. It works closely with the NSApplication object to run the application and manage the transitions between different application states. Your application delegate object must conform to the NSApplicationDelegate Protocol.

Application delegate object

Data model objects

Data model objects store your applications content and are therefore specific to your application. For example, a banking application might store a database containing financial transactions, whereas a painting application might store an image object or even the sequence of drawing commands that led to the creation of that image. (In the latter case, an image object is still a data object because it is just a container for the image data. The actual rendering of that image still takes place elsewhere in your application.)

28

The Application Style Determines the Core Architecture


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 2

The Core Application Design

Object Window controllers

Description The NSWindowController class defines the base implementation for all window controller objects. Each instance of this class is responsible for loading and managing a single window and coordinating with the system to handle standard window behaviors. In your applications, you subclass NSWindowController to manage both the window and its contents. Each window controller object is ultimately responsible for everything that happens in its window. If the contents of your window are simple, the window controller object may do all of the management itself. If your window is more complex, the window controller might use one or more view controllers to manage portions of the window.

Window objects

You use instances of NSWindow as is to represent your onscreen windows. Window objects can be configured in different styles depending on your applications needs. For example, most windows have title bars and borders but you can also configure windows without those visual adornments. Although not strictly required, a window object is almost always managed by a corresponding window controller.

View controllers

The NSViewController class defines the base implementation for all view controller objects. This class coordinates the loading of a single view hierarchy into your application. You can use view controllers to divide up the work for managing more sophisticated window layouts. In such a scenario, your view controllers would work together (and with the overarching window controller) to present the overall window contents. If you have developed iOS applications, be aware that AppKit view controllers play a less prominent role than UIKit view controllers. In Mac OS X, AppKit view controllers are assistants to the window controller, which is ultimately responsible for everything that goes in the window. The main job of an AppKit view controller is to load its view hierarchy. Everything else is custom code that you write.

View objects

The NSView class defines the basic behavior for all views. A view defines a rectangular region in a window, draws the contents of that region, and handles events in that region. Visually, views can be layered on top of each other to create view hierarchies, whereby one view obscures a portion of the underlying view. Cocoa provides several classes that represent the standard system controls. These classes provide standard visual items such as buttons, text fields, and tables that you can use to build your user interface. Although a few views are used as is to present visual adornments, most work with your code to manage user interactions with your applications content.

Any application can also have secondary windows, also known as dialogs and panels. These windows are subordinate to the current document window or, in the case of single-window applications, to the main window. They support the document or main window in various waysfor example, allowing selection of fonts and color, allowing the selection of tools from a palette, or displaying a warning A secondary window is often modal.

The Application Style Determines the Core Architecture


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

29

CHAPTER 2

The Core Application Design

Additional Core Objects for Multiwindow Applications


As opposed to a single-window application, a multiwindow application uses several windows to present its primary content. The Cocoa support for multiwindow applications is built around a document-based model implemented by a subsystem called the document architecture. In this model, each document object manages its content, coordinates the reading and writing of that content from disk, and presents the content in a window for editing. All document objects work with the Cocoa infrastructure to coordinate event delivery and such, but each document object is otherwise independent of its fellow document objects. Figure 2-5 shows the relationships among the core objects of a multiwindow document-based application. Many of the same objects in this figure are identical to those used by a single-window application. The main difference is the insertion of the NSDocumentController and NSDocument objects between the application objects and the objects for managing the user interface. Figure 2-5 Key objects in a multiwindow document application
Model Data Model Objects

Controller NSApplication Application delegate

Event Loop

NSDocumentController

NSDocument

View controllers
(optional)

Window controller

View

Views and UI Objects

NSWindow

Table 2-3 (page 31) describes the role of the insertedNSDocumentController and NSDocument objects. (For information about the roles of the other objects in the diagram, see Table 2-2 (page 28).)

30

The Application Style Determines the Core Architecture


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 2

The Core Application Design

Table 2-3 Object

Additional objects used by multiwindow document applications Description

Document Controller The NSDocumentController class defines a high-level controller for creating and object managing all document objects. In addition to managing documents, the document controller also manages many document-related menu items, such as the Open Recent menu and the open and save panels. Document object The NSDocument class is the base class for implementing documents in a multiwindow application. This class acts as the controller for the data objects associated with the document. You define your own custom subclasses to manage the interactions with your applications data objects and to work with one or more NSWindowController objects to display the document contents on the screen.

For more detailed information about multiwindow, document-based applications, see Implementing a Document-Based Design (page 49).

Shoebox Applications Should Not Use NSDocument


When implementing a single-window, shoebox application, it is better not to use NSDocument objects to manage your content. The NSDocument class was designed specifically for use in multiwindow document applications. Instead, use custom controller objects to manage your data. Those custom controllers would then work with a view controller or your applications main window controller to coordinate the presentation of the data. Although you normally use an NSDocumentController object only in multiwindow applications, you can subclass it and use it in a single-window application to coordinate the Open Recent and similar behaviors. When subclassing, though, you want to override any methods related to the creation of NSDocument objects.

The Application Life Cycle


The application life cycle is the progress of an application from its launch through its termination. Applications can be launched by either the user or the system. The user launches applications by double-clicking the application icon, using Launchpad, or by opening a file whose type is currently associated with the application. In Mac OS X v10.7 and later, the system launches applications at user login time when it needs to restore the users desktop to its previous state. When an application is launched, the system creates a process and all of the normal system-related data structures for it. Inside the process, it creates a main thread and uses it to begin executing your applications code. At that point, your applications code takes over and your application is running.

The Application Life Cycle


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

31

CHAPTER 2

The Core Application Design

The main Function is the Application Entry Point


Like any C-based application, the main entry point for a Mac OS X application at launch time is the main function. In a Mac OS X application, the main function is used only minimally. Its main job is to give control to the AppKit framework. Therefore, any new project you create in Xcode comes with a default main function like the one shown in Listing 2-1. With few exceptions, you should never change the implementation of this function. Listing 2-1 The main function of a Mac OS X application

#import <Cocoa/Cocoa.h> int main(int argc, char *argv[]) { return NSApplicationMain(argc, }

(const char **) argv);

The NSApplicationMain function initializes your application and prepares it to run. As part of the initialization process, this function does several things:

The NSApplicationMain function creates an instance of the NSApplication class. You can access this object from anywhere in your application using the sharedApplication class method. It configures the memory management objects needed for the main thread.

For garbage-collected applications, it installs a collector on the applications main thread. For memory-managed applications, it installs a last-chance autorelease pool to catch autoreleased objects.

It loads the nib file specified by the NSMainNibFile key in the Info.plist file and instantiates all of the objects in that file. This is your applications main nib file and should contain your application delegate and any other critical objects that must be loaded early in the launch cycle. Any objects that are not needed at launch time should be placed in separate nib files and loaded later. It calls the run method of your application object to finish the launch cycle and begin processing events.

By the time the run method is called, the main objects of your application are loaded into memory but the application is still not fully launched. The run method notifies the application delegate that the application is about to launch, shows the application menu bar, opens any any files that were passed to the application, does some framework housekeeping, and starts the event processing loop. All of this work occurs on the applications main thread with one exception. Files may be opened in secondary threads if the canConcurrentlyReadDocumentsOfType: class method of the corresponding NSDocument object returns YES.

32

The Application Life Cycle


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 2

The Core Application Design

Note: Although you can subclass NSApplication to customize the application behaviors, you should avoid doing so whenever possible and use the default NSApplication class to run your application. If you do decide to subclass, you must let Cocoa know that you have done so by setting the value of the NSPrincipalClass key to your custom class name in your applications Info.plist file. The NSApplicationMain function uses the value of that key when creating the application object. For more about your applications Info.plist file, see The Information Property List File (page 80). If your application preserves its user interface between launch cycles, Cocoa loads any preserved data at launch time and uses it to re-create the windows that were open the last time your application was running. For more information about how to preserve your applications user interface, see User Interface Preservation (page 37).

The Applications Main Event Loop Drives Interactions


As the user interacts with your application, the applications main event loop processes incoming events and dispatches them to the appropriate objects for handling. When the NSApplication object is first created, it establishes a connection with the system window server, which receives events from the underlying hardware and transfers them to the application. The application also sets up a FIFO event queue to store the events sent to it by the window server. The main event loop is then responsible for dequeueing and processing events waiting in the queue, as shown in Figure 2-6. Figure 2-6 The main event loop
Observer Main run loop Event source event event event
Mach Port

event

Window Server

event

The run method of the NSApplication object is the workhorse of the main event loop. In a closed loop, this method executes the following steps until the application terminates: 1. The run method services window-update notifications, which results in the redrawing of any windows that are marked as dirty. It dequeues an event from its internal event queue using the
nextEventMatchingMask:untilDate:inMode:dequeue: method and converts the event data into an NSEvent object.

2.

3.

It dispatches the event to the appropriate target object using the sendEvent: method of NSApplication.

The Application Life Cycle


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

33

CHAPTER 2

The Core Application Design

The nextEventMatchingMask:untilDate:inMode:dequeue: method is the funnel point for the main event loop. If there are no events in the event queue, this method blocks the main thread. During that time, the window server is still processing events and may place new ones on the event queue. When that happens, the system wakes up the applications main thread so that it can process the newly received events. When the application dispatches an event, the sendEvent: method uses the type of the event to determine the appropriate target. There are two major types of input events: key events and mouse events. Key events are sent to the key windowthe window that is currently accepting key presses. Mouse events are dispatched to the window in which the event occurred. For mouse events, the window looks for the view in which the event occurred and dispatches the event to that object first. Views are responder objects and are capable of responding to any type of event. If the view is a control, it typically uses the event to generate an action message for its associated target. Other types of views may process the events directly, use them to generate notifications or send messages to your code, pass them to other objects for handling, or ignore them entirely. The overall process for handling events is described in detail in Cocoa Event-Handling Guide.

Automatic and Sudden Termination of Applications Improve the User Experience


In Mac OS X v10.7 and later, the use of the Quit command to terminate an application is diminished in favor of more user-centric techniques. Specifically, Cocoa supports two techniques that make the termination of an application transparent and fast:

Automatic termination eliminates the need for users to quit an application. Instead, the system manages application termination transparently behind the scenes, terminating applications that are not in use to reclaim needed resources such as memory. Sudden termination allows the system to kill an applications process immediately without waiting for it to perform any final actions. The system uses this technique to improve the speed of operations such as logging out of, restarting, or shutting down the computer.

Automatic termination and sudden termination are independent techniques, although both are designed to improve the user experience of application termination. Although Apple recommends that applications support both, an application can support one technique and not the other. Applications that support both techniques can be terminated by the system without the application being involved at all. On the other hand, if an application supports sudden termination but not automatic termination, then it must be sent a Quit event, which it needs to process without displaying any user interface dialogs. Automatic termination transfers the job of managing processes from the user to the system, which is better equipped to handle the job. Users do not need to manage processes manually anyway. All they really need is to run applications and have those applications available when they need them. Automatic termination makes that possible while ensuring that system performance is not adversely affected. Applications must opt in to both automatic termination and sudden termination and implement appropriate support for them. In both cases, the application must ensure that any user data is saved well before termination can happen. And because the user does not quit an autoterminable application, such an application should also save the state of its user interface using the built-in Cocoa support. Saving and restoring the interface state provides the user with a sense of continuity between application launches.

34

The Application Life Cycle


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 2

The Core Application Design

Note: If you do not opt in to automatic termination for your applications, the user must explicitly quit your application to remove it from memory. For information on how to support for automatic termination in your application, see Automatic Termination (page 35). For information on how to support sudden termination, see Sudden Termination (page 36).

Support the Key Runtime Behaviors in Your Applications


No matter what style of application you are creating, there are specific behaviors that all applications should support. These behaviors are intended to help users focus on the content they are creating rather than focus on application management and other busy work that is not part of creating their content.

Automatic Termination
Automatic termination is a feature that you must explicitly code for in your application. Declaring support for automatic termination is easy, but applications also need to work with the system to save the current state of their user interface so that it can be restored later as needed. The system can quit an auto-terminable application at any time, so saving this information maintains continuity for the application. Usually, the system quits the application some time after the user has closed all of its windows. However, the system may also quit an application with open windows if the application is not currently on screen, perhaps because the user hid it or switched spaces. Thus, to support automatic termination, you should do the following:

Declare your applications support for automatic termination, either programmatically or using an Info.plist key; see Enabling Automatic Termination in Your Application (page 35). Support saving and restoring your window configurations; see User Interface Preservation (page 37). Save the users data at appropriate times.

Single-window applications should implement strategies for saving data at appropriate checkpoints; see Data-Saving Strategies for Single-Window Applications (page 36). Multiwindow applications can use the autosaving and saveless documents capabilities in Cocoa; see Implementing a Document-Based Design (page 49).

Whenever possible, support sudden termination for your application as well; see Sudden Termination (page 36).

Enabling Automatic Termination in Your Application


Declaring support for automatic termination lets the system know that it should manage the actual termination of your application at appropriate times. An application has two ways to declare its support for automatic termination:

Support the Key Runtime Behaviors in Your Applications


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

35

CHAPTER 2

The Core Application Design

Include the NSSupportsAutomaticTermination key (with the value YES) in the applications Info.plist file. Use the NSProcessInfo class to declare support for automatic termination dynamically. You can also use this class to change the default support of an application that includes the NSSupportsAutomaticTermination key in its Info.plist file.

Data-Saving Strategies for Single-Window Applications


When implementing a single-window application, avoid forcing the user to save changes manually. Instead, identify appropriate points in your code where any user-related changes should be saved and write those changes to disk automatically. This benefits the user by eliminating the need to think about manually saving changes, and when done regularly, it ensures that the user does not lose much data if there is a problem. Some appropriate times when you can save user data automatically include the following:

When the user closes the application window or quits the application (applicationWillTerminate:) When the application is deactivated (applicationWillResignActive:) When the user hides your application (applicationWillHide:) Whenever the user makes a valid change to data in your application

The last item means that you have the freedom to save the users data at any time it makes sense to do so. For example, if the user is editing fields of a data record, you can save each field value as it is changed or you can wait and save all fields when the user displays a new record. Making these types of incremental changes ensures that the data is always up-to-date but also requires more fine-grained management of your data model. In such an instance, Core Data can help you make the changes more easily. For information about Core Data, see Core Data Starting Point.

Sudden Termination
Sudden termination lets the system know that your applications process can be killed directly without any additional involvement from your application. The benefit of supporting sudden termination is that it lets the system close applications more quickly, which is important when the user is shutting down a computer or logging out. An application has two ways to declare its support for sudden termination:

Include the NSSupportsSuddenTermination key (with the value YES) in the applications Info.plist file. Use the NSProcessInfo class to declare support for sudden termination dynamically. You can also use this class to change the default support of an application that includes the NSSupportsSuddenTermination key in its Info.plist file.

36

Support the Key Runtime Behaviors in Your Applications


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 2

The Core Application Design

One solution is to declare global support for the feature globally and then manually override the behavior at appropriate times. Because sudden termination means the system can kill your application at any time after launch, you should disable it while performing actions that might lead to data corruption if interrupted. When the action is complete, reenable the feature again. You disable and enable sudden termination programmatically using the disableSuddenTermination and enableSuddenTermination methods of the NSProcessInfo class. These methods increment and decrement a counter, respectively, maintained by the process. When the value of this counter is 0, the process is eligible for sudden termination. When the value is greater than 0, sudden termination is disabled. Enabling and disabling sudden termination dynamically also means that your application should save data progressively and not rely solely on user actions to save important information. The best way to ensure that your applications information is saved at appropriate times is to support the interfaces in Mac OS X v10.7 for saving your document and window state. Those interfaces facilitate the automatic saving of relevant user and application data. For more information about saving your user interface state, see User Interface Preservation (page 37). For more information about saving your documents, see Implementing a Document-Based Design (page 49). For additional information about enabling and disabling sudden termination, see NSProcessInfo Class Reference.

User Interface Preservation


The Resume feature of Mac OS X v10.7 saves the state of your applications windows and restores them during subsequent launches of your application. Saving the state of your windows enables you to return your application to the state it was in when the user last used it. Use the Resume feature especially if your application supports automatic application termination, which can cause your application to be terminated while it is running but hidden from the user. If your application supports automatic termination but does not preserve its interface, the application launches into its default state. Users who only switched away from your application might think that the application crashed while it was not being used.

Writing Out the State of Your Windows and Custom Objects


The following sections primarily describe the process for preserving the state of a single-window application. However, most of the steps described in these sections also apply to multiwindow document applications. For additional information on what you need to do to save state in a multiwindow document-based application, see Windows Are Restored Automatically (page 65). You must do the following to preserve the state of your user interface:

For each window, you must set whether the window should be preserved using the setRestorable: method. For each preserved window, you must specify an object whose job is to re-create that window at launch time. Any objects involved in your user interface must write out the data they require to restore their state later. At launch time, you must use the provided data to restore your objects to their previous state.

Support the Key Runtime Behaviors in Your Applications


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

37

CHAPTER 2

The Core Application Design

The actual process of writing out your application state to disk and restoring it later is handled by Cocoa, but you must tell Cocoa what to save. Your applications windows are the starting point for all save operations. Cocoa iterates over all of your applications windows and saves data for the ones whose isRestorable method returns YES. Most windows are preserved by default, but you can change the preservation state of a window using the setRestorable: method. In addition to preserving your windows, Cocoa saves data for most of the responder objects associated with the window. Specifically, it saves the views and window controller objects associated with the window. (For a multiwindow document-based application, the window controller also saves data from its associated document object.) Figure 2-7 shows the path that Cocoa takes when determining which objects to save. Window objects are always the starting point, but other related objects are saved, too. Figure 2-7
Archived state

Responder objects targeted by Cocoa for preservation

NSWindow NSWindow NSWindow

View View Views

NSWindowController

NSDocument

Although the Cocoa responder objects save a lot of useful information about the state of your windows and views, they do not have any inherent knowledge about your applications data structures. Therefore, it is your responsibility to save any additional information needed to restore the window to its current state. There are several places where you can write out your custom state information:

If you subclass NSWindow or NSView, implement the encodeRestorableStateWithCoder: method in your subclass and use it to write out any relevant data. Alternatively, your custom responder objects can override the restorableStateKeyPaths method and use it to specify key paths for any attributes to be preserved. Cocoa uses the key paths to locate and save the data for the corresponding attribute. Attributes must be compliant with key-value coding and key-value observing.

If your window has a delegate object, implement the window:willEncodeRestorableState: method for the delegate and use it to store any relevant data. In your window controller, use the encodeRestorableStateWithCoder: method to save any relevant data or configuration information.

38

Support the Key Runtime Behaviors in Your Applications


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 2

The Core Application Design

Be judicious when deciding what data to preserve, and strive to write out the smallest amount of information that is required to reconfigure your window and associated objects. All Cocoa window and view objects save basic information about their size and location, plus information about other attributes that might affect the way they are currently displayed. For example, a tab view saves the index of the selected tab, and a text view saves the location and range of the current text selection. Those objects do not, however, save the actual data they are displaying. You are expected to save enough information during the save process to reattach the window to the same data objects later. Important: Never use the user interface preservation mechanism as a way to save your applications actual data. The archive created for interface preservation can change frequently and may be ignored altogether if there is a problem during the restoration process. Your application data should always be saved independently in data files that are managed by your application. For information on how to use coder objects to archive state information, see Archives and Serializations Programming Guide.

Notifying Cocoa About Changes to Your Interface State


Whenever the preserved state of one of your responder objects changes, mark the object as dirty by calling the invalidateRestorableState method of that object. Having done so, at some point in the future, encodeRestorableStateWithCoder: message is sent to your responder object. Marking your responder objects as dirty lets Cocoa know that it needs to write their preservation state to disk at an appropriate time. Invalidating your objects is a lightweight operation in itself because the data is not written to disk right away. Instead, changes are coalesced and written at key times, such as when the user switches to another application or logs out. You should mark a responder object as dirty only for changes that are truly interface related. For example, a tab view marks itself as dirty when the user selects a different tab. However, you do not need to invalidate your window or its views for many content-related changes, unless the content changes themselves caused the window to be associated with a completely different set of data-providing objects. If you used the restorableStateKeyPaths method to declare the attributes you want to preserve, Cocoa preserves and restores the values of those attributes of your responder object. Therefore, any key paths you provide should be key-value observing compliant and generate the appropriate notifications. For more information on how to support key-value observing in your objects, see Key-Value Observing Programming Guide.

Restoring Your Windows and Custom Objects at Launch TIme


As part of your applications normal launch cycle, Cocoa checks to see whether there is any preserved interface data. If there is, Cocoa uses that data to try to re-create your applications windows. Of course, Cocoa does need help from your application to do that. Every window is expected to identify a class that knows about the window and can act on its behalf at launch time. This class is known as the restoration class of the window and its job is to do whatever is necessary to create the window when asked to do so by Cocoa. The restoration class is responsible for creating both the window and all of the critical objects required by that window. For most application styles, the restoration class usually creates one or more controller objects as well. For example, in a single-window application, the restoration class would likely create the window controller used to manage the window and then retrieve the window from that object. Because it creates

Support the Key Runtime Behaviors in Your Applications


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

39

CHAPTER 2

The Core Application Design

these controller objects too, you typically use high-level application classes for your restoration classes. An application might use the application delegate, a document controller, or even a window controller as a restoration class. During the launch cycle, Cocoa does the following to create each preserved window: 1. Cocoa retrieves the windows restoration class from the preserved data and calls its restoreWindowWithIdentifier:state:completionHandler: class method. The restoreWindowWithIdentifier:state:completionHandler: class method is expected to call the provided completion handler with the desired window object. To do this, it typically does one of the following:

2.

It creates any relevant controller objects (including the window controller) that might normally be created to display the window. If the controller objects already exist (perhaps because they were already loaded from a nib file), the method gets the window from those existing objects.

If the window could not be created, perhaps because the associated document was deleted by the user, the restoreWindowWithIdentifier:state:completionHandler: should pass an error object to the completion handler. 3. Cocoa uses the returned window to restore it and any preserved responder objects to their previous state.

Standard Cocoa window and view objects are restored to their previous state without additional help. If you subclass NSWindow or NSView, implement the restoreStateWithCoder: method to restore any custom state. If you implemented the restorableStateKeyPaths method in your custom responder objects, Cocoa automatically sets the value of the associated attributes to their preserved values. Thus, you do not have to implement the restoreStateWithCoder: to restore these attributes.

For the window delegate object, Cocoa calls the window:didDecodeRestorableState: method to restore the state of that object. For your window controller, Cocoa calls the restoreStateWithCoder: method to restore its state.

When re-creating each window, Cocoa passes the windows unique identifier string to the restoration class. You are responsible for assigning identifier strings to your windows prior to preserving the window state. Your restoration class can use these identifiers to determine which window and associated objects it needs to re-create. The contents of an identifier string can be anything you want but should be something to help you identify the window later. For example, you might assign the string preferences as the unique identifier of your applications preferences window. For a single-window application whose main window controller and window are loaded from the main nib file, the job of your restoration class is fairly straightforward. Here, you could use the application delegates class as the restoration class and implement the restoreWindowWithIdentifier:state:completionHandler: method similar to the implementation shown in Listing 2-2. Because the application has only one window, it returns the main window directly. If you used the app delegates class as the restoration class for other windows, your own implementation could use the identifier parameter to determine which window to create.

40

Support the Key Runtime Behaviors in Your Applications


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 2

The Core Application Design

Listing 2-2

Returning the main window for a single-window application

+ (void)restoreWindowWithIdentifier:(NSString *)identifier state:(NSCoder *)state completionHandler:(void (^)(NSWindow *, NSError *))completionHandler { // Get the window from the window controller, // which is stored as an outlet by the delegate. // Both the app delegate and window controller are // created when the main nib file is loaded. MyAppDelegate* appDelegate = (MyAppDelegate*)[[NSApplication sharedApplication] delegate]; NSWindow* mainWindow = [appDelegate.windowController window]; // Pass the window to the provided completion handler. completionHandler(mainWindow, nil); }

Using Custom Restoration Code to Preserve Your Interface


If your application already preserves its state and restores it upon subsequent relaunches, you should still incorporate support for the Cocoa restoration process. Cocoa stores information besides just your window information when saving your user interface. It later uses this information to drop your new window objects into the places where other parts of the system might be expecting the old window objects. So it is important to allow this to happen. When it comes time to restore your windows, plan to use a restoration class to initiate the restore process. You do not need to encode and restore the state of your interface objects using the Cocoa-provided methods if you already have your own code to do that, but you do need to provide Cocoa with the new window object you restored yourself. Thus, the restoreWindowWithIdentifier:state:completionHandler: method of your restoration class would typically just return a window that you already created and restored with your custom code. Listing 2-3 shows a possible implementation for this method that retrieves an already created window from elsewhere in your application. Listing 2-3 Providing Cocoa with an already restored window

+ (void)restoreWindowWithIdentifier:(NSString *)identifier state:(NSCoder *)state completionHandler:(void (^)(NSWindow *, NSError *))completionHandler { // Get the window that has already been restored. NSWindow *window = getMyAlreadyRestoredWindow(); // Pass the window to the provided completion handler. completionHandler(window, nil); }

Cocoa triggers the restoration of your windows between calls to the applicationWillFinishLaunching: and applicationDidFinishLaunching: methods of your application delegate. This timing gives your application delegate an opportunity to create and restore its windows in the applicationWillFinishLaunching: method and be able to provide those windows to Cocoa later. If Cocoa asks for a window that (for whatever reason) you cannot restore or do not want to restore, pass nil for the window when calling the completion handler.

Support the Key Runtime Behaviors in Your Applications


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

41

CHAPTER 2

The Core Application Design

Tweaking the Restoration Process in Your Application Object


The NSApplication object starts the interface restoration process and handles any problems that might arise. You should not need to modify the default application behavior for this process in most cases, but if you find that you need to make adjustments, you can override the application objects restoreWindowWithIdentifier:state:completionHandler: method to do so. The restoreWindowWithIdentifier:state:completionHandler: method is the applications funnel point for creating windows. The default behavior of this method is to get the restoration class for the window and use that class to obtain the corresponding window object. However, if you remove a class from your application that was acting as a restoration class, you can override this method to detect that case and provide a new restoration class. For more information about overriding this method, see NSApplication Class Reference.

Applications Are Built Using Many Different Pieces


The objects of the core architecture are important but are not the only objects you need to consider in your design. The core objects manage the high-level behavior of your , but the objects in your applications view layer do most of the work to display your custom content and respond to events. Other objects also play important roles in creating interesting and engaging applications.

The User Interface


An applications user interface is made up of a menu bar, one or more windows, and one or more views. The menu bar is a repository for commands that the user can perform in the application. Commands may apply to the application as a whole, to the currently active window, or to the currently selected object. You are responsible for defining the commands that your application supports and for providing the event-handling code to respond to them. You use windows and views to present your applications visual content on the screen and to manage the immediate interactions with that content. A window is an instance of the NSWindow class. A panel is an instance of the NSPanel class (which is a descendant of NSWindow) that you use to present secondary content. Single-window applications have one main window and may have one or more secondary windows or panels. Multiwindow applications have multiple windows for displaying their primary content and may have one or more secondary windows or panels too. The style of a window determines its appearance on the screen. Figure 2-8 shows the menu bar, along with some standard windows and panels.

42

Applications Are Built Using Many Different Pieces


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 2

The Core Application Design

Figure 2-8

Windows and menus in an application

Levels
Menu bar Dock Modal panels Restart alert Font panel Application windows Untitled Untitled-1 Untitled-2

A view, an instance of the NSView class, defines the content for a rectangular region of a window. Views are the primary mechanism for presenting content and interacting with the user and have several responsibilities. For example:

Drawing and animation support. Views draw content in their rectangular area. Views that support Core Animation layers can use those layers to animate their contents. Layout and subview management. Each view manages a list of subviews, allowing you to create arbitrary view hierarchies. Each view defines layout and resizing behaviors to accommodate changes in the window size. Event handling. Views receive events. Views forward events to other objects when appropriate.

Z-order

For information about creating and configuring windows, see Window Programming Guide. For information about using and creating view hierarchies, see View Programming Guide.

Event Handling
The system window server is responsible for tracking mouse, keyboard, and other events and delivering them to your application. When the system launches an application, it creates both a process and a single thread for the application. This initial thread becomes the applications main thread. In it, the NSApplication object sets up the main run loop and configures its event-handling code, as shown in Figure 2-9. As the window server delivers events, the application queues those events and then processes them sequentially in the applications main run loop. Processing an event involves dispatching the event to the application object best suited to handle it. For example, mouse events are usually dispatched to the view in which the event occurred.

Applications Are Built Using Many Different Pieces


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

43

CHAPTER 2

The Core Application Design

Figure 2-9

Processing events in the main run loop

Event queue

Application

Application object Operating system

Core objects

Note: A run loop monitors sources of input on a specific thread of execution. The applications event queue represents one of these input sources. While the event queue is empty, the main thread sleeps. When an event arrives, the run loop wakes up the thread and dispatches control to the NSApplication object to handle the event. After the event has been handled, control passes back to the run loop, which can then process another event, process other input sources, or put the thread back to sleep if there is nothing more to do. For more information about how run loops and input sources work, see Threading Programming Guide. Distributing and handling events is the job of responder objects, which are instances of the NSResponder class. The NSApplication, NSWindow, NSDrawer, NSView, NSWindowController, and NSViewController classes are all descendants of NSResponder. After pulling an event from the event queue, the application dispatches that event to the window object where it occurred. The window object, in turn, forwards the event to its first responder. In the case of mouse events, the first responder is typically the view object (NSView) in which the touch took place. For example, a mouse event occurring in a button is delivered to the corresponding button object. If the first responder is unable to handle an event, it forwards the event to its next responder, which is typically a parent view, view controller, or window. If that object is unable to handle the event, it forwards it to its next responder, and so , until the event is handled. This series of linked responder objects is known as the responder chain. Messages continue traveling up the responder chaintoward higher-level responder objects, such as a window controller or the application objectuntil the event is handled. If the event isn't handled, it is discarded. The responder object that handles an event often sets in motion a series of programmatic actions by the application. For example, a control object (that is, a subclass of NSControl) handles an event by sending an action message to another object, typically the controller that manages the current set of active views. While processing the action message, the controller might change the user interface or adjust the position of views in ways that require some of those views to redraw themselves. When this happens, the view and graphics infrastructure takes over and processes the required redraw events in the most efficient manner possible. For more information about responders, the responder chain, and handling events, see Cocoa Event-Handling Guide.

44

Applications Are Built Using Many Different Pieces


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 2

The Core Application Design

Graphics, Drawing, and Printing


There are two basic ways in which a Mac OS X application can draw its content:

An application can use native drawing technologies (such as Core Graphics and AppKit). An application can use OpenGL.

The native Mac OS X drawing technologies typically use the infrastructure provided by Cocoa views and windows to render and present custom content. When a view is first shown, the system asks it to draw its content. System views draw their contents automatically, but custom views must implement a drawRect: method. Inside this method, you use the native drawing technologies to draw shapes, text, images, gradients, or any other visual content you want. When you want to update your views visual content, you mark all or part of the view invalid by calling its setNeedsDisplay: or setNeedsDisplayInRect: method. The system then calls your views drawRect: method (at an appropriate time) to accommodate the update. This cycle then repeats and continues throughout the lifetime of your application. If you are using OpenGL to draw your applications content, you still create a window and view to manage your content, but those objects simply provide the rendering surface for an OpenGL drawing context. Once you have that drawing context, your application is responsible for initiating drawing updates at appropriate intervals. For information about how to draw custom content in your views, see Cocoa Drawing Guide.

Text Handling
The Cocoa text system, the primary text-handling system in Mac OS X, is responsible for the processing and display of all visible text in Cocoa. It provides a complete set of high-quality typographical services through the text-related AppKit classes, which enable applications to create, edit, display, and store text with all the characteristics of fine typesetting. The Cocoa text system provides all these basic and advanced text-handling features, and it also satisfies additional requirements from the ever-more-interconnected computing world: support for the character sets of all of the worlds living languages, powerful layout capabilities to handle various text directionality and nonrectangular text containers, and sophisticated typesetting capabilities such as control of kerning, ligatures, line breaking, and justification. Cocoas object-oriented text system is designed to provide all these capabilities without requiring you to learn about or interact with more of the system than is necessary to meet the needs of your application. Underlying the Cocoa text system is Core Text, which provides low-level, basic text layout and font-handling capabilities to higher-level engines such as Cocoa and WebKit. Core Text provides the implementation for many Cocoa text technologies. Application developers typically have no need to use Core Text directly. However, the Core Text API is accessible to developers who must use it directly, such as those writing applications with their own layout engine and those porting older ATSUI- or QuickDraw-based codebases to the modern world. For more information about the Cocoa text system, see Cocoa Text Architecture Guide.

Applications Are Built Using Many Different Pieces


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

45

CHAPTER 2

The Core Application Design

Implementing the Application Menu Bar


The classes NSMenu and NSMenuItem are the basis for all types of menus. An instance of NSMenu manages a collection of menu items and draws them one beneath another. An instance of NSMenuItem represents a menu item; it encapsulates all the information its NSMenu object needs to draw and manage it, but does no drawing or event-handling itself. You typically use Interface Builder to create and modify any type of menu, so often there is no need to write any code. The application menu bar stretches across the top of the screen, replacing the menu bar of any other application when the application is foremost. All of an applications menus in the menu bar are owned by one NSMenu instance thats created by the application when it starts up.

Xcode Templates Provide the Main Menu


Xcodes Cocoa application templates provide that NSMenu instance in a nib file called MainMenu.xib. This nib file contains an application menu (named with the applications name), a File menu (with all of its associated document commands), an Edit menu (with text editing commands and Undo and Redo menu items), and Format, View, Window, and Help menus (with their own menu items representing commands). These menu items, as well as all of the menu items of the File menu, are connected to the appropriate first-responder action methods. For example, the About menu item is connected to the orderFrontStandardAboutPanel: action method that displays a standard About window. The following table lists the File menu first-responder action connections that already exist in the template application. File menu command New Open... First-responder action
newDocument: openDocument:

Open Recent > Clear Menu clearRecentDocuments: Close Save Save As... Revert to Saved Page Setup... Print...
performClose: saveDocument: saveDocumentAs: revertDocumentToSaved: runPageLayout: printDocument:

The template has similar ready-made connections for the Edit, Format, View, Window, and Help menus. If your application does not support any of the supplied actions (for example, printing), you should remove the associated menu items (or menu) from the nib. Alternatively, you may want to repurpose and rename menu commands and action methods to suit your own application, taking advantage of the menu mechanism in the template to ensure that everything is in the right place.

46

Implementing the Application Menu Bar


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 2

The Core Application Design

Connect Menu Items to Your Code or Your First Responder


For your applications custom menu items that are not already connected to action methods in objects or placeholder objects in the nib file, there are two common techniques for handling menu commands in a Mac OS X application:

Connect the corresponding menu item to a first responder method. Connect the menu item to a method of your custom application object or your application delegate object.

Of these two techniques, the first is more common given that many menu commands act on the current document or its contents, which are part of the responder chain. The second technique is used primarily to handle commands that are global to the application, such as displaying preferences or creating a new document. It is possible for a custom application object or its delegate to dispatch events to documents, but doing so is generally more cumbersome and prone to errors. In addition to implementing action methods to respond to your menu commands, you must also implement the methods of the NSMenuValidation protocol to enable the menu items for those commands. Step-by-step instructions for connecting menu items to action methods in your code are given in Designing User Interfaces in Xcode. For more information about menu validation and other menu topics, see Application Menu and Pop-up List Programming Topics.

Implementing the Application Menu Bar


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

47

CHAPTER 2

The Core Application Design

48

Implementing the Application Menu Bar


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 3

Implementing a Document-Based Design

A multiwindow, document-based application is one of the more common types of applications. Documents are identically contained but uniquely composed sets of data that can be stored in files. Traditional word processors and spreadsheet applications are examples of document-based applications. One application typically handles multiple documents, each in its own window, and often displays more than one document at a time. Cocoa provides excellent support for this design through a subsystem called the document architecture.

Three Key Objects Support Document-Based Applications


There are three major classes in the document architecture: NSDocumentController, NSDocument, and NSWindowController. Objects of these classes divide and orchestrate the work of creating, saving, opening, and managing the documents of an application. They are arranged in a tiered one-to-many relationship, as depicted in Figure 3-1. An application can have only one NSDocumentController object, which creates and manages one or more NSDocument objects (one for each New or Open operation). In turn, an NSDocument object creates and manages one or more NSWindowController objects, one for each of the windows displayed for a document. In addition, some of these objects have responsibilities analogous to NSApplication and NSWindow delegates, such as approving major events like closing and quitting. These objects are also described in The Core Objects for All Cocoa Applications (page 27) and Additional Core Objects for Multiwindow Applications (page 30).

Three Key Objects Support Document-Based Applications


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

49

CHAPTER 3

Implementing a Document-Based Design

Figure 3-1

Relationships among NSDocumentController, NSDocument, and NSWindowController objects


NSDocumentController Owns and manages

NSDocument

NSDocument

NSWindowController

NSWindowController

NSWindowController

NSDocumentController Creates and Manages Documents


An applications NSDocumentController object is a high-level controller. It has the following primary responsibilities:

A document controller creates empty documents in response to the New item in the File menu It creates documents initialized with data from a file in response to the Open item in the File menu It tracks and manages those documents It handles document-related menu items, such as Open Recent

When a user chooses New from the File menu, the NSDocumentController object gets the appropriate NSDocument subclass from the applications Information property list and allocates and initializes an instance of this class. Likewise, when the user chooses Open, the NSDocumentController object displays the Open panel, gets the users selection, finds the NSDocument subclass for the file, allocates an instance of the class, and initializes it with data from the file. In both cases, the NSDocumentController object adds a reference to the document object to an internal list to help manage its documents.

50

Three Key Objects Support Document-Based Applications


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 3

Implementing a Document-Based Design

NSDocumentController is hard-wired to respond appropriately to certain application events, such as when

the application starts up, when it terminates, when the system is shutting down, and when documents are opened or printed from the Finder. Most of the time, you can use this class as is to manage your applications documents. Alternatively, you can make the application delegate a custom object and implement the delegate methods invoked as a result of the same events, and these methods will be invoked instead (see NSApplicationDelegate Protocol Reference). Generally, the default NSDocumentController object is an adequate application controller for most situations. If the default object does not meet all of your requirements for an application controllerfor example, handling user preferences or responding to uncommon application delegate messagesusually you should create a separate controller object (instead of subclassing NSDocumentController). However, it is possible to create a custom subclass to tweak your applications document-handling behavior, as described in You Rarely Need to Subclass NSDocumentController (page 58).

NSDocument Presents and Stores Document Data


NSDocument is the base class for document objects in the application architectureyou must create an NSDocument subclass for each type of document your application handles. When your application is running, it has an NSDocument-based object for each open document to manage the persistent data associated with that document. As such, an NSDocument object is a model-controller with the following responsibilities:

An NSDocument object manages the display and capture of the data in its windows (with the assistance of its window controllers) It loads and stores (that is, reads and writes) the persistent data associated with its document It responds to action messages to save, print, revert, and close documents It runs and manages the Save and Page Setup dialogs

A fully implemented NSDocument object also knows how to track its edited status, perform undo and redo operations, print document data, and validate its menu items. Although these behaviors arent completely provided by default, the NSDocument object does assist the developer in implementing each, in the following ways:

For tracking edited status, NSDocument provides an API for updating a change counter. For undo and redo operations, NSDocument lazily creates an NSUndoManager instance when one is requested, responds appropriately to Undo and Redo menu commands, and updates the change counter when undo and redo operations are performed. For printing, NSDocument facilitates the display of the Page Setup dialog and the subsequent modification of the NSPrintInfo object used in printing. To do this, subclasses of NSDocument must override printOperationWithSettings:error:. To validate menu items, NSDocument implements validateUserInterfaceItem: to manage the enabled state of the Revert and Save As menu items. If you want to validate other menu items, you can override this method, but be sure to invoke the superclass implementation. For more information on menu item validation, see NSUserInterfaceValidations Protocol Reference.

Three Key Objects Support Document-Based Applications


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

51

CHAPTER 3

Implementing a Document-Based Design

When designing your document objects, you should always maintain a clean separation between these data-handling activities of the document object itself and the code for managing the visual presentation of that data. The document object is responsible for the data, including the reading and writing of that data to disk. The visual presentation of that data is the responsibility of the associated window controller object. Keeping a clean separation between these two activities makes for a more modular design that can be updated more easily in the future. Any and all objects that are part of the persistent state of a document should be considered part of that documents model. Sometimes the NSDocument object itself has some data that would be considered part of the model. For example, the Sketch example application has a subclass of NSDocument named SKTDrawDocument; objects of this class might have an array of SKTGraphic objects that comprises the model of the document. In addition to the actual SKTGraphic objects, the SKTDrawDocument object contains some data that should technically be considered part of the model, because the order of the graphics within the documents array matters in determining the front-to-back ordering of the SKTGraphic objects. An NSDocument object should not contain or require the presence of any objects that are specific to the applications user interface. Although a document can own and manage NSWindowController objectswhich present the document visually and allow the user to edit itit should not depend on these objects being there. For example, it might be desirable to have a document open in your application without having it visually displayed. For details about subclassing NSDocument, see You Must Subclass NSDocument (page 53). If you have a large data set or require a managed object model, you may want to use NSPersistentDocument, a subclass of NSDocument, to create a document-based application that uses Core Data. For more information, see Core Data Starting Point.

NSWindowController Manages One Document Window


An NSWindowController object manages one window associated with a document, which is usually stored in a nib file. As such, it is a view-controller. If a document has multiple windows, each window has its own window controller. For example, a document might have a main data-entry window and a window that lists records for selection; each window would have its own NSWindowController object. When requested by its owning NSDocument object, an NSWindowController object loads the nib file containing a window, displays the window, and sets itself as the Files Owner of the nib file. It also assumes responsibility for closing windows properly (after ensuring that the data they display is saved). Window controllers keep track of their window using their window outlet. The window outlet of your window controller (set as the Files Owner in your nib file) should be connected to the window for which your window controller is responsible. While not required, its often convenient to set up your window controller as the delegate of the window it manages. In your nib file, connect the delegate outlet of the window your window controller is managing to the object that represents your window controllerspecifically, the Files Owner object.

52

Three Key Objects Support Document-Based Applications


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 3

Implementing a Document-Based Design

Note: NSWindowController does not depend on being the controlled window's delegate to do its job, and it doesn't implement any NSWindow delegate methods. A subclass of NSWindowController, however, is a fine place to put implementations of NSWindow delegate methods, and if you do so you'll probably need to connect the delegate outlet of the window to the File's Owner of the nib file as described. But you do not have to do so for NSWindowController itself to work properly. The Xcode document-based application template does not subclass NSWindowController, and you do not need to do so if you are writing a simple application. However, if you are writing an application with more advanced requirements, you will almost certainly want to do so. For more information, see You May Want to Subclass NSWindowController (page 56).

Subclassing Objects in the Document Architecture


You can create a document-based application without writing much code. If your requirements are minimal, you can use the default NSWindowController instance and default NSDocumentController instance provided by the AppKit framework. You have only to create a document project, compose the human interface, implement a subclass of NSDocument, and add any other custom classes or behavior required by your application. If your application requirements are more complex, you can customize the default object architecture through subclassing and delegation, as described in this section. Table 3-1 summarizes the object architecture and subclass requirements of a document-based application. Table 3-1 Class
NSDocument NSWindowController

Document Architecture Objects and Subclasses Number of objects Subclassing 1 per document 1 per window Required Optional (but likely) Optional (but unlikely)

NSDocumentController 1 per application

You Must Subclass NSDocument


Every application that uses the document architecture must create at least one subclass of NSDocument. To create a document-based Cocoa application, you choose the Xcode template presented in the New Project dialog. When you do this, you get a new application project that already contains a subclass of NSDocument and nib files for your document and application menu. Empty method implementations are provided for loading and saving data; comments explain what you need to fill in. Empty methods are also provided for document initialization, specifying the window nib file name, and adding code that is called after the document nib file is loaded. Without writing any additional code, you should be able to compile and run the application. When you first launch the application, you see an untitled document with an empty window. The File menu commands all do something reasonable, such as bringing up a Save panel or Open panel. Because you have not yet defined any types or implemented loading and saving, you can't open or save anything.

Subclassing Objects in the Document Architecture


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

53

CHAPTER 3

Implementing a Document-Based Design

Subclass only NSDocument for documents that have only one window and are simple enough that there isnt much benefit in splitting the controller layer into a model-controller and a view-controller. The NSDocument subclass provides storage for the model and the ability to load and save document data. It also has any outlets and actions required for the user interface. It overrides the windowNibName method to return the nib file name used for documents of this type. The NSDocument object automatically creates an NSWindowController object to manage that nib file, but the NSDocument object serves as the Files Owner proxy object for the nib file. The document architecture requires that you override some NSDocument methods (among several choices), and recommends overriding several others in certain situations. You must override one reading and one writing method. In the simplest case, you can override the two data-based reading and writing methods. If you need to deal with the location of the file, then you can override the URL-based reading and writing methods instead. If your application supports document files that are file packages, then you can override the file-wrapper reading and writing methods instead. Note that these methods read and write entire files at once. If your application has a large data set, you may want instead to read and write pieces of your files at different times. For information about reading and writing files, see File System Programming Guide.

Initializing a New Document


The initializers of NSDocument are an issue for subclasses. The init method is the designated initializer, and it is invoked by the other initializers initWithType:error: and initWithContentsOfURL:ofType:error:. If you must perform initializations that must be done when creating new documents but not when opening existing documents, override initWithType:error:. If you have any initializations that apply only to documents that are opened, override initWithContentsOfURL:ofType:error:. If you have general initializations, override init. In all three cases, be sure to invoke the superclass implementation as the first thing. If you override init, make sure that your override never returns nil. Returning nil could cause a crash (in some versions of AppKit) or present a less than useful error message. If, for example, you want to prevent the creation or opening of documents under circumstances unique to your application, override a specific NSDocumentController method instead. Note: If you dont want to open an untitled file when the application is launched or activated, implement the application delegate method applicationShouldOpenUntitledFile: to return NO. If you do want to open an untitled file when launched, but don't want to open an untitled file when already running and activated from the dock, you can instead implement the delegates applicationShouldHandleReopen:hasVisibleWindows: method to return NO.

Reading Document Data


Typically, document-based applications need to override one of the following methods to read document data:

readFromData:ofType:error: readFromURL:ofType:error: readFromFileWrapper:ofType:error:

54

Subclassing Objects in the Document Architecture


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 3

Implementing a Document-Based Design

The readFromData:ofType:error: method may be overridden to convert an NSData object containing document data of a certain type into the documents internal data structures and display that data in a document window. The NSData object usually results from the document reading a document file. The readFromURL:ofType:error: method reads data from a file, creates an NSFileWrapper object from it, and gives this object to the readFromFileWrapper:ofType:error: method. If this object represents a simple file, it is passed to the readFromData:ofType:error: method for processing; otherwise (if the object represents a directory), the readFromFileWrapper:ofType:error: method can be overridden to handle the situation. Subclasses can override any of these methods instead of the data-based reading methods if the way NSDocument reads and writes document data is not sufficient; their override implementations, however, must also assume the loading duties of the data-based reading methods.

Concurrent Document Opening


A class method of NSDocument, canConcurrentlyReadDocumentsOfType:, enables your NSDocument subclass to load documents concurrently, using background threads. You can override this method to return YES to enable this capability. When you do, initWithContentsOfURL:ofType:error: executes on a background thread when opening files via the Open panel or from the Finder. This override allows concurrent reading of multiple documents and also allows the application to be responsive while reading a large document. The default implementation of this method returns NO. A subclass override should return YES only for document types whose reading code can be safely executed concurrently, in non-main threads. If a document type relies on shared state information, you should return NO for that type. Also, you should disable undo registration during document reading, which is a good idea even in the absence of concurrency.

Saving Document Data


Typically, document-based applications need to override one of the following methods to write document data:

dataOfType:error: writeToURL:ofType:error: fileWrapperOfType:error: writeToURL:ofType:forSaveOperation:originalContentsURL:error:

The dataOfType:error: method may be overridden to create and return document data (packaged as an NSData object) of a supported type, usually in preparation for writing that data to a file. By default, the writeToURL:ofType:error: method writes data to a file after obtaining the data from the fileWrapperOfType:error: method, which gets it from the dataOfType:error: method. You can override the writeToURL:ofType:forSaveOperation:originalContentsURL:error: method instead of one of the other three, simple writing methods if your document writing machinery needs access to the on-disk representation of the document revision that is about to be overwritten. This method is responsible for doing document writing in a way that minimizes the danger of leaving the disk to which writing is being done in an inconsistent state in the event of an application crash, system crash, hardware failure, power outage, and so on.

Subclassing Objects in the Document Architecture


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

55

CHAPTER 3

Implementing a Document-Based Design

By default, when NSDocument runs the Save panel and the document has multiple writable document types, NSDocument inserts an accessory view near the bottom of the panel. This view contains a pop-up menu of the writable types. If you dont want this pop-up menu, override shouldRunSavePanelWithAccessoryView to return NO. You can also override prepareSavePanel: to customize of the Save panel. Warning: If your NSDocument subclass uses an NSTextView object to display its data and it has an active NSUndoManager object, your document-writing method override (or another override called during document saving) should send the NSTextView object a breakUndoCoalescing message. Send this message when saving the text views contents to preserve proper tracking of unsaved changes and the documents dirty state. See Implementing Undo (page 61) for more information about supporting undo. You can define types for your application using the Xcode Property List editor, which provides an editable list of document types as described in Supported Document Types Are Declared in the Application Property List (page 59).

You May Want to Subclass NSWindowController


If your document has only one window but it is complex enough that youd like to split up some of the logic in the controller layer, you can subclass NSWindowController as well as NSDocument. In this way, you can add specific knowledge of the applications view layer that the window controller is responsible for managing. Any outlets and actions and any other behavior that is specific to the management of the user interface goes into the NSWindowController subclass. Especially for larger applications, splitting the controller duties between two classes makes a lot of sense. This strategy allows you to have documents that are open, but not onscreen, to avoid having to allocate memory and other resources of a front end that may not be used in some circumstances.

Reasons to Subclass NSWindowController


If your document requires or allows multiple windows for a single document, that is a good reason to subclass NSWindowController. For example, a CAD program could need to present front, top, and side views, as well as a rendered 3D view of a document. When it does, you might want to have one or more subclasses of NSWindowController to manage the different kinds of windows that your document needs, and so you must create one of each in makeWindowControllers. Some applications need only one window for a document but want to allow the user to create several copies of the window for a single document (sometimes this is called a multiple-view document) so that the user can have each window scrolled to a different position or displayed differently, such as at a different scale. In this case, your makeWindowControllers override may create only one NSWindowController object, but would have be a menu command or similar control that allows the user to create others. Another reason to subclass NSWindowController is to customize your document window titles. To customize a document's window title properly, subclass NSWindowController and override windowTitleForDocumentDisplayName:. If your application requires even deeper customization, override synchronizeWindowTitleWithDocumentName.

56

Subclassing Objects in the Document Architecture


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 3

Implementing a Document-Based Design

How to Subclass NSWindowController


Once you've decided to subclass NSWindowController, you need to change the default document-based application setup. First, add any Interface Builder outlets and actions for your document's user interface to the NSWindowController subclass instead of to the NSDocument subclass. The NSWindowController subclass instance should be the Files Owner for the nib file because that creates better separation between the view-related logic and the model-related logic. Some menu actions can still be implemented in the NSDocument subclass. For example, Save and Revert are implemented by NSDocument, and you might add other menu actions of your own such as an action for creating new views on a document. Second, instead of overriding windowNibName in your NSDocument subclass, override makeWindowControllers. In makeWindowControllers, create at least one instance of your custom NSWindowController subclass and use addWindowController: to add it to the document. If your document always needs multiple controllers, create them all here. If your document will support multiple views but by default has one, create the controller for the default view here and provide user actions for creating other views. You should not force the windows to be visible in makeWindowControllers. NSDocument does that for you if its appropriate.

An NSWindowController Subclass Manages Nib Files


An NSWindowController object expects to be told what nib file to load (through its initWithWindowNib... methods), because it is a generic implementation of the default behavior for all window controllers. However, when you write a subclass of NSWindowController, that subclass is almost always designed to control the user interface contained in a particular nib file, and your subclass would not work with a different nib file. It is therefore inconvenient and error-prone for the client of the subclass to have to tell it which nib file to load. This is solved by overriding the init method to call the superclass's initWithWindowNibName: method with the correct nib name. Now, clients just use init, and the controller has the correct nib file. You can also override the initWithWindowNib... methods to log an error, because no clients should ever try to tell your subclass which nib file to use. This is a good idea for any NSWindowController subclass designed to work with a specific nib file. You should do otherwise only if you are extending only the basic functionality of NSWindowController in your subclass and have not tied that functionality to any particular nib file. An NSWindowController object without an associated NSDocument object is useful by itself. NSWindowController can be used as the base class for auxiliary panel controllers in order to gain the use of its nib management abilities. One common standalone use of NSWindowController subclasses is as controllers for shared panels such as find panels, inspectors, or preferences panels. For example, the Sketch example application uses NSWindowController subclasses for its various secondary panels. In this case, you can make an NSWindowController subclass that implements a shared instance method. For example, you could create a PreferencesController subclass with a sharedPreferenceController class method that creates a single instance the first time it is called and returns that same instance on all subsequent calls. Because your subclass derives from NSWindowController, you can just tell it the name of your preferences nib file and it will handle loading the nib file and managing the window automatically. You add your own outlets and actions, as usual, to hook up the specific user interface for your panel and add methods to manage the panels behavior.

Subclassing Objects in the Document Architecture


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

57

CHAPTER 3

Implementing a Document-Based Design

If you have multiple window controllers for a single document, you may want to explicitly control document closing. By default, a document closes when its last remaining window controller closes. However, if you want the document to close when a particular window closesthe documents main window, for examplethen you can send the main window controller a setShouldCloseDocument: message with a value of YES.

You Rarely Need to Subclass NSDocumentController


Usually, you should not need to subclass NSDocumentController. Almost anything that can be done by subclassing can be done just as easily by the applications delegate. However, it is possible to subclass NSDocumentController if you need to. For example, if you need to customize the Open panel, an NSDocumentController subclass is needed. You can override the NSDocumentController method runModalOpenPanel:forTypes: to customize the panel or add an accessory view. The addDocument: and removeDocument: methods are provided for subclasses that want to know when documents are opened or closed. There are two ways to subclass NSDocumentController:

You can make an instance of your subclass in your applications main nib file. This instance becomes the shared instance. You can create an instance of your subclass in your application delegates applicationWillFinishLaunching: method.

The first NSDocumentController object to be created becomes the shared instance. The AppKit framework creates the shared instance (using the NSDocumentController class) during the finish launching phase of application startup. So if you need a subclass instance, you must create it before AppKit does.

Xcode Provides a Document-Based Application Template


To expedite the development of document-based applications, Xcode provides a Cocoa Application project template with an option checkbox labeled Create Document-Based Application. This project template provides the following things:

A skeletal NSDocument subclass implementation By default, the subclass of NSDocument is named MyBigDocument. The project includes MyBigDocument.h and MyBigDocument.m. The latter file includes commented blocks for important methods, including an init method that initializes and returns self, providing a location for subclass-specific initialization. It also includes a fully implemented windowNibName method that returns the name of the document window nib file, MyBigDocument, and an override of windowControllerDidLoadNib:. In addition, the template includes skeletal implementations of the dataOfType:error: and readFromData:ofType:error: basic writing and reading methods showing how to set the value of the NSError object passed as an indirect parameter.

A nib file for the application's document

58

Xcode Provides a Document-Based Application Template


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 3

Implementing a Document-Based Design

This nib file is named MyBigDocument.xib (assuming you use the default subclass name MyBigDocument). The subclass of NSDocument is made Files Owner of the nib file. It has an outlet named window connected to its window object. The window has only a text field on it, with the words "Your document contents here".

The applications main menu nib file The main menu nib file, named MainMenu.xib, contains an application menu (named with the applications name), a File menu (with all of its associated document commands), an Edit menu (with text editing commands and Undo and Redo menu items), and Format, View, Window, and Help menus (with their own menu items representing commands). These menu items, as well as all of the menu items of the File menu, are connected to the appropriate first-responder action methods. For example, the About menu item is connected to the orderFrontStandardAboutPanel: action method that displays a standard About window. See Implementing the Application Menu Bar (page 46) for more information about the main menu nib file provided by the Xcode application templates.

The application's information property list The <applicationName>-Info.plist file contains placeholder values for global application keys, as well as for the CFBundleDocumentTypes key, whose associated value is a dictionary containing key-value pairs specifying information about the document types the application works with including their NSDocument subclass.

The Xcode Cocoa Application project template dialog also provides an option checkbox labeled Use Core Data which, if used in conjunction with the Create Document-Based Application option, creates a document-based application containing a subclass of NSPersistentDocument. For a tutorial that shows how to implement a simple document-based application, see Building a Text Editor in 15 Minutes.

Supported Document Types Are Declared in the Application Property List


Applications use an information property list file, which is stored in the applications bundle and named <applicationName>-Info.plist by default, to specify various information that can be used at runtime. Document-based applications use this property list to specify the document types the application can edit or view. This information is used by the application and system entities such as the Finder and Launch Services as well. For a new application, you should create a type with a name and extension that make sense for your application. You can add more types as well. The application's main, or most important, document type should be listed first in the list of types. This is the type that NSDocumentController uses by default when the user asks for a new document. For example, when the NSDocumentController object creates a new document or opens an existing document, it searches the property list for such items as the document class that handles a document type, the uniform type identifier (UTI) for the type, and whether the application can edit or only view the type.

Supported Document Types Are Declared in the Application Property List


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

59

CHAPTER 3

Implementing a Document-Based Design

Similarly, Launch Services may use information about the icon file for the type. Document types information is associated with the CFBundleDocumentTypes key as an array of dictionaries, each of which contains the key-value pairs that define the document type. If your application has some types that it can read but not write, you can declare this by setting the role for those types to Viewer instead of Editor in Xcode. If your application has some types that it can write but not read, you can declare this by using the NSExportableAs key. You can include the NSExportableAs key in the type dictionary for another type that your document class supports, usually the type dictionary for the most native type for your document class. Its value is an array of type names that your document class can write, but not read. The Sketch example uses this key to allow it to export TIFF and EPS images even though it cannot read those types. Write-only types can be chosen only when doing Save As operations. They are not allowed for Save operations. Sometimes an application might understand how to read a type, but not how to write it, and when it reads documents of that type, it should automatically convert them to another type that you can write. An example of this would be an application that can read documents from an older version or from a competing product. It might want to read in the old documents and automatically convert them to the new native format. The first step is to add the old type as a read-only type. By doing this, your application is able to open the old files, but they come up as untitled files. If you want to automatically convert them to be saved as your new type, you can override the readFrom... methods in your NSDocument subclass to call super and then reset the filename and type afterwards. You should use setFileType: and setFileURL: to set an appropriate type and name for the new document. When setting the filename make sure to strip the filename extension of the old type from the original filename, if it is there, and add the extension for the new type. For more information about the property list, see The Information Property List File (page 80).

Multiple Document Types Use Multiple NSDocument Subclasses


The document architecture provides support for applications that handle multiple types of documents, each type using its own subclass of NSDocument. For example, you could have an application that enables users to create text documents, spreadsheets, and other types of documents, all in a single application. Such different document types each require a different user interface encapsulated in a unique NSDocument subclass. If your multiple-document-type application opens only existing documents, you can use the default NSDocumentController instance, because the document type is determined from the file being opened. However, if your application creates new documents, it needs to choose the correct type. The NSDocumentController action method newDocument: creates a new document of the first type listed in the applications array of document types configured in the Info.plist file. But this does not work for applications that support several distinct types of document. If your application cannot determine which type to create depending on circumstances, you must provide a user interface allowing the user to choose which type of document to create. You can create your own new actions, either in your applications delegate or in an NSDocumentController subclass. You could create several action methods and have several different New menu items, or you could have one action that asks the user to pick a document type before creating a new one.

60

Multiple Document Types Use Multiple NSDocument Subclasses


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 3

Implementing a Document-Based Design

Once the user selects a type, your action method can use the NSDocumentController method makeUntitledDocumentOfType:error: to create a document of the correct type. After creating the document, your method should add it to the document controller's list of documents, and it should send the document makeWindowControllers and showWindows messages. Alternatively, if you subclass NSDocumentController, you can override the defaultType method to determine the document type and return it, when the user chooses New from the File menu.

The Document Architecture Has Built-in Undo Support


Undo is not always easy to implement, but at least the mechanism for implementing it is straightforward. By default, an NSDocument object has its own NSUndoManager object. The NSUndoManager class enables you to construct invocations that do the opposite of a previous action.

Implementing Undo
The key to implementing undo properly is to have well-defined primitives for changing your document. Each model object, plus the NSDocument subclass itself, should define the set of primitive methods that can change it. Each primitive method is then responsible for using the undo manager to enqueue invocations that undo the action of the primitive method. For example, if you decide that setColor: is a primitive method for one of your model objects, then inside of setColor: your object would do something like the following:
[[[myDocument undoManager] prepareWithInvocationTarget:self] setColor:oldColor]

This message causes the undo manager to construct and save an invocation. If the user later chooses Undo, the saved invocation is invoked and your model object receives another setColor: message, this time with the old color. You dont have to keep track of whether commands are being undone to support redo. In fact, the way redo works is by watching what invocations get registered as the undo is happening and recording them on the redo stack. You can use the setUndoManager: method if you need to use a subclass or otherwise need to change the undo manager used by the document. Because many discrete changes might be involved in a user-level action, all the undo registrations that happen during a single cycle of the event loop are usually grouped together and are undone all at once. NSUndoManager has methods that allow you to control the grouping behavior further if you need to. Another aspect of good undo implementation is to provide action names so that the Undo and Redo menu items can have more descriptive titles. Undo action names are usually best set in action methods instead of the change primitives in your model objects. This is because many primitive changes might go into one user action, or different user actions might result in the same primitives being called in different ways. The Sketch example application implements undo in action methods. For more information on supporting undo in your application, see Undo Architecture.

The Document Architecture Has Built-in Undo Support


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

61

CHAPTER 3

Implementing a Document-Based Design

Implementing Partial Undo


Because NSUndoManager does multiple-level undo, do not implement undo for only a subset of the possible changes to your document. The undo manager relies on being able to reliably take the document back through history with repeated undos. If some changes get skipped, the undo stack state is no longer synchronized with the contents of the document. Depending on your architecture, that situation can cause problems that range from merely annoying to fatal. For example, imagine that you have a drawing program that is able to undo a resize, but not a delete operation. If the user selects a graphic and resizes it, the undo manager gets an invocation that can undo that resize operation. Now the user deletes that graphic (which is not recorded for undo). If users now try to undo, nothing happens (at the very least), because the graphic that was resized is no longer there and undoing the resize cant have any visual effect. At worst, the application might crash trying to send a message to a freed object. So when you implement undo, remember that everything that causes a change to the document should be undoable. If there are some changes that you cannot undo, there are two ways to handle the situation when a user makes such a change. If you can be absolutely sure that the change has no relationship to any other changes that can happen to the document (that is, something totally independent of all the rest of the contents of the document has changed), then you do not register any undo action for that change. On the other hand, if the change does have some relationship to the rest of the document contents, remove all actions from the undo manager when such a change takes place. Such changes then mark points of no return in your user experience. When designing your application and document format, you should strive to avoid the need for these point of no return operations.

Managing the Change Count


Because of undo support, the document must keep more information than just whether the document is dirty or clean. If a user opens a file, makes five changes, and then chooses Undo five times, the document should once again be clean. But if the user chooses Undo only four times, the document is still dirty. The NSDocument object keeps a change count to deal with this. The change count can be modified by sending an updateChangeCount: message with one of the supported change types. The supported changes are NSChangeDone, NSChangeUndone, and NSChangeCleared. The NSDocument object itself clears the change count whenever the user saves or reverts the document. If the document has an undo manager, it observes the undo manager and automatically updates the change count when changes are done, undone, or redone.

Not Supporting Undo


If you don't want to support undo at all, first send the setHasUndoManager: message with a parameter value of NO to your document. This causes the document never to get an undo manager. Without an undo manager (and without undo support from your model objects), the document cannot automatically track its dirty state. So, if you aren't implementing undo, you need to send an updateChangeCount: message explicitly whenever your document is edited.

62

The Document Architecture Has Built-in Undo Support


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 3

Implementing a Document-Based Design

The Document Architecture Supports Robust Error Handling


Many NSDocument and NSDocumentController methods include as their last parameter an indirect reference to an NSError object. These are methods that create a document, write a file, access a resource, or perform a similar operation. Two examples of NSDocumentController methods that take error parameters are openUntitledDocumentAndDisplay:error:, which creates a new untitled document, and openDocumentWithContentsOfURL:display:error:, which opens a document located by a URL. In case of failure, these methods directly return nil and, in the last parameter, indirectly return an NSError object that describes the error. Before calling such a method, client code that is interested in a possible error declares an NSError object variable and passes the address of the variable in the error parameter. If the clients are not interested in the error, they pass NULL in the error parameter. Using NSError objects gives Cocoa applications the capability to present much more useful error messages to the user, including detailed reasons for the error condition, suggestions for recovery, and even a mechanism for attempting programmatic recovery. In addition, AppKit handles presenting the error to the user. For detailed information about NSError handling see Error Handling Programming Guide. Important: Cocoa methods that take error parameters in the Cocoa error domain are guaranteed to return NSError objects. So, if you override such a method, you must adhere to the following rule: A method that takes an error:(NSError **)outError argument must set the value of *outError to point to an NSError object whenever the method returns a value that signals failure (typically nil or NO) and outError != NULL. If you override a method that takes an error parameter and you call the superclass implementation, you don't need to set outError yourself. Pass it the error argument that your override received when invoked. If you override such a method to prevent some action but you don't want an error alert to be presented to the user, return an error object whose domain is NSCocoaErrorDomain and whose code is NSUserCancelledError. The AppKit framework presents errors through the NSApplication implementations of the presentError: and presentError:modalForWindow:delegate:didPresentSelector:contextInfo: methods declared by NSResponder. Those implementations silently ignore errors whose domain is NSCocoaErrorDomain and whose code is NSUserCancelledError. So, for example, if your override of openDocumentWithContentsOfURL:display:error: wanted to avoid presenting an error to the user, it could set an error object as shown in the following fragment:
if (outError) { *outError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]; }

Documents Are Automatically Saved


In Mac OS X v10.7 and later, users dont need to save documents explicitly or be concerned about losing unsaved changes. Instead, the system automatically writes document data to disk as necessary. Your NSDocument subclass opts into this behavior by overriding the autosavesInPlace class method to return

The Document Architecture Supports Robust Error Handling


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

63

CHAPTER 3

Implementing a Document-Based Design

YES. The ideal baseline for saveless documents is this: The document data that users see in an application

window is identical to the document on disk at all times. For practical reasons, the system does not attempt to save every change immediately, but it saves documents often enough and at the correct times to ensure that the document in memory and the one on disk are effectively the same. Part of the implementation of saveless documents is file coordination, a mechanism that serializes access to files among processes to prevent inconsistencies due to nonsequential reading and writing. Applications use file coordination so that users dont need to remember to save document changes before causing the documents file to be read by another application. Document-based Cocoa applications use file coordination automatically. Automatic document saving is supported by the implementation of autosaving in place. Autosaving in place and autosaving elsewhere both protect against the user losing work due to application crashes, kernel panics, and power failures. However, autosaving in place differs from autosaving elsewhere in that it overwrites the actual document file rather than writing a new file next to it containing the autosaved document contents. (Autosaving in place performs a safe save by writing to a new file first, then moving it into the place of the document file when done.) The document architecture still uses autosaving elsewhere to save untitled documents that have content but have not been explicitly saved and named by the user. In this case, untitled documents are autosaved in ~/Library/Autosave Information. In addition, NSDocument saves earlier revisions of documents elsewhere, giving the user access to previous versions. The saveless-documents model automates crash protection but preserves the ability for users to save documents explicitly. It also automates maintenance of multiple older versions. Users can save immediately in the traditional way (by choosing File > Save or pressing Command-S). For an untitled document, an explicit Save command presents a dialog enabling the user to name the document and specify the location where it is to be written to disk. You should not invoke the autosavesInPlace method to find out whether autosaving is being done. Instead, the document architecture passes one of two new autosaving-related enumerators as an NSSaveOperationType parameter to your overrides of the NSDocument methods beginning with save... and write..., and you can examine those values. The autosave enumerators are NSAutosaveInPlaceOperation and NSAutosaveElsewhereOperation. The old NSAutosaveOperation enumerator is equivalent to NSAutosaveElsewhereOperation and is deprecated in Mac OS X v10.7. Before you enable autosaving, consider the saving performance of your application. If your application saves quickly, there is little reason not to enable it. But if your application saves slowly, enabling autosaving could cause periodic blocking of your user interface while saving is happening. So, for example, if you have already implemented the autosaving behavior introduced in Mac OS X v10.4 (sending setAutosavingDelay: to the NSDocumentController object with a nonzero value), then your applications saving performance is probably acceptable, and opting into autosaving in place is as simple as overriding autosavesInPlace to return YES. Otherwise, you may first need to address any issues with your document model or saving logic that could hinder saving performance.

Document Saving Is Asynchronous


In Mac OS X v10.7 and later, NSDocument saves asynchronously, so that document data is written to a file on a background thread. In this way, even if writing is slow, the applications user interface remains responsive. You can override the method canAsynchronouslyWriteToURL:ofType:forSaveOperation: to return

64

Document Saving Is Asynchronous


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 3

Implementing a Document-Based Design

YES to enable asynchronous saving. In this case, NSDocument creates a separate writing thread and invokes writeSafelyToURL:ofType:forSaveOperation:error: on it. However, the main thread remains blocked until an object on the writing thread invokes the unblockUserInteraction method.

When unblockUserInteraction is invoked, the application resumes dequeueing user interface events, the user is able to continue editing the document, even if the writing of document data takes some time. The right moment to invoke unblockUserInteraction is when an immutable snapshot of the documents contents has been taken, so that writing out the snapshot of the documents contents can continue safely on the writing thread while the user continues to edit the document on the main thread. For more information on asynchronous document saving, see Application Kit Release Notes (Lion) and comments in the NSDocument header file.

Users Can Browse Document Versions


The document architecture implements the Versions feature of Mac OS X v10.7 in the behavior of NSDocument. An NSDocument subclass adopts autosaving in place by returning YES from +autosavesInPlace, as described in Documents Are Automatically Saved (page 63), and adopting autosaving in turn enables version browsing. After a document has been named and saved, the Save menu item and Command-S keyboard equivalent are replaced by the Save a Version menu item. This command saves a version of the document identified by date and time. The user can choose File > Revert to Saved, or choose Browse All Revisions from the pop-up menu in the upper-right corner of the document title bar, to display a dialog enabling the user to choose between the last saved version or an older version. Choosing an older version displays a Time Machinelike user interface that selects among all of the documents versions. If the user chooses to restore a previous version, the current document contents are preserved on disk, if necessary, and the file's contents are replaced with those of the selected version. By holding the Option key, the user can also choose to restore a copy of a previous version, which does not affect the current document contents. The user can also select and copy contents from them and paste them into the current document. When saving happens without user knowledge, it becomes easier for unintentional edits to get saved to disk, resulting in potential data loss. To help prevent autosaving unintentional edits, NSDocument tries to determine when a user has opened a document to read it, but not edit it. For example, if the document has not been edited for some period of time, it is locked for editing and opened only for reading. (The period after editing when the document is locked is settable by a user preference.) When an edit is made to the document, NSDocument offers the user the choice of canceling the change, creating a new document with the change, or allowing editing. A document that is preventing edits displays Locked in the upper-right corner of the title bar. The user can explicitly enable editing of the document by clicking on the Locked label and choosing Unlock in the pop-up menu. A document that has been changed since it was last opened and is therefore being actively autosaved in place displays Edited in the titlebar instead of Locked.

Windows Are Restored Automatically


The document architecture implements the Resume feature of Mac OS X v10.7, as described in User Interface Preservation (page 37), so that individual applications need to encode only information that is peculiar to them and necessary to restore the state of their windows. The document architecture implements the following steps in the window restoration process:

Users Can Browse Document Versions


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

65

CHAPTER 3

Implementing a Document-Based Design

1.

The NSWindowController method setDocument: sets the restoration class of document windows to the class of the shared NSDocumentController object. The NSWindow object invalidates its restorable state whenever its state changes by sending invalidateRestorableState to itself. At the next appropriate time, Cocoa sends the window an encodeRestorableStateWithCoder: message, and the window encodes identification and status information into the passed-in encoder. When the system restarts, Cocoa relaunches the application and sends the restoreWindowWithIdentifier:state:completionHandler: message to the NSApp application object. Applications can override this method to do any general work needed for window restoration, such as substituting a new restoration class or loading it from a separate bundle.
NSApp decodes the restoration class for the window, sends the restoreWindowWithIdentifier:state:completionHandler: message to the restoration class object, and returns YES.

2.

3.

4. 5.

The restoration class reopens the document and locates its window. Then it invokes the passed-in completion handler with the window as a parameter. Cocoa sends the restoreStateWithCoder: message to the window, which decodes its restorable state from the passed-in NSCoder object and restores the details of its content.

Although the preceding steps describe only window restoration, in fact every object inheriting from NSResponder has its own restorable state. For example, an NSTextView object stores the selected range (or ranges) of text in its restorable state. Likewise, an NSTabView object records its selected tab, an NSSearchField object records the search term, an NSScrollView object records its scroll position, and an NSApplication object records the z-order of its windows. An NSDocument object has state as well. Although NSDocument does not inherit from NSResponder, it implements many NSResponder methods, including these restoration methods. When the application is relaunched, Cocoa sends the restoreStateWithCoder: message to the relevant objects in turn, proceeding top-down: first to the NSApplication object, then to each NSWindow object, then to the NSWindowController object, then to the NSDocument object, and then to each view that has saved state.

66

Windows Are Restored Automatically


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 4

Implementing the Full-Screen Experience

Enabling a window of your application to assume full-screen mode, taking over the entire screen, provides users with a more immersive, cinematic experience. Full-screen appearance can be striking and can make your application stand out. From a practical standpoint, full-screen mode presents a better view of users data, enabling them to concentrate fully on their content without the distractions of other applications or the desktop. In full-screen mode, by default, the menu bar and Dock are autohidden; that is, they are normally hidden but reappear when the user moves the pointer to the top or bottom of the screen. A full-screen window does not draw its titlebar and may have special handling for its toolbar. The full-screen experience makes sense for many applications but not for all. For example, the Finder, Address Book, and Calculator would not provide any benefit to users by assuming full-screen mode. The same is probably true for most utility applications. Media-rich applications, on the other hand, can often benefit from full-screen presentation. Beginning with Mac OS X v10.7, Cocoa includes support for full-screen mode through APIs in NSApplication, NSWindow, and NSWindowDelegate protocol. When the user chooses to enter full-screen mode, Cocoa dynamically creates a space and puts the window into that space. This behavior enables the user to have one window of an application running in full-screen mode in one space, while using other windows of that application, as well as other applications, on the desktop in other spaces. While in full-screen mode, the user can switch between windows in the current application or switch applications. Applications that have implemented full-screen user interfaces in previous versions of Mac OS X should consider standardizing on the full-screen APIs in Mac OS X v10.7.

Full-Screen API in NSApplication


Full-screen support in NSApplication is provided by the presentation option NSApplicationPresentationFullScreen. You can find the current presentation mode via the NSApplication method currentSystemPresentationOptions, which is also key-value observable. You can set the presentation options using the NSApplication method setPresentationOptions:. (Be sure to observe the restrictions on presentation option combinations documented with NSApplicationPresentationOptions, and set the presentation options in a try-catch block to ensure that your program does not crash from an invalid combination of options.) A window delegate may also specify that the window toolbar be removed from the window in full-screen mode and be shown automatically with the menu bar by including NSApplicationPresentationAutoHideToolbar in the presentation options returned from the window:willUseFullScreenPresentationOptions: method of NSWindowDelegate.

Full-Screen API in NSApplication


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

67

CHAPTER 4

Implementing the Full-Screen Experience

Full-Screen API in NSWindow


The application must specify whether a given window can enter full-screen mode. Applications can set collection behavior using the setCollectionBehavior: method by passing in various constants, and the current options may be accessed via the collectionBehavior method. You can choose between two constants to override the window collection behavior, as shown in the following table: Constant
NSWindowCollectionBehaviorFullScreenPrimary

Behavior The frontmost window with this collection behavior becomes the full-screen window. A window with this collection behavior has a full-screen button in the upper right of its titlebar. Windows with this collection behavior can be shown in the same space with the full-screen window.

NSWindowCollectionBehaviorFullScreenAuxiliary

When a window goes into full-screen mode, the styleMask changes to NSFullScreenWindowMask to reflect the state of the window.The setting of the styleMask goes through the setStyleMask: method. As a result, a window can override this method if it has customization to do when entering or exiting full-screen. A window can be taken into or out of full-screen mode using the toggleFullScreen: method. If an application supports full-screen mode, it should add a menu item to the View menu with toggleFullScreen: as the action, and nil as the target.

Full-Screen API in NSWindowDelegate Protocol


The following notifications are sent before and after the window enters and exits full-screen mode:
NSWindowWillEnterFullScreenNotification NSWindowDidEnterFullScreenNotification NSWindowWillExitFullScreenNotification NSWindowDidExitFullScreenNotification

The window delegate has the following corresponding window delegate notification methods:
windowWillEnterFullScreen: windowDidEnterFullScreen: windowWillExitFullScreen: windowDidExitFullScreen:

The NSWindowDelegate protocol methods supporting full-screen mode are listed in Table 4-1.

68

Full-Screen API in NSWindow


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 4

Implementing the Full-Screen Experience

Table 4-1 Method

Window delegate methods supporting full-screen mode Description Invoked to allow the delegate to modify the full-screen content size. Returns the presentation options the window will use when transitioning to full-screen mode. Invoked when the window is about to enter full-screen mode.

window: willUseFullScreenContentSize:

window: willUseFullScreenPresentationOptions:

customWindowsToEnterFullScreenForWindow:

window: Invoked to start the window animation startCustomAnimationToEnterFullScreenWithDuration: into full-screen mode, including

transitioning to a new space. You can implement this method to perform custom animation with the given duration to be in sync with the system animation.
windowDidFailToEnterFullScreen:

Invoked if the window failed to enter full-screen mode. The system has started its animation out of full-screen mode, including transitioning back to the desktop space. You can implement this method to perform custom animation with the given duration to be in sync with the system animation. Invoked when the window is about to exit full-screen mode. Invoked if the window failed to exit full-screen mode.

customWindowsToExitFullScreenForWindow:

window: startCustomAnimationToExitFullScreenWithDuration: windowDidFailToExitFullScreen:

For details, see NSWindowDelegate Protocol Reference and the Application Kit Release Notes (Lion).

Full-Screen API in NSWindowDelegate Protocol


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

69

CHAPTER 4

Implementing the Full-Screen Experience

70

Full-Screen API in NSWindowDelegate Protocol


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 5

Supporting Common Application Behaviors

During the design phase of creating your application, you need to think about how to implement certain features that users expect in well-formed Mac OS X applications. Integrating these features into your application architecture can have an impact on the data model or may require cooperation between significantly different portions in the application.

You Can Prevent the Automatic Relaunch of Your Application


By default, as part of the Resume feature of Mac OS X v10.7, applications that were open at logout are relaunched by the system when the user logs in again. You can prevent the automatic relaunching of your application at login by sending it a disableRelaunchOnLogin message. This NSApplication method increments a counter that controls the application being relaunched; if the counter is 0 at the time the user logs out, then the app is relaunched when the user logs back in. The counter is initially zero, providing the default relaunch behavior. Your can reinstate automatic relaunching of your application by sending it an enableRelaunchOnLogin message. This message decrements the relaunch counter, so an equal number of disableRelaunchOnLogin and enableRelaunchOnLogin messages enables relaunching. Both methods are thread safe. If your application should not be relaunched because it launches via some other mechanism, such as the launchd system process, then the recommended practice is to send the application a disableRelaunchOnLogin message once, and never pair it with an enableRelaunchOnLogin message. If your application should not be relaunched because it triggers a restart (for example, if it is an installer), then the recommended practice is to send it a disableRelaunchOnLogin message immediately before you attempt to trigger a restart and send it an enableRelaunchOnLogin message immediately after. This procedure handles the case where the user cancels restarting; if the user later restarts for another reason, then your application should be relaunched.

Making Your Application Accessible Enables Many Users


Millions of people have a disability or special need. These include visual and hearing impairments, physical disabilities, and cognitive and learning challenges. Access to computers is vitally important for this population, because computers can provide a level of independence that is difficult to attain any other way. As populations around the world age, an increasing number of people will experience age-related disabilities, such as vision or hearing loss. Current and future generations of the elderly will expect to be able to continue using their computers and accessing their data, regardless of the state of their vision and hearing. Applications that support customizable text displays, access by a screen reader, or the replacement of visual cues by audible ones can serve this population well.

You Can Prevent the Automatic Relaunch of Your Application


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

71

CHAPTER 5

Supporting Common Application Behaviors

Mac OS X is designed to accommodate assistive technologies and has many built-in features to help people with disabilities. Users access most of this functionality through the Universal Access pane of System Preferences. Some of these built-in technologies take advantage of the same accessibility architecture that allows external assistive technologies to access your application. Designing your application with accessibility in mind not only allows you to reach a larger group of users, it results in a better experience for all your users. As a first step in designing your application, be sure to read Mac OS X Human Interface Guidelines. That book provides detailed specifications and best practices for designing and implementing an intuitive, consistent, and aesthetically pleasing user interface that delivers the superlative experience Macintosh users have come to expect. During the design process, you also should be aware of the accessibility perspective on many basic design considerations. Consider the following basic accessibility-related design principles:

Support full keyboard navigation. For many users, a mouse is difficult, if not impossible, to use. Consequently, a user should be able to perform all your applications functions using the keyboard alone. Dont override built-in keyboard shortcuts. This applies both to the keyboard shortcuts Mac OS X reserves (listed in Keyboard Shortcuts Quick Reference) and to the accessibility-related keyboard shortcuts (listed in Accessibility Keyboard Shortcuts). As a general rule, you should never override reserved keyboard shortcuts. In particular, you should take care not to override accessibility-related keyboard shortcuts or your application will not be accessible to users who enable full keyboard access. A corollary to this principle is to avoid creating too many new keyboard shortcuts that are specific to your application. Users should not have to memorize a new set of keyboard commands for each application they use.

Provide alternatives for drag-and-drop operations. If your application relies on drag-and-drop operations in its workflow, you should provide alternate ways to accomplish the same tasks. This may not be easy; in fact, it may require the design of an alternate interface for applications that are heavily dependent on drag and drop. Make sure theres always a way out of your applications workflow. This is important for all users, of course, but its essential for users of assistive technologies. A user relying on an assistive application to use your application may have a somewhat narrower view of your applications user interface. For this reason, its especially important to make canceling operations and retracing steps easy.

In addition to the basic design principles, you should consider the requirements of specific disabilities and resulting design solutions and adaptations you can implement. The main theme of these suggestions is to provide as many alternate modes of content display as possible, so that users can find the way that suits their needs best. Consider the following categories of disabilities:

Visual Disabilities. Visual disabilities include blindness, color blindness, and low vision. Make your application accessible to assistive applications, such as screen readers. Ensure that color is not the only source of come particular information in your user interface. Provide an audio option for all visual cues and feedback, as well as succinct descriptions of images and animated content. Hearing Disabilities. When you design the audio output of your application, remember that some users may not be able to hear your applications sound effects well or at all. And, of course, there are situations in which any user could not use audio output effectively, such as in a library. Providing redundant audio and visual output can aid comprehension for users with other types of disabilities as well, such as cognitive and learning disabilities.

72

Making Your Application Accessible Enables Many Users


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 5

Supporting Common Application Behaviors

Motor and Cognitive Disabilities. People with motor disabilities may need to use alternatives to the standard mouse and keyboard input devices. Other users may have difficulty with the fine motor control required to double-click a mouse or to press key combinations on the keyboard. Users with cognitive or learning disabilities may need extra time to complete tasks or respond to alerts.

Mac OS X provides support for many types of disabilities at the system level through solutions offered in the Universal Access system preferences, illustrated in Figure 5-1. In addition, most standard Cocoa objects implement accessibility through the NSAccessibility protocol, providing reasonable default behavior in most cases, Cocoa applications built with standard objects are automatically accessible. In general, you need to explicitly implement the NSAccessibility protocol methods only if you subclass one of them, adding new behavior. Figure 5-1 Universal Access system preference dialog

The NSAccessibility informal protocol defines methods that Cocoa classes must implement to make themselves available to an external assistive application. An assistive application interacts with your application to allow persons with disabilities to use your application. For example, a person with a visual impairment could use an application to convert menu items and button labels into speech and then perform actions by verbal command. An accessible object is described by a set of attributes that define characteristics such as the object type, its value, its size and position on the screen, and its place in the accessibility hierarchy. For some objects, the set of attributes can include parameterized attributes. Parameterized attributes behave similar to a function by allowing you to pass a parameter when requesting an attribute value. For more information, see Accessibility Overview.

Making Your Application Accessible Enables Many Users


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

73

CHAPTER 5

Supporting Common Application Behaviors

Provide User Preferences for Customization


Preferences are settings that enable users to customize the appearance or behavior of your software. The Mac OS X user defaults system lets you access and manage user preferences. You can use the defaults system to provide reasonable initial values for application settings, as well as save and retrieve the user's own preference selections across sessions. The Cocoa NSUserDefaults class provides programmatic access to the user defaults system. The NSUserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Booleans, and URLs. A default object must be a property list, that is, an instance of (or for collections a combination of instances of ): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData. The user defaults system groups defaults into domains. Two of the domains are persistently saved in the user defaults database: the application domain stores application-specific defaults, and the global domain stores defaults applicable to all applications. In addition, the user defaults system provides three volatile domains whose values last only while the user defaults object exists: the argument domain for defaults set from command-line arguments, the languages domain containing defaults for a locale if one is specified, and the registration domain for factory defaults registered by the application. Your application uses the NSUserDefaults class to register default preferences, and typically it also provides a user interface (a preferences panel) that enables the user to change those preferences. Preferences are stored in the ~/Library/Preferences/ folder in a standard XML-format property list file as a set of key-value pairs. The application-specific preferences property list is identified by the bundle identifier of the application. For more details, see Preferences and Settings Programming Guide.

Integrate Your App With Spotlight Search


Spotlight is a fast desktop search technology that allows users to organize and search for files based on metadata. Metadata is data about a file, rather than the actual content stored in the file. Metadata can include familiar information such as an assets author and modification date but it can also be keywords or other information that is custom to a particular asset. For example, an image file might have metadata describing the images dimensions and color model. Developers of applications that save documents to disk should consider providing Spotlight support by implementing a metadata importer. A Spotlight metadata importer is a small plug-in bundle that you create to extract information from files created by your application. Spotlight importers are used by the Spotlight server to gather information about new and existing files, as illustrated in Figure 5-2.

74

Provide User Preferences for Customization


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 5

Supporting Common Application Behaviors

Figure 5-2

Spotlight extracting metadata

System Store Spotlight Importers Spotlight Importers MDItem kMDItemTitle kMDItemAuthors kMDItemCopyright . . .

Spotlight

Apple provides importers for many standard file types that the system uses, including RTF, JPEG, Mail, PDF and MP3. However, if you define a custom document format, you must create a metadata importer for your own content. Xcode provides a template for writing Spotlight importer plug-ins. For information about writing a Spotlight importer, see Spotlight Importer Programming Guide. In addition, you may wish to provide metadata search capability in your application. Cocoa provides the NSMetadataQuery class which enables you to construct queries using a subset of the NSPredicate classes and execute the queries asynchronously. For information about providing Spotlight search capability in your application, see File Metadata Search Programming Guide. For more information about Spotlight, see Spotlight Overview.

Use Services to Increase Your Applications Usefulness


Services allow a user to access the functionality of one application from within another application. An application that provides a service advertises the operations it can perform on a particular type of datafor example, encryption of text, optical character recognition of a bitmapped image, or generating text such as a message of the day. When the user is manipulating that particular type of data in some application, the user can choose the appropriate item in the Services menu to operate on the current data selection (or merely insert new data into the document). See Services Implementation Guide for more information.

Make Use of Resolution Independence


Resolution independence decouples the resolution of the user's screen from the units you use in your codes drawing operations. While Mac OS X v10.4 and earlier assumed a screen resolution of 72 dots per inch (dpi), most modern screens actually have resolutions that are 100 dpi or more. The result of this difference is that content rendered for a 72-dpi screen appears smaller on such screens and will appear smaller yet as screen resolutions increase on future displays.

Use Services to Increase Your Applications Usefulness


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

75

CHAPTER 5

Supporting Common Application Behaviors

For applications that depend on a graphical user interface, the dwindling size of user interface elements can make them too small for users to interact with effectively. For example, a checkbox might be too small to click easily. The solution is to make the drawing sizes specified by the application independent of the display's pixel resolution and allow arbitrary scaling between the two. Depending on the type of application, the user interface, and the drawing technologies used, you may need to update your code to provide the best user experience on a resolution-independent system. Cocoa applications require little work to support resolution independence because the Cocoa frameworks handle the scaling for you. Standard Cocoa controls, such as buttons and checkboxes, automatically scale correctly. However, depending on how you manipulate windows and the views within them, you may need to make some changes. In addition, you may need to provide high-resolution versions of any custom artwork. If you draw directly to the screen on a pixel-by-pixel basis, you need to obtain the current scale factor and scale all your images manually. To obtain the scale factor for a particular window, use the userSpaceScaleFactor method in the NSWindow class. In some cases, you may need to convert rectangles or points from the coordinate system of one NSView instance to another (typically the superview or subview), or from one NSView instance to the containing window. The NSView class defines methods for this purpose and, in Mac OS X v10.5 and later, methods that should be used when performing pixel alignment of view content. See Cocoa Drawing Guide and View Programming Guide for more information about coordinate systems. Make sure your application's custom artwork can scale properly as the pixel density of displays increases. That is, the art needs to be larger in terms of pixel dimensions to avoid loss of resolution at high scale factors onscreen. For example, application icons, custom buttons and other controls, and custom images may need high-resolution versions of their artwork. If an existing standard control or icon provides the same function as your custom version, you should consider adopting the standard version. For application icons, you should make sure that the icon family includes images up to 512 x 512 pixels. For new icons, its easiest to design the large icons first and then scale them down. You can use the Icon Composer utility available in the /Developer/Applications/Utilities/ folder to create your icons. Icon Composer v2.0 and later includes support for larger icon sizes. For more information about resolution independence, see Resolution Independence Guidelines.

Prepare for Fast User Switching


Fast user switching lets users share a single machine without having to log out every time they want to access their user account. Users share physical access to the machine, including the same keyboard, mouse, and monitor. However, instead of logging out, a new user simply logs in and switches out the previous user. Processes in a switched-out login session continue running as before. They can continue processing data, communicating with the system, and drawing to the screen buffer as before. However, because they are switched out, they do not receive input from the keyboard and mouse. Similarly, if they were to check, the monitor would appear to be in sleep mode. As a result, it may benefit some applications to adjust their behavior while in a switched-out state to avoid wasting system resources. While fast user switching is a convenient feature for users, it does provide several challenges for application developers. Applications that rely on exclusive access to certain resources may need to modify their behavior to live in a fast user switching environment. For example, an application that stores temporary data in /tmp may run into problems when a second instance running under a different user tries to modify the same files in that directory.

76

Prepare for Fast User Switching


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 5

Supporting Common Application Behaviors

To support fast user switching, there are certain guidelines you should follow in developing your applications, most of which describe safe ways to identify and share system resources. A summary of these guidelines is as follows:

Incorporate session ID information into the name of any entity that appears in a global namespace, including file names, shared memory regions, semaphores, and named pipes. Tagging such application-specific resources with a session ID is necessary to distinguish them from similar resources created by applications in a different session. The Security layer of the system assigns a unique ID to each login session. Incorporating this ID into cache file or temporary directory names can prevent namespace collisions when creating these files. See Supporting Fast User Switching for information on how to get the session ID.

Don't assume you have exclusive access to any resource, especially TCP/IP ports and hardware devices. Don't assume there is only one instance of a per-user service running. Use file-level or range-level locks when accessing files. Accept and handle user switch notifications. See User Switch Notifications for more information.

For more information on user switching, see Multiple User Environments.

Take Advantage of the Dock


The Dock is a desktop application designed to reduce desktop clutter, provide users with feedback about an application, and allow users to switch easily between tasks. You can customize your application Dock tile by modifying the Dock icon and adding items to the menu displayed for your application, and you can customize the Dock icon of a minimized window. An applications Dock icon is, by default, the applications icon. While your application is running, you can modify or replace the default icon with another image that indicates the current state of your application. For example, the icon for Mail changes when messages are waiting to be read. A badgethe red circle and number in the figureis overlaid onto Mails application icon to indicate the number of unread messages. The number changes each time Mail retrieves more messages. When the user holds the Control key down and clicks on a Dock tile, a menu appears. If your application does nothing to customize the menu, the application Dock tiles menu contains a list of the applications open documents (if any), followed by the standard menu items Keep in Dock, Open at Login, Show in Finder, Hide, and Quit. You can add other menu items to the Dock menu, either statically by providing a custom menu nib file, or dynamically by overriding the application delegates applicationDockMenu: method. You can also customize a dock tile when your application is not currently running by creating a Dock tile plug-in that can update the Dock tile icon and menu. For example, you may want to update the badge text to indicate that new content will be available the next time the application is launched, and you may want to customize the applications Dock menu to deal with the new content. For information explaining how to customize your applications Dock icon and menu, see Dock Tile Programming Guide.

Take Advantage of the Dock


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

77

CHAPTER 5

Supporting Common Application Behaviors

78

Take Advantage of the Dock


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 6

Build-Time Configuration Details

Configuring your application properly is an important part of the development process. Mac OS X applications use a structured directory configuration to manage their code and resource files. And although most of the files are custom and are there to support your application, some are required by the system or the App Store and must be configured properly. All of the requirements for preparing your application and distributing it on the App Store are described in the App Store Resource Center.

Configuring Your Xcode Project


To develop a Mac OS X application, you create an Xcode project. An Xcode Project is a repository for all the files, resources, and information required to build your application (or one of a number of other software products). A project contains all the elements used to build your application and maintains the relationships between those elements. It contains one or more targets, which specify how to build the software. A project defines default build settings for all the targets in the project (each target can also specify its own build settings, which override the project build settings). You create a new project using the Xcode File > New > New Project menu command, which invokes the New Project dialog. This dialog enables you to choose a template for your project, such as a Cocoa application, to name it, and to locate it in your file system. The New Project dialog also provides options, so you can specify whether your application uses the Cocoa document architecture or the Core Data framework. When you save your project, Xcode lets you to create a local Git repository to enable source code control for your project. If you have two or more closely related projects, you should create an Xcode Workspace and add your projects to it. A workspace groups projects and other documents so you can work on them together. One project can use the products and shared libraries of another project while building, and Xcode does indexing across the entire workspace, extending the scope of content-aware features such as code completion. Once you have created your project, you write and edit your code using the Xcode source editor. You also use Xcode to build and debug your code, setting breakpoints, viewing the values of variables, stepping through running code, and reviewing issues found during builds or code analysis. When you create a new project, it includes one or more targets, where each target specifies one build product and the instructions for how the product is to be built. Most developers never need to change the default of the vast majority of build settings, but there are a few basic settings that you should check for each target, such as the deployment target (platform, OS version, and architecture), main user interface, and linked frameworks and libraries. You also need to set up one or more schemes to specify the targets, build configuration, and executable configuration to use when the product specified by the target is launched. You use the project editor and the scheme editing dialog to edit build settings and control what happens when you build and run your code. Building and Running Your Code explains how to work with Xcode build settings and schemes.

Configuring Your Xcode Project


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

79

CHAPTER 6

Build-Time Configuration Details

For more information about using Xcode to create, configure, build, and debug your project, as well as archiving your program to package it for distribution or submission to the Mac App Store, see Xcode 4 User Guide.

The Information Property List File


An information property list (Info.plist) file contains essential runtime-configuration information for the application. Xcode provides a version of this file with every Mac OS X application project and configures it with several standard keys. Although the default keys cover several important aspects of the applications configuration, most applications require additional keys to specify their configuration fully. To view the contents of your Info.plist file, select it in the Groups & Files pane. Xcode displays a property list editor similar to the one in Figure 6-1 (which is from Xcode 3.2.5). You can use this window to edit the property values and add new key-value pairs. By default, Xcode displays a more user-friendly version of each key name. To see the key names that Xcode adds to the Info.plist file, Control-click an item in the editor and choose Show Raw Keys/Values from the contextual menu that appears. Figure 6-1 The information property list editor

Xcode automatically adds some important keys to the Info.plist file of all new projects and sets their initial values. However, there are several keys that Mac OS X applications use commonly to configure their launch environment and runtime behavior. Here are some keys that you might want to add to your applications Info.plist file specifically for Mac OS X:

LSApplicationCategoryType (required for applications distributed using the App Store)

80

The Information Property List File


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 6

Build-Time Configuration Details

CFBundleDisplayName LSHasLocalizedDisplayName NSHumanReadableCopyright LSMinimumSystemVersion UTExportedTypeDeclarations UTImportedTypeDeclarations

For detailed information about these and other keys that you can include in your applications Info.plist file, see Information Property List Key Reference.

The Mac OS X Application Bundle


When you build your Mac OS X application, Xcode packages it as a bundle. A bundle is a directory in the file system that groups related resources together in one place. A Mac OS X application bundle contains a single Contents directory, inside of which are additional files and directories with the applications code, resources, and localized content. Table 6-1 lists the contents of a typical Mac OS X application bundle, which for demonstration purposes is called MyApp. This example is for illustrative purposes only. Some of the files listed in this table may not appear in your own application bundles. Table 6-1 Files
Contents/MacOS/MyApp

A typical application bundle Description The executable file containing your applications code. The name of this file is the same as your application name minus the .app extension. This file is required.

Contents/Info.plist

Also known as the information property list file, a file containing configuration data for the application. The system uses this data to determine how to interact with the application at specific times. This file is required. For more information, see The Information Property List File (page 80).

The Mac OS X Application Bundle


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

81

CHAPTER 6

Build-Time Configuration Details

Files

Description

Contents/Resources/English.lproj/MainMenu.nib The English version of the applications main

nib file. This file contains the default interface objects to load at application launch time. Typically, this nib file contains the applications menu bar and application delegate object. It may also contain other controller objects that should always be available at launch time. (The name of the main nib file can be changed by assigning a different value to the NSMainNibFile key in the Info.plist file.) This file is optional but recommended. For more information, see The Information Property List File (page 80)
Contents/Resources/sun.png (or other resource files)

Nonlocalized resources are placed at the top level of the Resources directory (sun.png represents a nonlocalized image file in the example). The application uses nonlocalized resources when no localized version of the same resource is provided. Thus, you can use these files in situations where the resource is the same for all localizations. Localized resources are placed in subdirectories with an ISO 639-1 language abbreviation for a name plus an .lproj suffix. Although more human readable names (such as English.lproj, French.lproj, and German.lproj) can be used for directory names, the ISO 639-1 names are preferred because they allow you to include an ISO 3166-1 regional designation. (For example, the en_GB.lproj directory contains resources localized for English as spoken in Great Britain, the es.lproj directory contains resources localized for Spanish, and the de.lproj directory contains resources localized for German.)

Contents/Resources/en_GB.lproj Contents/Resources/es.lproj Contents/Resources/de.lproj

Other language-specific project directories

A Mac OS X application should be internationalized and have a language.lproj directory for each language it supports. Even if you provide localized versions of your resources, though, include a default version of these files at the top level of your Resources directory. The default version is used when a specific localization is not available. At runtime, you can access your applications resource files from your code using the following steps: 1. 2. Obtain a reference to your applications main bundle object (typically using theNSBundle class). Use the methods of the bundle object to obtain a file-system path to the desired resource file.

82

The Mac OS X Application Bundle


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 6

Build-Time Configuration Details

3.

Open (or access) the file and use it.

You obtain a reference to your applications main bundle using the mainBundle class method of NSBundle. The pathForResource:ofType: method is one of several NSBundle methods that you can use to retrieve the location of resources. The following example shows how to locate a file called sun.png and create an image object using it. The first line gets the bundle object and the path to the file. The second line creates an NSImage object that you could use to display the image in your application.
NSString* imagePath = [[NSBundle mainBundle] pathForResource:@"sun" ofType:@"png"]; NSImage* sunImage = [[NSImage alloc] initWithContentsOfFile:imagePath];

Note: If you prefer to use Core Foundation to access bundles, you can obtain a CFBundleRef opaque type using the CFBundleGetMainBundle function. You can then use that opaque type plus the Core Foundation functions to locate any bundle resources. For information on how to access and use resources in your application, see Resource Programming Guide. For more information about the structure of a Mac OS X application bundle, see Bundle Programming Guide.

Internationalizing Your Application


The process of preparing a project to handle content in different languages is called internationalization. The process of converting text, images, and other content into other languages is called localization. Project resources that are candidates for localization include:

Code-generated text, including locale-specific aspects of date, time, and number formatting Static textfor example, strings you specify programmatically and display in parts of your user interface or an HTML file containing application help Icons (including your application icon) and other images when those images either contain text or have some culture-specific meaning Sound files containing spoken language Nib files

Users select their preferred language from the Language and Text system preference, shown in Figure 6-2.

Internationalizing Your Application


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

83

CHAPTER 6

Build-Time Configuration Details

Figure 6-2

The Language preference view

Your application bundle can include multiple language-specific resource directories. The names of these directories consist of three components: an ISO 639-1 language code, an optional ISO 3166-1 region code, and a .lproj suffix. For example, to designate resources localized to English, the bundle would be named en.lproj. By convention, these directories are called lproj directories. Note: You may use ISO 639-2 language codes instead of those defined by ISO 639-1. For more information about language and region codes, see Language and Locale Designations in Internationalization Programming Topics. Each lproj directory contains a localized copy of the applications resource files. When you request the path to a resource file using the NSBundle class or the CFBundleRef opaque type, the path you get back automatically reflects the resource for the users preferred language. For more information about internationalization and how you support localized content in your Mac OS X applications, see Internationalization Programming Topics.

84

Internationalizing Your Application


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 7

Tuning for Performance and Responsiveness

As you develop your application and your project code stabilizes, you can begin performance tuning. Of course, you want your application to launch and respond to the users commands as quickly as possible. A responsive application fits easily into the users workflow and feels well crafted.

Speed Up Your Applications Launch Time


You can improve your applications performance at launch time by minimizing or deferring work until after the launch sequence has completed. The launch of an application provides users with the first impression of your application, and its something they see on a regular basis. Your overriding goal during launch should be to display the applications menu bar and main window and then start responding to user commands as quickly as possible. Making your application responsive to commands quickly provides a better experience for the user. The following sections provide some general tips on how to make your application launch faster.

Delay Initialization Code


Many applications spend a lot of time initializing code that isnt used until much later. Delaying the initialization of subsystems that are not immediately needed can speed up your launch time considerably. Remember that the goal is to display your application interface quickly, so try to initialize only the subsystems related to that goal initially. Once you have posted your interface, your application can continue to initialize additional subsystems as needed. However, remember that just because your application is able to process commands does not mean you need all of that code right away. The preferred way of initializing subsystems is on an as-needed basis. Wait until the user executes a command that requires a particular subsystem and then initialize it. That way, if the user never executes the command, you will not have wasted any time running the code to prepare for it. Avoid putting a lot of extraneous initialization code in your awakeFromNib methods. The system calls the awakeFromNib method of your main nib file before your application enters its main event loop. Use that method to initialize the objects in that nib and to prepare your application interface. For all other initialization, use the applicationDidFinishLaunching: method of your NSApplicationDelegate object instead. For more information on nib files and how they are loaded, see Resource Programming Guide.

Speed Up Your Applications Launch Time


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

85

CHAPTER 7

Tuning for Performance and Responsiveness

Simplify Your Main Nib File


Loading a nib file is an expensive process that can slow down your application launch time if you are not careful. When a nib file is loaded, all of the objects in that file are instantiated and made ready for use. The more objects you include in your applications main nib, the more time it takes to load that file and launch your application. The instantiation process for objects in a nib file requires that any frameworks used by those objects must themselves reside in memory. Thus loading a nib for a Cocoa application would likely require the loading of both the AppKit and Foundation frameworks, if they were not already resident in memory. Similarly, if you declare a custom class in your main nib file and that class relies on other frameworks, the system must load those frameworks as well. When designing your applications main nib file, you should include only those objects needed to display your applications initial user interface. Usually, this would involve just your applications menu bar and initial window. For any custom classes you include in the nib, make sure their initialization code is as minimal as possible. Defer any time-consuming operations or memory allocations until after the class is instantiated.

Minimize Global Variables


For both applications and frameworks, be careful not to declare global variables that require significant amounts of initialization. The system initializes global variables before calling your applications main routine. If you use a global variable to declare an object, the system must call the constructor or initialization method for that object during launch time. In general, its best to avoid declaring objects as global variables altogether when you can use a pointer instead. If you are implementing a framework or any type of reusable code module, you should also minimize the number of global variables you declare. Each application that links to a framework acquires a copy of that frameworks global variables. These variables might require several pages of virtual memory, which then increases the memory footprint of the application. An increased memory footprint can lead to paging in the application, which has a tremendous impact on performance. One way to minimize the global variables in a framework is to store the variables in a malloc-allocated block of memory instead. In this technique, you access the variables through a pointer to the memory, which you store as a global variable. Another advantage of this technique is that it allows you to defer the creation of any global variables until the first time they are actually used. See Tips for Allocating Memory in Memory Usage Performance Guidelines for more information.

Minimize File Access at Launch Time


Accessing a file is one of the slowest operations performed on a computer, so it is important that you do it as little as possible, especially at launch time. There is always some file access that must occur at launch time, such as loading your executable code and reading in your main nib file, but reducing your initial dependence on startup files can provide significant speed improvements. If you can delay the reading of a file until after launch time, do so. The following list includes some files whose contents you may not need until after launch:

Frameworks not used directly by your applicationAvoid calling code that uses nonessential frameworks until after launch.

86

Speed Up Your Applications Launch Time


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 7

Tuning for Performance and Responsiveness

Nib files whose contents are not displayed immediatelyMake sure your nib files and awakeFromNib: code are not doing too much at launch time. See Simplify Your Main Nib File (page 86) for more information. User preference filesUser preferences may not be local so read them later if you can. Font filesConsider delaying font initialization until after the application has launched. Network filesAvoid reading files located on the network if at all possible.

If you must read a file at launch time, do so only once. If you need multiple pieces of data from the same file, such as from a preferences file, consider reading all of the data once rather than accessing the file multiple times.

Dont Block the Main Thread


The main thread is where your application handles user events and other input, so you should keep it free as much as possible to be responsive to the user. In particular, never use the main thread to perform long-running or potentially unbounded tasks, such as tasks that require network access. Instead, always move those tasks onto background threads. The preferred way to do so is to use Grand Central Dispatch (GCD) or operation objects to perform tasks asynchronously. For more information about doing work on background threads, see Concurrency Programming Guide.

Decrease Your Applications Code Size


In the context of performance, the more memory your application occupies, the more inefficient it is. More memory means more memory allocations, more code, and a greater potential for paging. Reducing your code footprint is not just a matter of turning on code optimizations in your compiler, although that does help. You can also reduce your code footprint by organizing your code so that only the minimum set of required functions is in memory at any given time. You implement this optimization by profiling your code. See Memory Instruments in Instruments User Guide for information about profiling your applications memory allocations.

Compiler-Level Optimizations
The Xcode compiler supports optimization options that let you choose whether you prefer a smaller binary size, faster code, or faster build times. For new projects, Xcode automatically disables optimizations for the debug build configuration and selects the Fastest, Smallest option for the release build configuration. Code optimizations of any kind result in slower build times because of the extra work involved in the optimization process. If your code is changing, as it does during the development cycle, you do not want optimizations enabled. As you near the end of your development cycle, though, the release build configuration can give you an indication of the size of your finished product, so the Fastest, Smallest option is appropriate.

Dont Block the Main Thread


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

87

CHAPTER 7

Tuning for Performance and Responsiveness

Table 7-1 lists the optimization levels available in Xcode. When you select one of these options, Xcode passes the appropriate flags to the compiler for the given group or files. These options are available at the target level or as part of a build configuration. See the Xcode Build System Guide for information on working with build settings for your project. Table 7-1 Compiler optimization options Description The compiler does not attempt to optimize code. Use this option during development when you are focused on solving logic errors and need a fast compile time. Do not use this option for shipping your executable. The compiler performs simple optimizations to boost code performance while minimizing the impact to compile time. This option also uses more memory during compilation. The compiler performs nearly all supported optimizations that do not require a space-time tradeoff. The compiler does not perform loop unrolling or function inlining with this option. This option increases both compilation time and the performance of generated code. The compiler performs all optimizations in an attempt to improve the speed of the generated code. This option can increase the size of generated code as the compiler performs aggressive inlining of functions. This option is generally not recommended. Fastest, Smallest The compiler performs all optimizations that do not typically increase code size. This is the preferred option for shipping code because it gives your executable a smaller memory footprint. As with any performance enhancement, do not make assumptions about which option will give you the best results. You should always measure the results of each optimization you try. For example, the Fastest option might generate extremely fast code for a particular module, but it usually does so at the expense of executable size. Any speed advantages you gain from the code generation are easily lost if the code needs to be paged in from disk at runtime.

Xcode setting None

Fast

Faster

Fastest

Use Core Data for Large Data Sets


If your application manipulates large amounts of structured data, store it in a Core Data persistent store or in a SQLite database instead of in a flat file. Both Core Data and SQLite provide efficient ways to manage large data sets without requiring the entire set to be in memory all at once. Use SQLite if you deal with low-level data structures, or an existing SQLite database. Core Data provides a high-level abstraction for efficient object-graph management with an Objective-C interface; it is, however, an advanced framework and you shouldn't use it until you have gained adequate experience. For more information about Core Data, see Core Data Programming Guide and Optimizing Core Data with Instruments.

88

Decrease Your Applications Code Size


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

CHAPTER 7

Tuning for Performance and Responsiveness

Eliminate Memory Leaks


Your application should not have any memory leaks. You can use the Instruments application to track down leaks in your code, both in the simulator and on actual devices. See Memory Instruments in Instruments User Guide for information about finding memory leaks.

Dead Strip Your Code


For statically linked executables, dead-code stripping is the process of removing unreferenced code from the executable file. If the code is unreferenced, it must not be used and therefore is not needed in the executable file. Removing dead code reduces the size of your executable and can help reduce paging. To enable dead-code stripping in Xcode, in the Linking group of Build Settings, set the Dead Code Stripping option to Yes.

Strip Symbol Information


Debugging symbols and dynamic-binding information can take up a lot of space and comprise a large percentage of your executables size. Before shipping your code, you should strip out all unneeded symbols. To strip debugging symbols from your executable, change the Xcode compiler code generation Generate Debug Symbols option to No. You can also generate debugging symbols on a target-by-target basis if you prefer. See the Xcode Help for more information on build configurations and target settings.

Build Fixed-Position Application Code


Much code is built with an option that enables indirect symbol addressing and position-independent code generation, so that the generated code can be relocated within the virtual memory space of the process. For projects such as bundles and frameworks, this option is required. The dynamic-linker must be able to relocate the bundle or framework and patch up symbol references at runtime. Unlike bundles and frameworks, applications do not need position-independent code generation. Application code is never relocated within the process space. You should always set the Generate Position-Dependent Code option to Yes when building the Release version of your executable. See Code Size Performance Guidelines for more detailed information about reducing the size of your executable.

Decrease Your Applications Code Size


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

89

CHAPTER 7

Tuning for Performance and Responsiveness

90

Decrease Your Applications Code Size


2011-06-27 | 2011 Apple Inc. All Rights Reserved.

REVISION HISTORY

Document Revision History

This table describes the changes to Mac OS X App Programming Guide. Date 2011-06-27 Notes New document describing the development process for Mac OS X applications.

91
2011-06-27 | 2011 Apple Inc. All Rights Reserved.

REVISION HISTORY

Document Revision History

92
2011-06-27 | 2011 Apple Inc. All Rights Reserved.

Potrebbero piacerti anche