The Core App Design
核心应用设计
To unleash the power of OS X, you develop apps using the Cocoa application environment. Cocoa presents the app’s user interface and integrates it tightly with the other components of the operating system. Cocoa 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 extensible—you can use them as is or extend them for your particular requirements.
使用Cocoa应用程序环境来开发应用,以释放OS X的强大性能。Cocoa呈现应用的用户界面,并将其与操作系统的其它组件整合为一体。Cocoa通过AppKit和Foundation框架这两个核心类库来提供一套面向对象的软件组件整合套装,以及一些提供支持技术的底层框架。Cocoa类可复用、可扩展——你可以直接使用它们,也可以根据特殊需要来扩展它们。
Cocoa makes it easy to create apps that adopt all of the conventions and expose all of the power of OS X. In fact, you can create a new Cocoa application project in Xcode and, without adding any code, have a functional app. Such an app 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 behavior is provided by Cocoa itself.
Cocoa使创建既能适应惯例、又能展现OS X能力的应用变得简单。事实上,如果在Xcode中创建一个新的Cocoa应用程序工程,几乎不用添加任何代码,就拥有了一个可用的应用。这个应用可以显示它的窗口(或创建新的文档),还能实现许多基本系统功能。为实现这些功能,尽管Xcode模板提供了一些代码,但代码的数量极少。大部分的功能是由Cocoa本身提供的。
To make a great app, you should build on the foundations Cocoa lays down for you, working with the conventions and infrastructure provided for you. To do so effectively, it's important to understand how a Cocoa app fits together.
创建精彩的应用,需要建立在Cocoa铺设的基础之上,与为你提供的惯例和基础设施协同工作。为更有效地做到这一点,理解Cocoa应用如何适配很重要。
Fundamental Design Patterns
基础设计模式
Cocoa incorporates many design patterns in its implementation. Table 2-1 lists the key design patterns with which you should be familiar.
Cocoa在其实现中包含多种设计模式。表2-1列出了你应当熟悉的重要设计模式。
Table 2-1 Fundamental design patterns used by Mac apps
表2-1 Mac应用使用的基础设计模式
Design pattern | Why it is important |
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 app. 使用模型-视图-控制器(MVC)设计模式确保现在创建的对象在应用的将来版本中很容易地复用和更新。 Cocoa provides most of the classes used to build your app’s 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. Cocoa提供了构建应用控制器和视图层的大多数类。你的工作是定制需要的类、提供与之协同的必要的数据模型。 MVC is central to a good design for a Cocoa application because many Cocoa technologies and architectures are based on MVC and require that your custom objects assume one of the MVC roles. MVC对于一个好的Cocoa应用设计来说,是居于中心位置的;这是由于Cocoa技术和架构是基于MVC的,同时也要求自定义的对象也要扮演MVC中的一个角色。 |
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. 代理设计模式允许你不必继承一个对象,就可以在运行时改变其行为。代理对象遵循特定的协议,这个协议在代理和它所修改的对象之间定义了交互点。在这些特定点上,控制对象调用其代理的方法为代理提供信息,或询问该做什么。然后代理可以采取任何合理的操作。 |
Responder chain 响应者链 | The responder chain defines the relationships between event-handling objects in your app. As events arrive, the app 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 app’s controller objects, may also be responders. 窗口和视图是最常见的响应者类型,同时也是鼠标事件的第一响应者。其它类型的对象,比如应用的控制器对象,也可以是响应者。 |
Target-action 目标-动作 | Controls use the target-action design pattern to notify your app 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. 控件使用目标-动作设计模式来向应用通知用户交互。当用户通过预先定义的方式(比如按下按钮)与控件进行交互时,控件会向你指定的对象(目标)发送消息(动作)。当收到动作消息时,目标采取适当的方式进行响应。 |
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. 块对象是一种把代码和本地堆变量封装起来以便稍后执行的便捷方式。块被许多框架用于回调功能的替代,同时也用于与GCD连接,异步执行任务。 For more information about using blocks, see Blocks Programming Topics. 更多关于块使用的信息,参考Blocks Programming Topics。 |
Notifications 通知 | Notifications are used throughout Cocoa to deliver news of changes to your app. Many objects send notifications at key moments in the object’s life cycle. Intercepting these notifications gives you a chance to respond and add custom behavior. 通知贯穿于Cocoa,来向应用传递变化的消息。许多对象在生命周期的重要时刻发送通知。拦截这些通知,可获得响应的机会,来添加自定义行为。 |
Key-value observing (KVO) 键值观察(KVO) | 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. KVO跟踪一个对象的某一特定属性的变化。当这个属性变化时,会自动通知至所有已注册为对这个属性感兴趣的对象。这些观察者便拥有了一次响应变化的机会。 |
Bindings 绑定 | Cocoa bindings provide a convenient bridge between the model, view, and controller portions of your app. 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. Cocoa绑定在应用的模型、视图、控制器之间提供了方便的连接。可以通过控制器,将一个视图绑定到底层的数据对象(可以是静态的,也可以是动态的)。视图的变化会自动反映到数据模型上,反之亦然。 The use of bindings is not required for apps but does minimize the amount of code you have to write. You can set up bindings programmatically or using Interface Builder. 绑定的使用不是必须的,但它的确可以最大程度地降低代码量。可通过编程或使用界面生成器来设置绑定。 |
The App Style Determines the Core Architecture
应用的风格决定其核心架构
The style of your app defines which core objects you must use in its implementation. Cocoa supports the creation of both single-window and multiwindow apps. For multiwindow designs, it also provides a document architecture to help manage the files associated with each app window. Thus, apps can have the following forms:
应用的风格决定了在其实现中必须使用的核心对象。Cocoa同时支持单窗口和多窗口应用。对于多窗口应用,它还提供了文档架构,来管理连接到每个窗口的文档。因此,应用可拥有如下形式:
- Single-window utility app
- 单一窗口实用程序应用
- Single-window library-style app
- 单一窗口库类型应用
- Multiwindow document-based app
- 多窗口基于文档应用
You should choose a basic app 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 apps from iOS. The single-window style typically yields a more streamlined user experience, and it also makes it easier for your app to support a full-screen mode. However, if your app works extensively with complex documents, the multiwindow style may be preferable because it provides more document-related infrastructure to help you implement your app.
在设计进程的前期,就需要选定基本的应用风格,因为这个选择将影响后续的所有事情。许多情况下推荐单一窗口应用,尤其是开发者从iOS移植过来的应用。单一窗口应用会提供更便捷的用户体验,同时也使应用更易支持全屏模式。如果你的应用需要大量处理复杂文档,则更推荐多窗口应用,因为它提供了更多文件相关的基础设施,以便实现应用。
The Calculator app provided with OS X, shown in Figure 2-1, is an example of a single-window utility app. Utility apps 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 app, the data it processed is simply discarded.
OS X提供的计算机应用,如图2-1所示,是一个单一窗口实用程序应用的例子。实用程序通常处理短暂的数据或管理系统进程。计算器不创建或处理文档,也不存储用户数据,它只是在其窗口中简单地处理用户输入到文本框中的数据,并展示计算结果。当用户退出这个应用时,数据会被简单地丢弃。
Figure 2-1 The Calculator single-window utility app
图2-1 计算器,单一窗口实用程序
Single-window, library-style (or “shoebox”) apps do handle persistent user data. One of the most prominent examples of a library-style app is iPhoto, shown in Figure 2-2. The user data handled by iPhoto are photos (and associated metadata), which the app edits, displays, and stores. All user interaction with iPhoto happens in a single window. Although iPhoto stores its data in files, it doesn’t present the files to the user. The app presents a simplified interface so that users don’t need to manage files in order to use the app. Instead, they work directly with their photos. Moreover, iPhoto hides its files from regular manipulation in the Finder by placing them within a single package. In addition, the app saves the user’s 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 library-style app design.
单一窗口、库类型(或“鞋盒”)应用处理可持久用户数据。库类型应用的一个最显著的例子是iPhoto,如图2-2所示。iPhoto处理的数据是照片(以及关联的元数据),应用可以编辑、显示和存储它们。用户与iPhoto的所有交互都在单一窗口中进行。尽管iPhoto将数据存储到文件中,但它并不向用户展示文件。应用展示了一个简易的界面,用户无需管理文件就可以使用应用。相反,他们直接与照片打交道。iPhoto将其文件从Finder常规操作中隐藏,将它们放置到一个单独的包中。另外,应用在恰当的时间保存用户的编辑。这样,用户从手动保存、打开或关闭文档中解脱出来。简化用户操作是库类型应用设计的一个关键优势。
Figure 2-2 The iPhoto single-window app
图2-2 iPhoto,单一窗口应用
A good example of a multiwindow document-based app is TextEdit, which creates, displays, and edits documents containing plain or styled text and images. TextEdit does not organize or manage its documents—users 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 window’s toolbar and the app’s menu bar. Figure 2-3 shows a document created by TextEdit. For more information about the document-based app design, see Document-Based Apps Are Based on an NSDocument Subclass.
文本编辑是基于文档的多窗口应用的一个很好的例子,它创建、显示、编辑包含简易或格式化文本和图片的文档。文本编辑不组织或管理文档——用户使用Finder来组织和管理它们。每个文本编辑的文档在其自己的窗口中打开,许多文档可同时打开,用户使用窗口的工具栏、应用的菜单栏与最上方的文档进行交互。图2-3展示了一个由文本编辑创建的文档。关于基于文档的应用设计的更多信息,参考Document-Based Apps Are Based on an NSDocument Subclass。
Figure 2-3 TextEdit document window
图2-3 文本编辑文档窗口
Both single-window and multiwindow apps can present an effective full-screen mode, which provides an immersive experience that enables users to focus on their tasks without distractions. For information about full-screen mode, see Implementing the Full-Screen Experience.
单一窗口和多窗口应用都可以呈现有效的全屏模式,全屏模式可以提供沉浸式体验,确保用户专注于它们的任务而不被打扰。关于全屏模式的更多信息,参考Implementing the Full-Screen Experience。
The Core Objects for All Cocoa Apps
通用核心对象
Regardless of whether you are using a single-window or multiwindow app style, all apps use the same core set of objects. Cocoa provides the default behavior for most of these objects. You are expected to provide a certain amount of customization of these objects to implement your app’s custom behavior.
不管使用单一窗口风格,还是使用多窗口风格,所有应用都使用同一套核心对象。Cocoa为大多数对象提供了默认行为。你应当对这些对象进行适当的定制,来实现应用的自定义行为。
Figure 2-4 shows the relationships among the core objects for the single-window app styles. The objects in this figure are separated according to whether they are part of the model, view, or controller portions of the app. As you can see from the figure, the Cocoa–provided objects provide much of the controller and view layer for your app.
图2-4展示了单一窗口应用核心对象之间的关系。图中的对象按照它是否为应用模型、视图或控制器的组成部分来划分。从图中可以看出,Cocoa提供的对象为应用提供了大部分的控制器和视图层。
Figure 2-4 Key objects in a single-window app
图2-4 单一窗口应用中的关键对象
Table 2-2 describes the roles played by the objects in the diagram.
表2-2描述了这些对象所扮演的角色
Table 2-2 The core objects used by all Cocoa apps
表2-2 通用核心对象
Object | Description |
NSApplication object | (Required) Runs the event loop and manage interactions between your app and the system. You typically use the NSApplication class as is, putting any custom app-object-related code in your application delegate object. (必须)运行事件循环,管理应用与系统的交互。通常使用NSApplication类,把应用对象相关的自定义代码放置到应用代理对象中。 |
Application delegate object 应用代理对象 | (Expected) A custom object that you provide which works closely with the NSApplication object to run the app and manage the transitions between different application states. (期望)你提供的一个自定义对象,与NSApplication对象协同工作来运行应用、管理应用不同状态之间的转换。 Your application delegate object must conform to the NSApplicationDelegate Protocol. 应用代理对象必须遵守 NSApplicationDelegate Protocol。 |
Data model objects 数据模型对象 | Store content specific to your app. A banking app might store a database containing financial transactions, whereas a painting app might store an image object or the sequence of drawing commands that led to the creation of that image. 存储应用内容。银行应用可能会存储一个包含财务交易的数据库,绘图应用可能存储一个图片对象,或是能生成图片的绘图命令序列。 |
Window controllers 窗口控制器 | Responsible for loading and managing a single window each and coordinating with the system to handle standard window behaviors. 负责加载和管理单个窗口,并与系统协调,处理基本窗口行为。 You subclass NSWindowController to manage both the window and its contents. Each window controller is responsible for everything that happens in its window. If the contents of your window are simple, the window controller 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. 需要继承NSWindowController类来管理窗口和其内容。窗口控制器负责其窗口中发生的一切事务。如果窗口足够简单,窗口控制器可能能够自己完成所有的管理工作。如果窗口比较复杂,窗口控制器可能要使用一个或多个视图控制器来管理窗口的各个部分。 |
Window objects 窗口对象 | Represent your onscreen windows, configured in different styles depending on your app’s needs. For example, most windows have title bars and borders but you can also configure windows without those visual adornments. A window object is almost always managed by a window controller. 代表屏幕上的窗口,根据应用需要进行不同方式的配置。比如,大多数的窗口拥有标题栏和边框,但也可配置为不要这些可视化的装饰。窗口对象几乎总是由窗口控制器来管理。 An app 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 apps, to the main window. They support the document or main window, for 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. 应用也可拥有二级窗口,通常被称为对话框和面板。这些窗口从属于当前文档窗口;如果是单一窗口应用,则从属于主窗口。它们支持文档或主窗口,允许选择字体和颜色、从调色板中选择工具或显示一个警告。二级窗口通常是模态的。 |
View controllers 视图控制器 | Coordinate the loading of a single view hierarchy into your app. Use view controllers to divide up the work for managing more sophisticated window layouts. Your view controllers work together (with the window controller) to present the window contents. 负责协调向应用中加载单一视图层级。使用视图控制器将复杂的窗口布局管理工作进行分割。视图控制器(与窗口控制器)协同工作,来呈现窗口内容。 If you have developed iOS apps, be aware that AppKit view controllers play a less prominent role than UIKit view controllers. In 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. 如果你曾经开发过iOS应用,会发现AppKit视图控制器没有UIKit视图控制器那么重要。在OS X中,AppKit视图控制器是窗口控制器的辅助,它从根本上负责窗口中的所有事务。AppKit视图控制器的主要任务是加载其视图层次。剩余的事情就是你所写的自定义代码。 |
View objects 视图对象 | Define a rectangular region in a window, draw the contents of that region, and handle events in that region. Views can be layered on top of each other to create view hierarchies, whereby one view obscures a portion of the underlying view. 在窗口中定义一个矩形区域,并在该区域中绘制内容、处理事件。视图可被放置在其它视图之上来创建视图层级,借此一个视图会遮盖其下层视图的一部分。 |
Control objects 控件对象 | Represent standard system controls. These view subclasses provide standard visual items such as buttons, text fields, and tables that you can use to build your user interface. Although a few controls are used as is to present visual adornments, most work with your code is to manage user interactions with your app’s content. 代表基础系统控件。这些视图子类提供了标准的可视化项目,如按钮、文本框和表格,可使用它们来构建用户界面。尽管少数控件用于呈现可视化装饰,但大多数控件的工作是与代码协同工作,管理用户与应用内容的交互。 |
Additional Core Objects for Multiwindow Apps
多窗口应用附加的核心对象
As opposed to a single-window app, a multiwindow app uses several windows to present its primary content. The Cocoa support for multiwindow apps 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.
与单一窗口应用不同,多窗口应用使用几个窗口来展示其主要内容。Cocoa对多窗口应用的支持是围绕基于文档的模型来构建的,这个模型由称作“文档架构”的子系统来实现。在这个模型中,每个文档对象管理其内容、协调磁盘的读写,并将内容展示在窗口中以供编辑。所有的文档对象与Cocoa基础设施一同工作来协调事件的传递,但每个文档对象与其它文档对象都是独立的。
Figure 2-5 shows the relationships among the core objects of a multiwindow document-based app. Many of the same objects in this figure are identical to those used by a single-window app. The main difference is the insertion of the NSDocumentController and NSDocument objects between the application objects and the objects for managing the user interface.
图2-5展示了基于文档的多窗口应用的核心对象之间的关系。表格中的许多对象与单一窗口中使用的对象完全相同。主要的区别是在应用程序对象之间插入了NSDocumentController和NSDocument对象,以及管理用户界面的对象。
Figure 2-5 Key objects in a multiwindow document app
表2-5 多窗口文档应用关键对象
Table 2-3 describes the role of the inserted NSDocumentController and NSDocument objects. (For information about the roles of the other objects in the diagram, see Table 2-2.)
表2-3描述了插入的NSDocumentController和NSDocument对象的作用。(关于其它对象的作用信息,见表2-2)
Table 2-3 Additional objects used by multiwindow document apps
表2-3 多窗口文档应用使用的附加对象
Object | Description |
Document Controller object | The NSDocumentController class defines a high-level controller for creating and 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. NSDocumentController类定义了一个高层的控制器,用于创建和管理所有的文档对象。除管理文档外,文档控制器还管理许多文档相关的菜单项目,比如“最近打开”菜单、打开和保存面板。 |
Document object | The NSDocument class is the base class for implementing documents in a multiwindow app. 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 app’s data objects and to work with one or more NSWindowController objects to display the document contents on the screen. NSDocument类是多窗口应用实现文档的基础类。这个类扮演着与文档相关的数据对象的控制器。可自定义其子类,来管理与应用数据对象的交互,并与一个或多个NSWindowController对象协同工作,在屏幕上显示文档内容。 |
Integrating iCloud Support Into Your App
向应用中集成iCloud
No matter how you store your app’s data, iCloud is a convenient way to make that data available to all of a user’s devices. To integrate iCloud into your app, you change where you store user files. Instead of storing them in the user’s Home folder or in your App Sandbox container, you store them in special file system locations known as ubiquity containers. A ubiquity container serves as the local representation of corresponding iCloud storage. It is outside of your App Sandbox container, and so requires specific entitlements for your app to interact with it.
不管如何存储应用数据,iCloud是使数据向用户的所有设备可见的一个便捷方法。将iCloud集成到应用中,要改变存储用户文件的位置。将以往把它们存储到应用沙箱容器中的用户主文件夹中,替换为存储到的特殊文件系统位置——这个位置被称为无处不在的容器。无处不在的容器充当相应iCloud存储的本地表示。它在应用沙箱之外,所以需要特殊的应用授权来与其交互。
In addition to a change in file system locations, your app design needs to acknowledge that your data model is accessible to multiple processes. The following considerations apply:
除了文件系统位置之外的变化,应用设计需要确保数据模型可供多进程来存取。考虑以下情况:
- Document-based apps get iCloud support through the NSDocument class, which handles most of the interactions required to manage the on-disk file packages that represent documents.
- 基于文件的应用通过NSDocument类来获取iCloud支持。NSDocument类处理大多数交互,这些交互是管理表示文档的磁盘包文件所必须的。
- If you implement a custom data model and manage files yourself, you must explicitly use file coordination to ensure that the changes you make are done safely and in concert with the changes made on the user’s other devices. For details, see The Role of File Coordinators and Presenters in File System Programming Guide.
- 如果实现了自定义数据模型并自己管理文件,则必须明确使用文件协调,来确保你产生的变化是安全进行地,并与其它设备上产生的变化保持一致。更多细节,参考File System Programming Guide中的The Role of File Coordinators and Presenters。
- For storing small amounts of data in iCloud, you use key-value storage. Use key-value storage for such things as stocks or weather information, locations, bookmarks, a recent-documents list, settings and preferences, and simple game state. Every iCloud app should take advantage of key-value storage. To interact with key-value storage, you use the shared NSUbiquitousKeyValueStore object.
- 在iCloud中存储少量数据,可使用键-值存储。键-值存储用于股票或天气信息、位置、书签、最近文档列表、设置和偏好、简单游戏状态等。每个iCloud应用都应利用键-值存储。与键-值存储交互,使用共享的NSUbiquitousKeyValueStore对象。
To learn how to adopt iCloud in your app, read iCloud Design Guide.
学习如何在应用中适配iCloud,阅读iCloud Design Guide.
Shoebox-Style Apps Should Not Use NSDocument
“鞋盒”风格的应用不应当使用NSDocument
When implementing a single-window, shoebox-style (sometimes referred to as a “library” style) app, it is sometimes better not to use NSDocument objects to manage your content. The NSDocument class was designed specifically for use in multiwindow document apps. Instead, use custom controller objects to manage your data. Those custom controllers would then work with a view controller or your app’s main window controller to coordinate the presentation of the data.
实现单一窗口、“鞋盒”风格(有时指“库”类型)的应用时,有些时候不使用NSDocument对象用来管理内容为好。NSDocument类是专门针对多窗口文档应用来设计的。作为替代,使用自定义的控制器对象来管理数据。这些自定义控制器与视图控制器或应用主窗口控制器来协同展示数据。
Although you normally use an NSDocumentController object only in multiwindow apps, you can subclass it and use it in a single-window app to coordinate the Open Recent and similar behaviors. When subclassing, though, you must override any methods related to the creation of NSDocument objects.
尽管通常只在多窗口应用中使用NSDocumentController对象,但你可以继承它并用在单一窗口应用中,来协调“最近打开”及类似行为。不过,在继承这个类时,应当重写与创建NSDocument对象相关的所有方法。
Document-Based Apps Are Based on an NSDocument Subclass
基于文档的应用以NSDocument的子类为基础
Documents are containers for user data that can be stored in files and iCloud. In a document-based design, the app enables users to create and manage documents containing their data. One app typically handles multiple documents, each in its own window, and often displays more than one document at a time. For example, a word processor provides commands to create new documents, it presents an editing environment in which the user enters text and embeds graphics into the document, it saves the document data to disk (and, optionally, iCloud), and it provides other document-related commands, such as printing and version management. In Cocoa, the document-based app design is enabled by the document architecture, which is part of of the AppKit framework.
文档是用户数据的容器,可存储到文件和iCloud中。在基于文档的设计中,应用允许用户创建和管理包含他们的数据的文档。一个应用通常处理多个文档,每个文档在其自身的窗口中,并且应用经常同时显示多个文档。例如,一个文档处理器提供创建新文档的命令,它提供一个编辑环境,用户在其中输入文本、嵌入图片;它将文档数据存储到磁盘(也可以存储到iCloud);它提供其它文件相关的命令,比如打印和版本管理。在Cocoa中,基于文档的应用设计被文档架构所允许,这个文档架构是AppKit框架的一部分。
Documents in OS X
OS X中的文档
There are several ways to think of a document. Conceptually, a document is a container for a body of information that can be named and stored in a disk file and in iCloud. In this sense, the document is not the same as the file but is an object in memory that owns and manages the document data. To users, the document is their information—such as text and graphics formatted on a page. Programmatically, a document is an instance of a custom NSDocument subclass that knows how to represent internally persistent data that it can display in windows. This document object knows how to read document data from a file and create an object graph in memory for the document data model. It also knows how to handle the user’s editing commands to modify the data model and write the document data back out to disk. So, the document object mediates between different representations of document data, as shown in Figure 2-6.
有多个角度来看待一个文档。从概念上说,一个文档是信息的一个容器,可被命名,可存储到磁盘文件或iCloud中。这种情况下,文档与文件不同,它是一个内存中的对象,拥有并管理文档数据。对用户来说,文档是他们的信息——比如页面上格式化的文本和图片。从编程上说,一个文档是自定义NSDocument子类的一个实例,它知道如何表示内部的持久化数据,也能够将这些数据显示在窗口中。文档对象知道如何从文件读取文档数据、如何在内存中为文档数据模型创建图形。它还知道如何处理用户的编辑命令,并据此来修改数据模型,并把文档数据回写到磁盘。所以,文档对象在文档数据的不同表示中起到中介的作用,如图2-6所示。
Figure 2-6 Document file, object, and data model
表2-6 文档文件、对象和数据模型
Using iCloud, documents can be shared automatically among a user’s computers and iOS devices. Changes to the document data are synchronized without user intervention. For information about iCloud, see Integrating iCloud Support Into Your App.
使用iCloud,文档可自动在用户计算机和iOS设备之间分享。文档数据变化可自动同步,无需用户介入。关于iCloud的信息,参考Integrating iCloud Support Into Your App。
The Document Architecture Provides Many Capabilities for Free
文档架构免费提供多种能力
The document-based style of app is a design choice that you should consider when you design your app. If it makes sense for your users to create multiple discrete sets of data, each of which they can edit in a graphical environment and store in files or iCloud, then you certainly should plan to develop a document-based app.
当你设计应用时,需要考虑这个选择——应用是否采用基于文档的形式。如果用户需要创建多个独立成套的数据,每份数据都可在图形环境下编辑、并保存到文档或iCloud中,那么就应当计划去开发一个基于文档的应用。
The Cocoa document architecture provides a framework for document-based apps to do the following things:
Cocoa文档架构为基于文档的应用提供了一个框架,可完成下列任务:
- Create new documents. The first time the user chooses to save a new document, it presents a dialog enabling the user to name and save the document in a disk file in a user-chosen location.
- 创建新文档。用户首次选择保存新文档时,它展示一个对话框,允许用户来命名、在用户选择的位置将文档存储到磁盘文件中。
- Open existing documents stored in files. A document-based app specifies the types of document it can read and write, as well as read-only and write-only types. It can represent the data of different types internally and display the data appropriately. It can also close documents.
- 打开存储在文件中的现有文档。一个基于文档的应用指定它能够读取和写入的文档类型,以及只读和只写的文档类型。它能够表示不同类型的数据,并将它们恰当地显示出来。它还能关闭文档。
- Automatically save documents. Document-based apps can adopt autosaving in place, and its documents are automatically saved at appropriate times so that the data the user sees on screen is effectively the same as that saved on disk. Saving is done safely, so that an interrupted save operation does not leave data inconsistent.
- 自动保存文档。基于文档的应用采用自动保存,它的文档会在适当的时候自动保存,以保证用户在屏幕上看到的数据与存储在磁盘上的数据完全一致。保存过程是安全的,所以一个被打断的保存操作不会留下不一致的数据。
- Asynchronously read and write document data. Reading and writing are done asynchronously on a background thread, so that lengthy operations do not make the app’s user interface unresponsive. In addition, reads and writes are coordinated using NSFilePresenter protocol and NSFileCoordinator class to reduce version conflicts. Coordinated reads and writes reduce version conflicts both among different apps sharing document data in local storage and among different instances of an app on different devices sharing document data via iCloud.
- 异步读取和写入文档数据。读取过程和写入过程是在后台线程上异步完成的,所以长时间的操作不会导致用户界面卡顿。另外,读取和写入使用NSFilePresenter协议和NSFileCoordinator类来协调,以减少版本冲突。经过协调的读取和写入可减少版本冲突,既对在本地存储中共享文档数据的不同应用有效,也对通过iCloud共享文档的同一应用在不同设备上的不同实例有效。
- Manage multiple versions of documents. Autosave creates versions at regular intervals, and users can manually save a version whenever they wish. Users can browse versions and revert the document’s contents to a chosen version using a Time Machine–like interface. The version browser is also used to resolve version conflicts from simultaneous iCloud updates.
- 管理文档的不同版本。自动保存定期创建新版本,用户也可根据意愿手动保存新版本。使用一个类似Time Machine的界面,用户可以浏览版本,并将文档内容转换到所选择的版本。版本浏览器也用于解决iCloud同时更新时产生的冲突。
- Print documents. The print dialog and page setup dialog enable the user to choose various page layouts.
- 打印文档。打印对话框和页面设置对话框允许用户选择多种页面布局。
- Monitor and set the document’s edited status and validate menu items. To avoid automatic saving of inadvertent changes, old files are locked from editing until explicitly unlocked by the user.
- 监控和设置文档的编辑状态、验证菜单项目。为避免自动保存无意的更改,旧文件被锁定而禁止编辑,除非用户明确地将其解锁。
- Track changes. The document manages its edited status and implements multilevel undo and redo.
- 追踪变动。文档管理它自身的编辑状态,实现多层撤销和重做操作。
- Handle app and window delegation. Notifications are sent and delegate methods called at significant lifecycle events, such as when the app terminates.
- 处理应用和窗口代理。在生命周期的显著事件发生时,会发送通知、调用代理方法,比如当应用结束时。
See Document-Based App Programming Guide for Mac for more detailed information about how to implement a document-based app.
参阅Document-Based App Programming Guide for Mac来获取实现基于文档应用的详细信息。
The App Life Cycle
应用生命周期
The app life cycle is the progress of an app from its launch through its termination. Apps can be launched by the user or the system. The user launches apps by double-clicking the app icon, using Launchpad, or opening a file whose type is currently associated with the app. In OS X v10.7 and later, the system launches apps at user login time when it needs to restore the user’s desktop to its previous state.
应用的生命周期是一个应用从加载到终结的过程。应用可由用户或系统来加载。用户使用Launchpad,通过双击应用图标来加载应用,或打开一个类型与应用关联的文件。在OS X v10.7及之后,当系统需要恢复用户桌面到其之前状态时,会在用户登录时加载应用。
When an app 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 app’s code. At that point, your app’s code takes over and your app is running.
当一个应用被加载时,系统会为其创建一个进程和系统相关的数据结构。在进程之内,会创建一个主线程,并使用这个主线程来开始执行应用代码。从这一刻起,应用代码接管,应用开始运行。
The main Function is the App Entry Point
main函数是应用的入口
Like any C-based app, the main entry point for a Mac app at launch time is the main function. In a Mac app, the main function is used only minimally. Its main job is to give control to the AppKit framework. Any new project you create in Xcode comes with a default main function like the one shown in Listing 2-1. You should normally not need to change the implementation of this function.
与任何基于C语言的应用类似,Mac应用加载时的主入口是main函数。在Mac应用中,main函数被尽可能少地使用。它的主要工作是把控制权交给AppKit框架。你在Xcode中创建的任何新工程,都会有一个类似列表2-1的默认main函数。你通常无需更改这个函数的实现。
Listing 2-1 The main function of a Mac app
列表2-1 Mac应用的main函数
#import <Cocoa/Cocoa.h>
int main(int argc, char *argv[])
{
return NSApplicationMain(argc, (const char **) argv);
}
The NSApplicationMain function initializes your app and prepares it to run. As part of the initialization process, this function does several things:
NSApplicationMain函数初始化应用,并为其运行做准备。作为初始化进程的一部分,这个函数做几件事情:
- Creates an instance of the NSApplication class. You can access this object from anywhere in your app using the sharedApplication class method.
- 创建NSApplication类的一个实例。可使用sharedApplication类方法,在应用的任何地方访问这个对象。
- 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 app’s 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.
- 加载Info.plist文件中NSMainNibFile键描述的nib文件,并实例化这个文件中的所有对象。这是应用的主nib文件,应当包含在加载周期的早期必须加载的应用程序代理和其它关键对象。加载期间非必须的其它对象,应当放置到单独的nib文件中,稍后加载。
- Calls the run method of your application object to finish the launch cycle and begin processing events.
- 调用应用程序对象的run方法,结束加载周期,开始处理事件。
By the time the run method is called, the main objects of your app are loaded into memory but the app is still not fully launched. The run method notifies the application delegate that the app is about to launch, shows the application menu bar, opens any files that were passed to the app, does some framework housekeeping, and starts the event processing loop. All of this work occurs on the app’s main thread with one exception. Files may be opened in secondary threads if the canConcurrentlyReadDocumentsOfType: class method of the corresponding NSDocument object returns YES.
run方法调用之后,应用的主要对象已经加载到内存,但应用还没有完全启动。run方法通知应用代理应用即将启动、显示应用目录栏、打开传递到应用的任何文件、进行框架清理、开始事件处理循环。所有这些工作都在主线程上发生,只有一个例外——如果相应的NSDocument对象的canConcurrentlyReadDocumentsOfType:类方法返回YES,文件或许会在二级线程上打开。
If your app 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 app was running. For more information about how to preserve your app’s user interface, see User Interface Preservation.
如果应用在启动周期保存了其用户界面,Cocoa会在启动时加载保存的数据,并使用它来重新创建应用最近一次运行时打开的窗口。关于如何保存应用用户界面的更多信息,参考User Interface Preservation.
The App’s Main Event Loop Drives Interactions
应用的主事件循环驱动交互
As the user interacts with your app, the app’s 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 app. The app 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-7.
当用户与应用交互时,应用主事件循环处理进来的事件,并将它们分配到适合的处理对象。当NSApplication对象首次创建时,它会与系统窗口服务器建立一个连接。系统窗口服务器会从底层硬件接收事件,并将它们传递给应用。应用也会建立一个先入先出的事件队列,来存储窗口服务器发送给它的事件。主事件循环负责取出并处理在队列中等待的事件,如图2-7所示。
Figure 2-7 The main event loop
图2-7 主事件循环
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 app terminates:
NSApplication对象的run方法是主事件循环的主要执行者。在封闭循环中,这个方法执行下列步骤,直至应用终结:
- Services window-update notifications, which results in the redrawing of any windows that are marked as “dirty.” 为应用更新通知服务,应用更新通知会使标记为“已变化”的窗口进行重绘。
- Dequeues an event from its internal event queue using the nextEventMatchingMask:untilDate:inMode:dequeue: method and converts the event data into an NSEvent object. 使用nextEventMatchingMask:untilDate:inMode:dequeue:方法从其事件队列中取出一个事件,并将事件数据转换成一个NSEvent对象。
- Dispatches the event to the appropriate target object using the sendEvent: method of NSApplication. 使用NSApplication的方法将sendEvent:事件分派到合适的目标对象。
When the app 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 window—the window that is currently accepting key presses. Mouse events are dispatched to the window in which the event occurred.
当应用分派事件时,sendEvent:方法使用事件类型来决定适合的目标。输入事件有两大类:键盘事件和鼠标事件。键盘事件会发送给键盘窗口——即当前接收键盘输入的窗口。鼠标事件会分派到事件发生的窗口。
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.
对于鼠标事件,窗口会查找事件发生所在时所在的视图,并首先将这个事件发送到那个视图对象。视图是响应者对象,能够响应任何类型的事件。如果视图是一个控件,它通常使用事件,为与其关联的目标创建一个动作信息。
The overall process for handling events is described in detail in Cocoa Event Handling Guide.
事件处理的整个过程,在Cocoa Event Handling Guide中有详细描述。
Automatic and Sudden Termination of Apps Improve the User Experience
应用的自动结束和立即结束可增强用户体验
In OS X v10.7 and later, the use of the Quit command to terminate an app is diminished in favor of more user-centric techniques. Specifically, Cocoa supports two techniques that make the termination of an app transparent and fast:
在OS X v10.7及其之后,为支持更加以用户为中心的技术,使用Quit命令来结束应用的情况减少了。更确切的说,Cocoa支持两种技术,使用结束应用更加透明和迅速:
- Automatic termination eliminates the need for users to quit an app. Instead, the system manages app termination transparently behind the scenes, terminating apps that are not in use to reclaim needed resources such as memory.
- 自动结束排除了用户退出应用的需要。作为代替,系统在幕后管理应用的结束——结束那些不使用的应用以回收比如内存之类的资源。
- Sudden termination allows the system to kill an app’s 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 app termination. Although Apple recommends that apps support both, an app can support one technique and not the other. Apps that support both techniques can be terminated by the system without the app being involved at all. On the other hand, if an app 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.
自动结束和立即结束是独立的技术,尽管它们都是被设计为增强应用结束的用户体验。尽管苹果推荐应用同时支持这两种技术,但应用可以只支持一种,而不支持另外一种。同时支持这两种技术的应用可被系统结束而完全无需应用参与。另一方面,如果应用支持立即结束而不支持自动结束,那么它必须被发送一个Quit事件,它需要这个事件来处理,而不显示任何用户界面对话框。
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 apps and have those apps available when they need them. Automatic termination makes that possible while ensuring that system performance is not adversely affected.
自动结束把管理进程的工作由用户转换给了系统,而系统更善于处理这项工作。用户不需要去手动管理进程。他们需要的仅是运行应用,当需要它们的时候使它们可见。自动结束使其可行,同时还保证了不会对系统表现产生不利影响。
Apps must opt in to both automatic termination and sudden termination and implement appropriate support for them. In both cases, the app must ensure that any user data is saved well before termination can happen. And because the user does not quit an autoterminable app, such an app 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 app launches.
应用必须在自动结束和立即结束之间选择其一,并为它们实现恰当的支持。在两种情况下,应用必须确保在结束发生之前,用户数据已被妥善保存。而且由于用户不会退出一个自动结束的应用,所以这样的应用也应当使用内建的Cocoa支持来保存其用户界面状态。保存和恢复界面状态为用户提供连续的体验。
For information on how to support for automatic termination in your app, see Automatic Termination. For information on how to support sudden termination, see Sudden Termination.
关于如何支持自动结束,参考Automatic Termination。关于如何支持立即结束,参考Sudden Termination。
Support the Key Runtime Behaviors in Your Apps
在应用中支持关键的运行时特性
No matter what style of app you are creating, there are specific behaviors that all apps should support. These behaviors are intended to help users focus on the content they are creating rather than focus on app 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 app. Declaring support for automatic termination is easy, but apps 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 kill the underlying process for an auto-terminable app at any time, so saving this information maintains continuity for the app. Usually, the system kills an app’s underlying process some time after the user has closed all of the app’s windows. However, the system may also kill an app with open windows if the app is not currently on screen, perhaps because the user hid it or switched spaces.
自动结束特性需要应用进行显式地编程。声明支持自动结束很简单,但应用需要与系统协同,保存用户界面的当前状态,以便以后根据需要来恢复。系统在任何时候都可以结束一个自动结束应用的底层进程,所以需要保存这些信息来维护应用的连续性。通常,当用户关闭了应用的所有窗口一段时间之后,系统会结束应用的底层进程。然而,如果应用不在当前屏幕,可能是用户隐藏了它或交换了空间,那么即使应用有打开的窗口,系统也可能会结束它。
To support automatic termination, you should do the following:
为支持自动结束,应做如下工作:
- Declare your app’s support for automatic termination, either programmatically or using an Info.plist key.
- 声明应用支持自动结束,采用编程方式或使用Info.plist一个的键。
- Support saving and restoring your window configurations.
- 支持保存和恢复窗口配置。
- Save the user’s data at appropriate times.
- 在适当的时候保存用户数据。
- Single-window, library-style apps should implement strategies for saving data at appropriate checkpoints.
- 单一窗口、库类型应用应当实现在适当检查点保存数据的方法。
- Multiwindow, document-based apps can use the autosaving and saveless documents capabilities in NSDocument.
- 多窗口、基于文档的应用可使用NSDocument中的自动保存和不需保存文档的功能。
- Whenever possible, support sudden termination for your app as well.
- 可能的时候,让你的应用也支持立即结束。
Enabling Automatic Termination in Your App
在应用中允许自动结束
Declaring support for automatic termination lets the system know that it should manage the actual termination of your app at appropriate times. An app has two ways to declare its support for automatic termination:
声明支持自动结束会让系统知道在适当的时刻管理实际的应用结束。应用有两种方式来声明支持自动结束:
- Include the NSSupportsAutomaticTermination key (with the value YES) in the app’s Info.plist file. This sets the app’s default support status.
- 在应用的Info.plist文件中包含NSSupportsAutomaticTermination键(值设置为YES)。这样设置了应用默认支持状态。
- Use the NSProcessInfo class to declare support for automatic termination dynamically. Use this technique to change the default support of an app that includes the NSSupportsAutomaticTermination key in its Info.plist file.
- 使用NSProcessInfo类动态声明支持自动结束。使用这项技术来改变Info.plist文件中包含NSSupportsAutomaticTermination键的应用的默认支持状态。
Automatic Data-Saving Strategies Relieve the User
自动数据保存方法可减轻用户负担
You should always avoid forcing the user to save changes to their data manually. Instead, implement automatic data saving. For a multiwindow app based on NSDocument, automatic saving is as simple as overriding the autosavesInPlace class method to return YES. For more information, see Document-Based App Programming Guide for Mac.
应当避免强制用户手动保存其数据的更改。相反,实现数据的自动保存。对于基于NSDocument的多窗口应用来说,自动保存简单到重写autosavesInPlace类方法,令其返回YES即可。有关更多信息,参考Document-Based App Programming Guide for Mac。
For a single-window, library-style app, 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 app window or quits the app (applicationWillTerminate:)
- 当用户关闭应用窗口或退出应用时(applicationWillTerminate:)
- When the app is deactivated (applicationWillResignActive:)
- 应用处于非活动状态时(applicationWillResignActive:)
- When the user hides your app (applicationWillHide:)
- 用户隐藏应用时(applicationWillHide:)
- Whenever the user makes a valid change to data in your app
- 用户进行了重要的数据更改时
The last item means that you have the freedom to save the user’s 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.
最后一项意味着你有可以在任何有意义的时候保存用户数据。例如,用户编辑一条数据的多个条目,可以在每个条目变化时单独保存,也可以在用户显示下一条数据时保存全部条目。使用这种增量更改保证了数据始终是最新的,但却要求更加精细的数据模型管理。这种情况下,Core Data可帮助你更轻松的处理变动。关于Core Data的信息,参考Core Data Starting Point.
Sudden Termination
立即结束
Sudden termination lets the system know that your app’s process can be killed directly without any additional involvement from your app. The benefit of supporting sudden termination is that it lets the system close apps more quickly, which is important when the user is shutting down a computer or logging out.
立即结束让系统知道应用进程可以直接结束,而与应用无任何牵连。支持立即结束的好处是允许系统更快地结束应用,这在用户关闭计算机或登录的时候非常重要。
An app has two ways to declare its support for sudden termination:
应用有两种方式声明支持立即结束:
- Include the NSSupportsSuddenTermination key (with the value YES) in the app’s Info.plist file.
- 在应用的Info.plist文件中包含NSSupportsSuddenTermination键(值设置为YES)。
- Use the NSProcessInfo class to declare support for sudden termination dynamically. You can also use this class to change the default support of an app that includes the NSSupportsSuddenTermination key in its Info.plist file.
- 使用NSProcessInfo类动态声明支持立即结束。使用这项技术来改变Info.plist文件中包含NSSupportsSuddenTermination键的应用的默认支持状态。
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 app 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.
使用NSProcessInfo类的disableSuddenTermination和enableSuddenTermination方法来禁用和启用立即终结。这些方法分别对应一个计数器,由进程维护。当计数器的值为0时,进程可被立即结束。当这个值大于0时,立即结束被禁用。
Enabling and disabling sudden termination dynamically also means that your app should save data progressively and not rely solely on user actions to save important information. The best way to ensure that your app’s information is saved at appropriate times is to support the interfaces in OS X v10.7 for saving your document and window state. Those interfaces facilitate the automatic saving of relevant user and app data. For more information about saving your user interface state, see User Interface Preservation. For more information about saving your documents, see Document-Based Apps Are Based on an NSDocument Subclass.
动态启用和禁用立即结束意味着应用应当循序渐进地保存数据,而不应单纯依靠用户来保存重要信息。确保应用信息在恰当的时候被保存,最好的办法是支持OS X v10.7中保存文档和窗口状态的接口。这些接口简化了自动保存用户和应用数据的任务。 有关保存用户界面状态的更多信息,参考User Interface Preservation。有关保存文档的更多信息,参考Document-Based Apps Are Based on an NSDocument Subclass。
For additional information about enabling and disabling sudden termination, see NSProcessInfo Class Reference.
有关启用和禁用立即结束的更多信息,参考NSProcessInfo Class Reference。
User Interface Preservation
用户界面保存
The Resume feature, in OS X v10.7 and later, saves the state of your app’s windows and restores them during subsequent launches of your app. Saving the state of your windows enables you to return your app to the state it was in when the user last used it. Use the Resume feature especially if your app supports automatic termination, which can cause your app to be terminated while it is running but hidden from the user. If your app supports automatic termination but does not preserve its interface, the app launches into its default state. Users who only switched away from your app might think that the app crashed while it was not being used.
在OS X v10.7及其之后的恢复功能,保存应用窗口的状态,并在下次启动应用时恢复它们。保存窗口状态允许恢复应用至用户上次使用它时的状态。尽量使用恢复功能,特别是应用支持自动结束时,自动结束会导致虽然正在运行但已被用户隐藏的应用结束。如果应用支持自动结束却不保存界面,应用会以默认状态启动。仅仅是从应用切换出来的用户可能会认为它在没被使用期间崩溃了。
Writing Out the State of Your Windows and Custom Objects
写出窗口和其它自定义对象的状态
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.
- 对每个窗口,应当使用setRestorable:方法设置其是否应当被保存。
- 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.
- 在启动期间,要使用提供的数据来将对象恢复至之前的状态。
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 app’s windows are the starting point for all save operations. Cocoa iterates over all of your app’s 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.
将应用程序状态写入磁盘及稍后恢复的实际过程是由Cocoa处理的,但你必须告诉Cocoa要保存什么。应用窗口是所有保存操作的起始点。Cocoa遍历应用的所有窗口,并保存那些isRestorable方法返回YES的窗口。大多数窗口是采用默认方式保存的,但也可以使用setRestorable:方法来改变窗口的保存状态。
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 app, the window controller also saves data from its associated document object.) Figure 2-8 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.
除保存窗口之外,Cocoa还为与窗口关联的大多数响应者对象保存了数据。更确切的说,它保存了与窗口关联的视图和窗口控制器。(对于多窗口基于文档的应用,窗口控制器还从与其关联的文档对象保存数据。)表2-8展示了当决定哪个对象要保存时Cocoa遵循的路径。窗口对象总是起始点,但其它相关对象也被保存了。
Figure 2-8 Responder objects targeted by Cocoa for preservation
表2-8 被Cocoa设定为保存目标的响应者对象
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. However, these responder objects do not have any inherent knowledge about your app’s data structures. Therefore, it is your responsibility to save your app’s data and 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:
所有Cocoa窗口和视图对象保存基本的大小和位置信息,以及可能影响它们当前显示方式的其它属性信息。例如,标签视图保存当前选择标签的序号,文本视图保存当前选择文本的位置和范围。然而,这些响应者对象不含有关于应用数据结构的任何内存信息。因此,保存恢复窗口至当前状态所需的应用数据和其它信息,是你要完成的任务。可在下列位置记录你的自定义状态信息:
- 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.
- 如果继承了NSWindow或NSView,在子类中实现encodeRestorableStateWithCoder:方法,使用它来记录相关数据。或者,你自定义的响应者对象可重写restorableStateKeyPaths方法,使用它来指定要保存属性的键路径。Cocoa使用键路径为相应属性定位和保存数据。属性必须遵从键-值编码和键-值观察。
- If your window has a delegate object, implement the window:willEncodeRestorableState: method for the delegate and use it to store any relevant data.
- 如果窗口拥有代理对象,实现代理的window:willEncodeRestorableState:方法,使用它来保存相关数据。
- In your window controller, use the encodeRestorableStateWithCoder: method to save any relevant data or configuration information.
- 在窗口控制器中,使用encodeRestorableStateWithCoder:方法来保存数据和配置信息。
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. You are expected to save the actual data that the window displays and enough information to reattach the window to the same data objects later.
合理选择要保存的数据,尽可能少地记录重新配置窗口和其关联对象所需的信息。还需要记录窗口显示的实际数据,以及将窗口重新关联到同一数据对象所需的足够信息。
Important: Never use the user interface preservation mechanism as a way to save your app’s 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 app data should always be saved independently in data files that are managed by your app.
重要提示:决不要将用户界面保存机制用作保存应用实际数据的一种方式。为界面恢复所创建的包会经常变化,而且如果在恢复过程中出现问题,这个包会被完全忽略。应用数据应当独立保存在由应用管理的数据文件中。
For information on how to use coder objects to archive state information, see NSCoder Class Reference. For additional information on what you need to do to save state in a multiwindow document-based app, see Document-Based App Programming Guide for Mac.
关于如何使用编码对象来打包状态信息,参考NSCoder Class Reference。关于在多窗口基于文档应用中保存状态所需工作的信息,参考Document-Based App Programming Guide for Mac。
Notifying Cocoa About Changes to Your Interface State
将界面状态的变化通知Cocoa
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 app or logs out.
每当一个响应者对象的保存状态变化时,通过调用该对象的invalidateRestorableState方法,将其标记为已变化(dirty)。这样操作后,在将来的某个点,encodeRestorableStateWithCoder:消息会被发送到响应者对象。将响应者标记为已变化让Cocoa知道它需要在恰当的时候将保存状态写入到磁盘。将对象设置为无效是一个轻量级操作,因为数据不会立即写入到磁盘。相反,在诸如用户切换到另外的应用或退出登录等关键时刻,变化会被合并及写入。
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.
如果使用restorableStateKeyPaths方法来声明需要保存的属性,Cocoa会保存和恢复响应者对象的这些属性。因此,你提供的关键路径应当遵从键-值观察并产生合适的通知。关于如何在对象中支持键-值观察,参考Key-Value Observing Programming Guide。
Restoring Your Windows and Custom Objects at Launch Time
在启动时恢复窗口和自定义对象
As part of your app’s 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 app’s windows. Every window must identify a restoration class that knows about the window and can act on its behalf at launch time to create the window when asked to do so by Cocoa.
作为应用正常启动周期的一部分,Cocoa会检查是否有保存的界面数据。如果有,Cocoa使用这些数据来尝试重建应用窗口。每个窗口必须明确一个知道关于此窗口的恢复类,并且当Cocoa要求恢复时,能够代表自身创建窗口。
The restoration class is responsible for creating both the window and all of the critical objects required by that window. For most app styles, the restoration class usually creates one or more controller objects as well. For example, in a single-window app, 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 these controller objects too, you typically use high-level application classes for your restoration classes. An app might use the application delegate, a document controller, or even a window controller as a restoration class.
恢复类负责创建窗口及其所需的所有关键对象。对于大多数应用来说,恢复类通常会同时创建一个或多个控制器对象。例如,在单一窗口应用中,恢复类可能会创建用于管理窗口的窗口控制器,并从中获取窗口。由于它创建这些控制器对象,所以通常使用高层应用程序类作为恢复类。应用可能使用应用程序代理、文档控制器或者窗口控制器作为恢复类。
During the launch cycle, Cocoa restores each preserved window as follows:
在启动周期内,Cocoa按如下步骤来恢复保存的窗口:
- Cocoa retrieves the window’s restoration class from the preserved data and calls its restoreWindowWithIdentifier:state:completionHandler: class method. Cocoa从保存的数据中获取窗口的恢复类,并调用其restoreWindowWithIdentifier:state:completionHandler:类方法。
- The restoreWindowWithIdentifier:state:completionHandler: class method must call the provided completion handler with the desired window object. To do this, it does one of the following: restoreWindowWithIdentifier:state:completionHandler:类方法必须使用要求的窗口对象来调用提供的完成块。为做到这一点,它需要完成下列工作之一:
- 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.
- 如果控制器对象已经存在(可能是由于它们已经从nib文件中加载了),这个方法从这些已经存在的对象中获取窗口。
- 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. 如果无法创建窗口,可能是由于相关的文档被用户删除了,restoreWindowWithIdentifier:state:completionHandler:方法应当向完成块传递一个error对象。
- Cocoa uses the returned window to restore it and any preserved responder objects to their previous state. Cocoa使用返回的窗口来恢复它及保存的响应者对象至之前的状态。
- 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.
- 标准的Cocoa窗口和视图对象可被恢复到它们之前的状态,而无需额外帮助。如果继承了NSWindow或NSView,实现restoreStateWithCoder:方法来恢复自定义状态。如果在自定义响应者对象中实现了restorableStateKeyPaths方法,Cocoa会自动将相关属性设置为保存的值,因此,你不必实现restoreStateWithCoder:方法来恢复这些属性。
- For the window delegate object, Cocoa calls the window:didDecodeRestorableState: method to restore the state of that object.
- 对于窗口代理对象,Cocoa调用window:didDecodeRestorableState:方法来恢复其状态。
- For your window controller, Cocoa calls the restoreStateWithCoder: method to restore its state.
- 对于窗口控制器,Cocoa调用restoreStateWithCoder:方法来恢复其状态。
When re-creating each window, Cocoa passes the window’s unique identifier string to the restoration class. You are responsible for assigning user interface identifier strings to your windows prior to preserving the window state. You can assign an identifier in your window’s nib file or by setting your window object's identifier property (defined in NSUserInterfaceItemIdentification protocol). For example, you might give your preferences window an identifier of preferences and then check for that identifier in your implementation. Your restoration class can use this identifier 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.
当恢复窗口时,Cocoa将该窗口的唯一辨识符字符串传递给恢复类。在保存窗口状态之前,要为窗口分配用户界面辨识字符串。可在窗口的nib文件中指定辨识符,或者设置窗口对象的identifier属性(在NSUserInterfaceItemIdentification协议中定义)。例如,你可能为偏好窗口设置了preferences辨识符,就可在实现中检查它。恢复类可使用这个辨识符来决定它需要重建哪个窗口及其相关联的对象。辨识符字符串的内容可以是任何你想要的内容,但应当是以后能帮助你辨识该窗口的东西。
For a single-window app 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 delegate’s class as the restoration class and implement the restoreWindowWithIdentifier:state:completionHandler: method similar to the implementation shown in Listing 2-2. Because the app has only one window, it returns the main window directly. If you used the application delegate’s class as the restoration class for other windows, your own implementation could use the identifier parameter to determine which window to create.
对于一个窗口控制器和窗口均从主nib文件加载的单一窗口应用来说,恢复类的工作相当直接。在这里,你可以使用应用代理类作为恢复类,并实现与列表2-2中类似的restoreWindowWithIdentifier:state:completionHandler: 方法。由于应用只有一个窗口,它直接返回主窗口。如果使用应用代理类作为其它窗口的恢复类,在实现中可使用辨识符参数来决定创建哪个窗口。
Listing 2-2 Returning the main window for a single-window app
列表2-2 为单一窗口应用返回主窗口
+ (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);
}
Apps 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 app, but the objects in your app’s 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 apps.
在应用设计中,核心架构的对象很重要,但它们并不是唯一需要考虑的对象。核心对象管理应用的高级特性,但应用视图层的对象做了显示内容、响应事件的大多数工作。在创建有趣、迷人的应用中,其它对象也发挥了重要作用。
The User Interface
用户界面
An app’s 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 app. Commands may apply to the app as a whole, to the currently active window, or to the currently selected object. You are responsible for defining the commands that your app supports and for providing the event-handling code to respond to them.
应用用户界面由一个菜单栏、一个或多个窗口、一个或多个视图组成。菜单栏是用户可执行的命令容器。命令可应用到应用程序整体、当前活动窗口或当前选择的对象。你负责定义应用支持的命令,并提供响应它们所需的事件处理代码。
You use windows and views to present your app’s 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 apps have one main window and may have one or more secondary windows or panels. Multiwindow apps 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-9 shows the menu bar, along with some standard windows and panels.
使用窗口和视图在屏幕上呈现应用的可视化内容,并管理与这些内容的即时交互。窗口是NSWindow类的实例。面板是NSPanel类(它是NSWindow的子类)的实例,用来呈现二级内容。单一窗口应用拥有一个主窗口以及一个或多个二级窗口或面板。多窗口应用拥有多个窗口来展示主要内容,也可拥有一个或多个二级窗口或面板。窗口的样式决定其在屏幕上的外观。图2-9显示了菜单栏以及一些标准化的窗口和面板。
Figure 2-9 Windows and menus in an app
图2-9 应用的窗口和菜单
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:
视图是NSView类的实例,定义了窗口中一个矩形区域的内容。视图是呈现内容和与用户交互的主要机制,负有许多职责。例如:
- 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.
- 绘图和动画支持。视图在其矩形区域内绘制内容。支持Core Animation层的视图可使用这些层为其内容添加动画。
- 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.
- 事件处理。视图接收事件。视图根据需要将事件传递给其它对象。
For information about creating and configuring windows, see Window Programming Guide. For information about using and creating view hierarchies, see View Programming Guide.
有关创建和配置窗口的信息,参考Window Programming Guide。有关使用和创建视图层级的信息,参考View Programming Guide。
Event Handling
事件处理
The system window server is responsible for tracking mouse, keyboard, and other events and delivering them to your app. When the system launches an app, it creates both a process and a single thread for the app. This initial thread becomes the app’s main thread. In it, the NSApplication object sets up the main run loop and configures its event-handling code, as shown in Figure 2-10. As the window server delivers events, the app queues those events and then processes them sequentially in the app’s main run loop. Processing an event involves dispatching the event to the object best suited to handle it. For example, mouse events are usually dispatched to the view in which the event occurred.
系统窗口服务器负责追踪鼠标、键盘及其它事件,并将它们发送到你的应用。当系统启动应用时,为应用同时创建一个进程和一个单一线程。这个初始化的线程成为应用的主线程。在这个主线程中,NSApplication对象建立主运行循环,配置其事件处理代码,如图2-10所示。当窗口服务器发送事件时,应用将事件存入队列,然后按顺序在主运行循环上执行它们。处理事件就是将其分派到最适合处理它的对象上。例如,鼠标事件通常被分派到事件发生的视图上。
Figure 2-10 Processing events in the main run loop
图2-20 在主运行循环中处理事件
Note: A run loop monitors sources of input on a specific thread of execution. The app’s 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.
提示:运行循环在一个特殊的执行线程上监视输入源。应用事件队列表示的是这些输入源之一。当事件队列为空时,主线程睡眠。当一个事件到达时,运行循环唤醒线程,将控制权分派到NSApplication对象,来处理事件。事件处理完成后,控制权被交回至主循环。主循环继续处理其它事件或其它输入源,如果无事可做,则使线程睡眠。关于运行循环和输入源工作的更多信息,参考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 app 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.
分发和处理事件是响应者对象的工作,响应者对象是NSResponder类的实例。NSApplication、 NSWindow、 NSDrawer,、NSView,、NSWindowController和 NSViewController类都是NSResponder的子类。将一个事件从事件队列取出后,应用将事件分配到事件发生的窗口对象上。窗口对象依次将事件传递给它的第一响应者。对于鼠标事件来说,第一响应者通常是点击发生所在的视图对象。例如,在一个按钮上发生的鼠标事件,会传递到相应的按钮对象上,
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 on, until the event is handled. This series of linked responder objects is known as the responder chain. Messages continue traveling up the responder chain—toward higher-level responder objects, such as a window controller or the application object—until 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 app. 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.
处理事件的响应者通常由应用设置一系列程序动作。例如,一个控件对象(NSControl类的子类)处理事件时,会向其它对象发送一条动作信息,这个其它对象通常是管理当前活动视图的控制器。处理动作消息时,控制器可能会采用要求这些视图进行重绘的方式,改变用户界面或调整视图位置。这种情况发生时,视图和图形基础设施开始接管,并使用最有效的方式来处理要求的重绘事件。
For more information about responders, the responder chain, and handling events, see Cocoa Event Handling Guide.
关于响应者、响应者链和处理事件的更多信息,参考Cocoa Event Handling Guide。
Graphics, Drawing, and Printing
图形、绘制和打印
There are two basic ways in which a Mac app can draw its content:
Mac应用使用两种基础方式来绘制其内容:
- Native drawing technologies (such as Core Graphics and AppKit)
- 原生的绘制技术(比如Core Graphics和AppKit)
- OpenGL
- 开放图形库(Open Graphics Library)
The native 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 view’s visual content, you mark all or part of the view invalid by calling its setNeedsDisplay: or setNeedsDisplayInRect: method. The system then calls your view’s drawRect: method (at an appropriate time) to accommodate the update. This cycle then repeats and continues throughout the lifetime of your app.
原生的OS X绘制技术通常使用Cocoa视图和窗口提供的基础设施来渲染和呈现自定义内容。当视图首次显示时,系统要求其绘制内容。系统视图自动绘制其内容,但自定义视图必须实现drawRect:方法。在这个方法中,使用原生绘制技术来绘制形状、文本、图片、渐变或其它可视化内容。当想要更新视图的可视化内容时,通过调用setNeedsDisplay:或setNeedsDisplayInRect:方法,将视图的全部或部分设置为无效。系统会调用视图的drawRect:方法(在适当的时刻)来适应更新。在应用生命周期内,这个循环重复、连续。
If you are using OpenGL to draw your app’s 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 app is responsible for initiating drawing updates at appropriate intervals.
如果使用OpenGL来绘制应用内容,仍需要创建窗口和视图来管理内容,但这些对象只简单地为OpenGL绘制上下文提供渲染平面。一旦获取绘制上下文之后,应用就要开始定期更新绘图。
For information about how to draw custom content in your views, see Cocoa Drawing Guide.
有关在视图中绘制自定义内容的信息,参考Cocoa Drawing Guide。
Text Handling
文本处理
The Cocoa text system, the primary text-handling system in 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 apps to create, edit, display, and store text with all the characteristics of fine typesetting.
Cocoa文件系统是OS X中的主要文本处理系统,负责Cocoa中所有可视化文本的处理和显示。它通过与文本相关的AppKit类来提供一整套高质量的印刷服务。AppKit类允许应用使用可良好排版的字符来创建、编辑、显示和存储文本。
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 world’s 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. Cocoa’s 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 app.
Cocoa文本系统提供了基础且先进的文本处理功能,同时还可满足联系紧密的计算机应用的额外需求:支持世界上所有现存语言字符集、可处理多种不同书写方向和不规则文本容器的强大布局功能,以及诸如字距调整、连字、断行、对齐等复杂的排版功能。Cocoa面向对象的文本系统被设计为既能够提供满足应用需要的所有功能,而又无需你进行学习或与系统进行过多交互。
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. App 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 apps with their own layout engine and those porting older ATSUI- or QuickDraw-based codebases to the modern world.
Cocoa文本系统的底层是Core Text,它提供了底层、基础的文本布局,也为Cocoa和WebKit等高级引擎提供了文本处理能力。Core Text为许多Cocoa文本技术提供了实现方法。应用开发者通常不需要直接访问Core Text。不过,那些必须直接使用Core Text的开发者,比如使用自定义布局引擎来编写应用、需要将旧式的基于ATSUI或QuickDraw的编码导出为最新,也可访问Core Text的API。
For more information about the Cocoa text system, see Cocoa Text Architecture Guide.
关于Cocoa文本系统的更多信息,参考Cocoa Text Architecture Guide。
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.
NSMenu和NSMenuItem类是所有类型菜单的基础。一个NSMenu类的实例管理一系列的菜单项,并逐个绘制它们。一个NSMenuItem类的实例代表一个菜单项;它包含NSMenu对象绘制和管理它所需的全部信息,自己却不做绘制和事件处理的工作。通常使用界面生成器来创建和管理菜单,所以无需写任何代码。
The application menu bar stretches across the top of the screen, replacing the menu bar of any other app when the app is foremost. All of an app’s menus in the menu bar are owned by one NSMenu instance that’s created by the app when it starts up.
应用程序的菜单栏横贯于屏幕的顶部,如果应用处于最前端,则会替换掉其它应用的菜单栏。应用位于菜单栏中的所有菜单者归一个NSMenu实例所有,这个实例由应用在启动时创建。
Xcode Templates Provide the Menu Bar
Xcode模板提供了菜单栏
Xcode’s Cocoa application templates provide that NSMenu instance in a nib file called MainMenu.xib. This nib file contains an application menu (named with the app’s name), a File menu (with all of its associated 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 in the File’s Owner that displays a standard About window.
Xcode的Cocoa应用程序模板在称为MainMenu.xib的nib文件中提供了NSMenu实例。这个nib文件包含一个应用程序目录(以应用名称命名),一个文件目录(包含相关的命令),一个编辑目录(包含文本编辑命令和撤销、重做目录项)和格式、视图、窗口、帮助目录(包含表示命令的目录项)。这些目录项与文件目录中的所有目录项一样,都与适当的第一响应者动作方法相联。例如,关于目录项与File’s Owner中orderFrontStandardAboutPanel:的方法相联,File’s Owner显示一个标准的关于窗口。
The template has similar ready-made connections for the Edit, Format, View, Window, and Help menus. If your app 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 app, taking advantage of the menu mechanism in the template to ensure that everything is in the right place.
模板拥有已创建的编辑、格式、视图、窗口和帮助目录的连接。如果应用不支持提供的动作(比如打印),应当在nib中删除相关的目录项。或者,你可能想对目录命令或操作方法进行重定义或重命名,利用好模板中的菜单机制,确保一切顺利。
Connect Menu Items to Your Code or Your First Responder
将目录项连接至代码或第一响应者
For your app’s 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 app:
对于应用中的自定义目录项,它们尚未连接到nib文件中的对象或占位符对象的操作方法,在Mac应用中有两种常用技术来处理目录命令:
- 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 app, 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.
这两种技术中,第一种通常用于针对作为响应者链的一部分的当前文档或其内容的目录命令。第二种技术主要用于处理应用全局性的命令,比如显示偏好或创建一个新文档。自定义应用程序对象或其代理将事件分派到文档是可能的,但这样做效率低下且易出错。除了实现操作方法来响应菜单命令之外,还必须实现NSMenuValidation协议的方法,来为这些命令启用菜单项。
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.
Designing User Interfaces in Xcode给出了关于将菜单项连接到代码中的操作方法的步骤说明。关于目录验证和其它目录话题,参考Application Menu and Pop-up List Programming Topics。