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 Foundationframeworks, 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 specificprotocol 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, seeImplementing 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 themodel, 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 NSApplication對象 | (Required) Runs the event loop and manage interactions between your app and the system. You typically use theNSApplication 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 theNSApplication 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 thedocument 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 theNSDocumentController 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 insertedNSDocumentController and NSDocument objects. (For information about the roles of the other objects in the diagram, seeTable 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, seeThe 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 sharedNSUbiquitousKeyValueStore 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 useNSDocument objects to manage your content. TheNSDocument 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 customNSDocument 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, seeIntegrating 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 andNSFileCoordinator 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 themain 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 defaultmain 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 thesharedApplication class method.創建NSApplication類的一個實例。可使用sharedApplication類方法,在應用的任何地方訪問這個對象。Loads the nib file specified by the NSMainNibFile key in theInfo.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. Therun 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 correspondingNSDocument 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 theNSApplication 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 anNSEvent 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 areresponder objects and are capable of responding to any type of event. If the view is a control, it typically uses the event to generate anaction 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, seeAutomatic Termination. For information on how to support sudden termination, seeSudden 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 anInfo.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 inNSDocument.多窗口、基于文檔的應用可使用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 valueYES) 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 theNSSupportsAutomaticTermination key in itsInfo.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 onNSDocument, automatic saving is as simple as overriding theautosavesInPlace class method to return YES. For more information, seeDocument-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 valueYES) 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 theNSSupportsSuddenTermination key in itsInfo.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 thedisableSuddenTermination 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, seeUser Interface Preservation. For more information about saving your documents, seeDocument-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, seeNSProcessInfo 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 thesetRestorable: 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 thesetRestorable: 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 customresponder objects can override therestorableStateKeyPaths 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 withkey-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, seeNSCoder Class Reference. For additional information on what you need to do to save state in a multiwindow document-based app, seeDocument-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 theinvalidateRestorableState 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 bekey-value observing compliant and generate the appropriate notifications. For more information on how to support key-value observing in your objects, seeKey-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 itsrestoreWindowWithIdentifier: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, therestoreWindowWithIdentifier: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 subclassNSWindow orNSView, implement therestoreStateWithCoder: method to restore any custom state. If you implemented therestorableStateKeyPaths 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 ofpreferences 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 theNSWindow class. A panel is an instance of theNSPanel class (which is a descendant ofNSWindow) 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, seeView 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, seeThreading Programming Guide.
提示:運行循環在一個特殊的執行線程上監視輸入源。應用事件隊列表示的是這些輸入源之一。當事件隊列為空時,主線程睡眠。當一個事件到達時,運行循環喚醒線程,將控制權分派到NSApplication對象,來處理事件。事件處理完成后,控制權被交回至主循環。主循環繼續處理其它事件或其它輸入源,如果無事可做,則使線程睡眠。關于運行循環和輸入源工作的更多信息,參考Threading Programming Guide。
Distributing and handling events is the job of responder objects, which are instances of theNSResponder 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 ofNSControl) 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, seeCocoa 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’sdrawRect: 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, seeCocoa 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 andNSMenuItem are the basis for all types of menus. An instance ofNSMenu manages a collection of menu items and draws them one beneath another. An instance ofNSMenuItem represents a menu item; it encapsulates all the information itsNSMenu 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 oneNSMenu 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 calledMainMenu.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 theNSMenuValidation 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, seeApplication Menu and Pop-up List Programming Topics.
Designing User Interfaces in Xcode給出了關于將菜單項連接到代碼中的操作方法的步驟說明。關于目錄驗證和其它目錄話題,參考Application Menu and Pop-up List Programming Topics。
原文摘自蘋果官網,譯文為原創,請勿轉載。水平有限,譯文有錯誤、疏漏之處,敬請指正。知識有價,感謝您支持原創,微信掃碼贊助:郵箱:[email protected]微信公眾號CocoaLover,掃碼關注:
新聞熱點
疑難解答