File System Programming---(三)

Accessing Files and Directories

Before you can open a file, you first have to locate it in the file system. The system frameworks provide many routines for obtaining references to many well-known directories, such as the Library directory and its contents. You can also specify locations manually by building a URL or string-based path from known directory names.html

在你能夠打開一個文件以前,你首先須要在文件系統裏找到它。系統框架提供了不少例程來得到對衆所周知目錄的引用,好比Library 目錄以及它的內容。 你還能夠經過從已知的目錄名創建一個URL或基於字符串的路徑手動指定位置。express

When you know the location of a file, you can then start planning the best way to access it. Depending on the type of file, you may have several options. For known file types, you typically use built-in system routines to read or write the file contents and give you an object that you can use. For custom file types, you may need to read the raw file data yourself.編程

當你知道了文件的位置以後,你就能夠開始計劃用最好的方式來訪問它。 根據文件的類型,你能夠有多種選擇。 對於已知的文件類型,你一般使用內建系統例程來讀取或寫入文件內容,而後返回一個你可使用的對象。 對於自定義的文件類型,你可能須要本身讀取原始文件數據。數組

Choose the Right Way to Access Files

1、選擇正確的方法來訪問文件緩存

Although you can open any file and read its contents as a stream of bytes, doing so is not always the right choice. OS X and iOS provide built-in support that makes opening many types of standard file formats (such as text files, images, sounds, and property lists) much easier. For these standard file formats, you should use the higher-level options for reading and writing the file contents. Table 2-1 lists the common file types supported by the system along with information about how you access them.安全

儘管你能夠打開任何文件並以字節流的方式讀取它的內容,可是這不老是正確的選擇。 OS X 和 iOS 提供了更簡單打開不少標準文件格式(好比文本文件,圖像,聲音,以及屬性列表)的內建方法。對於這些標準文件格式,你應該使用更高級別的選項來讀取和寫入文件內容。表2-1 列出了系統支持的經常使用文件類型以及如何訪問它們的相關信息。服務器

Table 2-1  File types with specialized routines
表 2-1 帶有專門例程的文件類型

File Type網絡

Examples               數據結構

Descriptionapp

Resource files

Nib files

Image files

Sound files

Strings files

Localized resources

Apps use resource files to store data that is independent of the code that uses it. Resource files are commonly used to store localizable content such as strings and images. The process for reading data from a resource file depends on the resource type.

應用程序使用資源文件來存儲獨立於代碼的數據,這些數據被代碼使用。資源文件一般用於存儲本地化的內容,好比字符串和圖像。從資源文件讀取數據的過程取決於資源類型。

For information about how to read the contents of resource files, see Resource Programming Guide.

關於如何讀取資源文件內容的信息,請看Resource Programming Guide.

Text files

Plain text file

UTF-8 formatted file

UTF-16 formatted file

A text file is an unstructured sequence of ASCII or Unicode characters. You typically load the contents of a text file into anNSString object but may also read and write a text file as a raw stream of characters.

文本文件是一個非結構化的ASCII碼或Unicode字符序列。你一般把一個文本文件的內容載入一個NSString 對象,可是也可能以原始字節流的方式讀取或寫入一個文本文件。

For information about using the NSStringclass to load text from a file, see String Programming Guide.

關於使用NSString類從一個文件加載文本的信息,請看String Programming Guide.

Structured data files

XML file

Property list file

Preference file

A structured data file usually consists of string-based data arranged using a set of special characters.

結構性數據文件經常是由一組特殊字符組成的基於字符串的數據組成。

For information about parsing XML, seeEvent-Driven XML Programming Guide.

關於解析XML的信息,請看Event-Driven XML Programming Guide.

Archive files

Binary files created using a keyed archiver object

An archive is a file format used to store a persistent version of your app’s runtime objects. An archiver object encodes the state of the objects into a stream of bytes that can be written to disk all at once. An unarchiver reverses the process, using the stream of bytes to re-create the objects and restore them to their previous state.

歸檔是一種文件格式,它用來存儲應用程序運行時對象的一個持久性版本。歸檔對象把對象的狀態編碼爲字節流,用以一次性所有都寫入磁盤。 取消歸檔則相反,它使用字節流來從新建立對象,並讓它們恢復到它們先前狀態。

Archives are often a convenient alternative to implementing custom binary file formats for your documents or other data files.

歸檔經常是爲你的文檔或其它數據文件實現自定義二進制文件的一種便利替代方法。

For information on how to create and read archive files, see Archives and Serializations Programming Guide.

關於如何建立和讀取歸檔文件的信息,請看Archives and Serializations Programming Guide.

File packages

Custom document formats

A file package is a directory that contains any number of custom data files but which is presented to the user as if it were a single file. Apps can use file packages to implement complex file formats that contain multiple distinct files, or a mixture of different types of files. For example, you might use a file package if your file format includes both a binary data file and one or more image, video, or audio files. You access the contents of a file package using NSFileWrapperobjects.

文件包是一個目錄,它包含了任意數量的自定義數據文件,可是它呈現給用戶時是做爲一個單一文件顯示。應用程序可使用文件包來實現複雜的文件格式,它包含多個不一樣的文件,或多個不一樣類型文件的混合。 好比,若是你的文件格式包含一個二進制數據文件以及一個或多個圖像,視頻或音頻文件,你或許會使用一個文件包來實現。 你可使用NSFileWrapper 對象來訪問文件包的內容。

Bundles

Apps

Plug-ins

Frameworks

Bundles provide a structured environment for storing code and the resources used by that code. Most of the time, you do not work with the bundle itself but with its contents. However, you can locate bundles and obtain information about them as needed.

束提供了一個結構化環境來存儲代碼以及代碼使用的各類資源。大多數時間,你不會跟束自己而是跟它的內容一塊兒工做。然而,你能夠根據須要定位束並獲取它們的信息。

For information about bundles and how you access them, see Bundle Programming Guide

關於束以及如何訪問它們的信息,請看Bundle Programming Guide

Code files

Binary code resources

Dynamic shared libraries

Apps that work with plug-ins and shared libraries need to be able to load the associated code for that item to take advantage of its functionality.

帶有插件和共享庫的應用程序須要可以加載那項的相關代碼以充分利用其功能。

For information about how to load code resources into memory, see Code Loading Programming Topics.

關於如何加載代碼資源到內存的信息,請看Code Loading Programming Topics.

A file wrapper

A collection of files that appear as a single file

Apps use file wrappers to store files in a manner that can be written in a serialized manner that can use used with the pasteboard or stored as part of your data record. See 「Using FileWrappers as File Containers」 for more information on file wrappers.

應用程序使用文件封裝(file wrappers)來存儲文件,它以能被序列化方式寫入的方式來存儲。該方式能能使用粘貼板或做爲你的數據記錄的一部分來存儲。

瞭解文件封裝的更多信息,請看 「Using FileWrappers as File Containers」

In situations where the standard file formats are insufficient, you can always create your own custom file formats. When reading and writing the content of custom files, you read and write data as a stream of bytes and apply those bytes to your app’s file-related data structures. You have complete control over how you read and write the bytes and how you manage your file-related data structures. For more information about the techniques for reading and writing files that use custom file formats, see 「Techniques for Reading and Writing Files Without File Coordinators.」

當標準文件格式不符合你的要求時,你老是能夠建立你本身的自定義文件格式。當你讀取或寫入自定義文件內容時,你以字節流的方式讀取和寫入並把那些字節應用到應用程序文件相關的數據結構內。你已經徹底能掌控如何讀取和寫入字節以及如何管理文件相關的數據結構。

關於對自定義文件格式文件的讀取和寫入技術的更多信息,請看「Techniques for Reading and Writing Files Without File Coordinators.」

Specifying the Path to a File or Directory

2、指定文件或目錄的路徑

The preferred way to specify the location of a file or directory is to use the NSURLclass. Although the NSString class has many methods related to path creation, URLs offer a more robust way to locate files and directories. For apps that also work with network resources, URLs also mean that you can use one type of object to manage items located on a local file system or on a network server.

指定一個文件或目錄的位置的首選方式是使用NSURL類。儘管 NSString 類提供了不少關於路徑建立的方法,可是URLs 提供了一個更強大的方式來定位文件和目錄。 對於那些還跟網絡資源一塊兒工做的應用程序, URLs 還意味着你使用一種類型的對象就能夠管理位於一個本地文件系統或一個網絡服務器中的數據項。

Note: In addition to NSURL, you can also use the CFURLRef opaque type to manipulate paths as URLs. The NSURL class is toll-free bridged with theCFURLRef type, which means you can use them interchangeably in your code. For more information about how to create and manipulate URLs using Core Foundation, see CFURL Reference.

 注意: 除了NSURL, 你還可使用CFURLRef 不透明類型來把路徑做爲URLs來操做。NSURL 類和CFURLRef 類型徹底相連,就是說你能夠在代碼是交替使用它們。 關於如何使用Core Foundation建立和操做URLs的更多信息,請看CFURL Reference

For most URLs, you build the URL by concatenating directory and file names together using the appropriate NSURL methods until you have the path to the item. A URL built in that way is referred to as a path-based URL because it stores the names needed to traverse the directory hierarchy to locate the item. (You also build string-based paths by concatenating directory and file-names together, with the results stored in a slightly different format than that used by the NSURL class.) In addition to path-based URLs, you can also create a file reference URL, which identifies the location of the or directory using a unique ID.

對於大多數URLs, 你經過使用適當的NSURL方法來把目錄和文件名串聯到一塊兒直到你獲取到數據項的路徑。以那個方法構建的URL是做爲基於路徑的URL引用的,由於它存儲了遍歷目錄層次來定位數據項所需的名字。(你還能夠經過串聯目錄和文件名來構建基於字符串的路徑,它們在格式上跟NSURL 類有輕微的差異。) 除了基於路徑的URLs,你也能夠建立一個文件引用的URL,它使用一個惟一的ID標識目錄位置。

All of the following entries are valid references to a file called MyFile.txt in a user’s Documents directory:

全部如下方式均可以有效引用用戶Documents 文件夾裏的MyFile.txt文件:

Path-based URL: file://localhost/Users/steve/Documents/MyFile.txt

File reference URL: file:///.file/id=6571367.2773272/

String-based path: /Users/steve/Documents/MyFile.txt

Different file systems rely on different separator characters. Because of these changes, you should create your URL objects using the methods provided by the NSURL class.

不一樣的文件系統依賴不一樣的分隔字符。由於這些改變,你應該使用NSURL類提供的方法來建立你的URL對象。

You create URL objects the NSURL methods and convert them to file reference URLs only when needed. Path-based URLs are easier to manipulate, easier to debug, and are generally preferred by classes such as NSFileManager. An advantage of file reference URLs is that they are less fragile than path-based URLs while your app is running. If the user moves a file in the Finder, any path-based URLs that refer to the file immediately become invalid and must be updated to the new path. However, as long as the file moved to another location on the same disk, its unique ID does not change and any file reference URLs remain valid.

你用NSURL方法來建立URL對象,並只在須要時把它們轉化爲文件引用URLs。基於路徑的URLs更容易操做,調試,而且一般是NSFileManager 類的首選。 文件引用URLs的優勢是當應用程序運行時它們不像基於路徑的URLs那樣脆弱。若是用戶在Finder裏移動了一個文件,任何引用到該文件的基於路徑的URLs當即變成無效引用,而且必須更新到新路徑。 然而,只要文件被移動到同一個磁盤的另外一個地方,它的惟一ID不會改變而且任何文件引用URLs任然有效。

Important: Although they are safe to use while your app is running, file reference URLs are not safe to store and reuse between launches of your app because a file’s ID may change if the system is rebooted. If you want to store the location of a file persistently between launches of your app, create a bookmark as described in 「Locating Files Using Bookmarks.」

主要提示:儘管它們在應用程序運行時也能安全的使用,可是文件引用URLs在啓動應用程序期間存儲和從新使用並不安全, 由於文件的ID在系統重啓時可能發生改變。 若是你想在啓動應用程序期間也能存儲保持不變的文件地址,建立一個書籤。詳情請看「Locating Files Using Bookmarks.」

 

Of course, there are still times when you might need to use strings to refer to a file. Fortunately, the NSURL class provides methods to convert path-based URLs to and from NSString objects. You might use a string-based path when presenting that path to the user or when calling a system routine that accepts strings instead of URLs. The conversion between NSURL objects and NSString objects is done using the NSURL class’s method absoluteString.

固然,你有時候任然可能須要使用字符串來引用到一個文件。幸運的是,NSURL類提供了轉換基於路徑的URLs 和 NSString 對象的方法。當你向用戶呈現那個路徑或調用一個接收字符串做爲參數的系統例程時,你可能須要使用基於字符串的路徑。NSURL 對象和NSString 對象的轉換使用NSURL類的absoluteString 方法。

Because NSURL and NSString describe only the location of a file or directory, you can create them before the actual file or directory exists. Neither class attempts to validate the actual existence of the file or directory you specify. In fact, you must create the path to a nonexistent file or directory before you can create it on disk.

由於NSURL 和 NSString 只描述了文件或目錄的位置,全部你能夠在實際文件或目錄存在以前就建立它們。 無論類嘗試驗證指定文件或目錄的真實存在。事實上,你必須在磁盤上建立以前,先建立一個不存在的文件或目錄路徑。

For more information about the methods you use to create and manipulate URLs and strings, see NSURL Class Reference and NSString Class Reference.

關於你用來建立和操做URLs和字符串的方法,請看 NSURL Class Reference 和 NSString Class Reference.

Locating Items in the File System

3、在文件系統裏定位數據項

Before you can access a file or directory, you need to know its location. There are several ways to locate files and directories:

在你能訪問文件或目錄以前,你須要知道它的位置。 如下有幾種定位文件和目錄的方法:

  • Find the file yourself.

    本身找到文件。

  • Ask the user to specify a location.

    讓用戶指定一個位置。

  • Locating files in the standard system directories, in both sandboxed and non-sandboxed apps.

    在沙盒和非沙盒應用程序的標準系統目錄裏定位文件。

  • Using bookmarks.

    使用書籤。

The file systems of iOS and OS X impose specific guidelines on where you should place files, so most of the items your app creates or uses should be stored in a well-known place. Both systems provide interfaces for locating items in such well-known places, and your app can use these interfaces to locate items and build paths to specific files. An app should prompt the user to specify the location of a file or directory only in a limited number of situations that are described in 「Using the Open and Save Panels.」

iOS 和 OS X 文件系統中給你應該把文件放哪裏設置了具體的指導方針,所以應用程序建立或使用的大多數數據都存儲在一個衆所周知的地方。兩個系統都提供了接口用來定位這些數據在衆所周知地方的位置,而且你的應用程序能使用這些接口來定位數據項並建立路徑到指定文件。應用程序應該只在有限狀況下才讓用戶指定文件或目錄的位置,請看「Using the Open and Save Panels.」

Asking the User to Locate an Item

一、讓用戶定位一個數據項

In OS X, user interactions with the file system should always be through the standard Open and Save panels. Because these panels involve interrupting the user, you should use them only in a limited number of situations:

在OS X 中,用戶應該老是經過標準打開和保存面板來跟文件系統交互。由於這些面板牽涉到打斷用戶,因此你應該只在如下幾種狀況下才使用它們:

  • To open user documents

    打開用戶文檔。

  • To ask the user where to save a new document

    詢問用戶在哪存儲新文檔。

  • To associate user files (or directories of files) with an open window

    用一個開放窗口關聯用戶文件(或文件的目錄)。

You should never use the Open and Save panels to access any files that your app created and uses internally. Support files, caches, and app-generated data files should be placed in one of the standard directories dedicated to app-specific files.

你毫不應該使用打開和保存面板來訪問應用程序建立的任何文件並在內部使用。 Support files(支持文件), caches(緩存), 以及app-generated data files(應用程序生成的數據文件)應該放置在專門爲特定應用程序文件準備的標準目錄中。

For information on how to present and customize the Open and Save panels, see「Using the Open and Save Panels.」

關於如何呈現和自定義打開和保存面板的信息,請看「Using the Open and Save Panels.」

Locating Items in Your App Bundle

二、在應用程序束裏定位數據項

Apps that need to locate resource files inside their bundle directory (or inside another known bundle) must do so using an NSBundle object. Bundles eliminate the need for your app to remember the location of individual files by organizing those files in a specific way. The methods of the NSBundle class understand that organization and use it to locate your app’s resources on demand. The advantage of this technique is that you can generally rearrange the contents of your bundle without rewriting the code you use to access it. Bundles also take advantage of the current user’s language settings to locate an appropriately localized version of a resource file.

須要在應用程序的束目錄(或在另外一個已知束)裏定位資源文件的應用程序必須使用一個NSBundle 對象。束經過一種特殊方式組織那些文件,消除了應用程序記住單個文件位置的需求。NSBundle類的方法瞭解那個組織方式並根據須要使用它來定位應用程序的資源。該技術的優勢是你一般能夠從新組織束的內容而不用從新編寫你用來訪問它的代碼。 束還能充分利用當前用戶的語言設置來定位資源文件的一個合適的本地化版本。

The following code shows how to retrieve a URL object for an image namedMyImage.png that is located in the app’s main bundle. This code determines only the location of the file; it does not open the file. You would pass the returned URL to a method of the NSImage class to load the image from disk so that you could use it.

如下代碼顯示瞭如何爲在應用程序主束裏的Image.png文件取回一個URL對象。該代碼只決定了文件的位置;但不打開文件。你能夠把返回的URL傳給NSImage類的方法來從磁盤載入圖片,而後使用它。

NSURL* url = [[NSBundle mainBundle] URLForResource:@"MyImage" withExtension:@"png"];

For more information about bundles, including how to locate items in a bundle, seeBundle Programming Guide. For specific information about loading and using resources in your app, see Resource Programming Guide.

關於束的更多信息,包括如何在束裏定位數據項,請看Bundle Programming Guide。關於在應用程序里加載和使用資源具體信息,請看Resource Programming Guide。  

Locating Items in the Standard Directories

三、在標準目錄裏定位數據項

When you need to locate a file in one of the standard directories, use the system frameworks to locate the directory first and then use the resulting URL to build a path to the file. The Foundation framework includes several options for locating the standard system directories. By using these methods, the paths will be correct whether your app is sandboxed or not:

當你須要在其中一個標準目錄裏定位一個文件時,首先使用系統框架來定位目錄,而後使用結果URL來構建一個到文件的路徑。Foundation 框架包含了不少用來定位標準系統目錄的各類選項。經過使用這些方法,無論應用程序是不是沙盒路徑都會是正確的。

  • The URLsForDirectory:inDomains: method of the NSFileManager class returns a directory’s location packaged in an NSURL object. The directory to search for is an NSSearchPathDirectory constant. These constants provide URLs for the user’s home directory, as well as most of the standard directories.

     URLsForDirectory:inDomains: 方法,是NSFileManager類的一個方法,它返回一個包含目錄路徑的NSURL對象。要查找的目錄是一個NSSearchPathDirectory 常量。 這些常量提供了用戶主目錄以及大多數標準目錄的URLs。

  • The NSSearchPathForDirectoriesInDomains function behaves like theURLsForDirectory:inDomains: method but returns the directory’s location as a string-based path. You should use the URLsForDirectory:inDomains:method instead.

    NSSearchPathForDirectoriesInDomains 函數跟URLsForDirectory:inDomains:方法相似,可是它返回一個基於字符串的目錄路徑。你應該使用      URLsForDirectory:inDomains:  方法替換它。

  • The NSHomeDirectory function returns the path to either the user’s or app’s home directory. (Which home directory is returned depends on the platform and whether the app is in a sandbox.) When an app is sandboxed the home directory points to the app’s sandbox, otherwise it points to the User’s home directory on the file system. If constructing a file to a subdirectory of a user’s home directory, you should instead consider using theURLsForDirectory:inDomains: method instead.

    NSHomeDirectory 函數返回用戶或應用程序的主目錄。(返回的主目錄依賴於平臺以及應用程序是不是在一個沙盒內。) 當應用程序在沙盒,則返回指向應用程序沙盒的主目錄,不然它在文件系統中指向用戶的主目錄。若是向用戶的主目錄下的子目錄構建一個文件,你應該考慮使用URLsForDirectory:inDomains: 方法替代。

You can use the URL or path-based string you receive from the preceding routines to build new objects with the locations of the files you want. Both the NSURL andNSString classes provide path-related methods for adding and removing path components and making changes to the path in general. Listing 2-1 shows an example that searches for the standard Application Support directory and creates a new URL for a directory containing the app’s data files.

你可使用從前一個程序返回的URL或基於路徑的字符串來構建包含你想訪問文件位置的新對象。NSURL 和 NSString 類都提供路徑相關的各類方法來添加和刪除路徑組件以及對路徑作通常性修改。 列表2-1 顯示了一個例子,該例子查找標準Application Support目錄以及建立了一個包含應用程序數據文件的目錄的新URL。

Listing 2-1  Creating a URL for an item in the app support directory

- (NSURL*)applicationDataDirectory {
    NSFileManager* sharedFM = [NSFileManager defaultManager];
    NSArray* possibleURLs = [sharedFM URLsForDirectory:NSApplicationSupportDirectory
                                 inDomains:NSUserDomainMask];
    NSURL* appSupportDir = nil;
    NSURL* appDirectory = nil;
 
    if ([possibleURLs count] >= 1) {
        // Use the first directory (if multiple are returned)
        appSupportDir = [possibleURLs objectAtIndex:0];
    }
 
    // If a valid app support directory exists, add the
    // app's bundle ID to it to specify the final directory.
    if (appSupportDir) {
        NSString* appBundleID = [[NSBundle mainBundle] bundleIdentifier];
        appDirectory = [appSupportDir URLByAppendingPathComponent:appBundleID];
    }
 
    return appDirectory;
}

Locating Files Using Bookmarks

四、用書籤訂位文件

If you want to save the location of a file persistently, use the bookmark capabilities of NSURL. A bookmark is an opaque data structure, enclosed in an NSData object, that describes the location of a file. Whereas path- and file reference URLs are potentially fragile between launches of your app, a bookmark can usually be used to re-create a URL to a file even in cases where the file was moved or renamed.

若是你想要長期保持一個文件的位置,使用NSURL 的書籤功能。 書籤是一個不透明數據結構,它被封裝在一個NSData 對象中,用於描述一個文件的位置。 鑑於基於路徑以及文件引用URLs都在應用程序啓動期間都有潛在脆弱,可是書籤一般能用來從新建立一個文件的URL,即便該文件已經被移動或從新命名。

To create a bookmark for an existing URL, use thebookmarkDataWithOptions:includingResourceValuesForKeys:relativeToURL:error:method of NSURL. Specifying theNSURLBookmarkCreationSuitableForBookmarkFile option creates an NSDataobject suitable for saving to disk. Listing 2-2 shows a simple example implementation that uses this method to create a bookmark data object.

要想爲一個已經存在的URL建立一個書籤,使用NSURL的

bookmarkDataWithOptions:includingResourceValuesForKeys:relativeToURL:error:  方法。指定 NSURLBookmarkCreationSuitableForBookmarkFile 選項建立一個適合用來存儲到磁盤的一個NSData 對象。 列表2-2 顯示了一個簡單的例子用來表現使用該方法來建立一個書籤數據對象。

Listing 2-2  Converting a URL to a persistent form

- (NSData*)bookmarkForURL:(NSURL*)url {
    NSError* theError = nil;
    NSData* bookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationSuitableForBookmarkFile
                                            includingResourceValuesForKeys:nil
                                            relativeToURL:nil
                                            error:&theError];
    if (theError || (bookmark == nil)) {
        // Handle any errors.
        return nil;
    }
    return bookmark;
}

If you write the persistent bookmark data to disk using thewriteBookmarkData:toURL:options:error: method of NSURL, what the system creates on disk is an alias file. Aliases are similar to symbolic links but are implemented differently. Normally, users create aliases from the Finder when they want to create links to files elsewhere on the system.

若是你使用NSURL的writeBookmarkData:toURL:options:error: 方法來向磁盤寫入持久書籤,系統在磁盤上建立的是一個別名文件。 別名跟符號連接很類似,可是實現不同。 正常狀況下,當用戶想在系統的別處建立連接時,它們從Finder建立別名。

To transform a bookmark data object back into a URL, use theURLByResolvingBookmarkData:options:relativeToURL:bookmarkDataIsStale:error:method of NSURLListing 2-3 shows the process for converting a bookmark back into a URL.

要想把書籤數據對象轉爲一個URL,使用NSURL的

URLByResolvingBookmarkData:options:relativeToURL:bookmarkDataIsStale:error: 方法。列表 2-3 顯示了轉換一個書籤到一個URL的過程。

Listing 2-3  Returning a persistent bookmark to its URL form

- (NSURL*)urlForBookmark:(NSData*)bookmark {
    BOOL bookmarkIsStale = NO;
    NSError* theError = nil;
    NSURL* bookmarkURL = [NSURL URLByResolvingBookmarkData:bookmark
                                            options:NSURLBookmarkResolutionWithoutUI
                                            relativeToURL:nil
                                            bookmarkDataIsStale:&bookmarkIsStale
                                            error:&theError];
 
    if (bookmarkIsStale || (theError != nil)) {
        // Handle any errors
        return nil;
    }
    return bookmarkURL;
}

The Core Foundation framework provides a set of C-based functions that parallel the bookmark interface provided by NSURL. For more information about using those functions, see CFURL Reference.

Core Foundation 框架提供了一組基於C的函數,它們跟NSURL提供的書籤接口並行。關於使用那些函數的更多信息,請看CFURL Reference.

Enumerating the Contents of a Directory

4、遍歷一個目錄的內容

When you want to discover what files are in a given directory, you can enumeratethe contents of that directory. Cocoa supports enumerating a directory one file at a time or all at once. Regardless of which option you choose, you should enumerate directories sparingly because doing so can involve touching many files in the file system, which is expensive.

當你想發如今一個給定目錄中存在什麼文件時,你能夠遍歷該目錄的內容。Cocoa 支持一次遍歷一個文件,也支持一次遍歷全部文件。 無論你選擇哪一個,你都應該節制地遍歷目錄,由於遍歷目錄是一個昂貴的過程,它能涉及接觸文件系統中不少的文件。

Enumerating a Directory One File at a Time

一、在目錄中一次遍歷一個文件

Enumerating a directory one file at a time is recommended when you want to search for a specific file and stop enumerating when you find it. File-by-file enumerationuses the NSDirectoryEnumerator class, which defines the methods for retrieving items. Because NSDirectoryEnumerator itself is an abstract class, however, you do not create instances of it directly. Instead, you use either theenumeratorAtURL:includingPropertiesForKeys:options:errorHandler: orenumeratorAtPath: method of NSFileManager object to obtain a concrete instance of the class for use in your enumeration.

當你想查找一個特定文件時建議一次遍歷一個文件,而後在找到後中止遍歷。 文件接着文件遍歷使用
NSDirectoryEnumerator 類,該類定義了取回數據項的各類方法。 由於NSDirectoryEnumerator 自己是一個抽象類, 全部你不能直接建立它的實例。 可是你可使用
NSFileManager 對象的
enumeratorAtURL:includingPropertiesForKeys:options:errorHandler: 或
enumeratorAtPath: 方法來獲取類的實例,以用於枚舉。

Enumerator objects return the paths of all files and directories contained within the enumerated directory. Enumerations are recursive and cross device boundaries, so the number of files and directories returned may be more than what is present in the starting directory. You can skip the contents of any directory you are not interested in, by calling the enumerator object’s skipDescendents method. Enumerator objects do not resolve symbolic links or attempt to traverse symbolic links that point to directories.

枚舉對象返回包含在被枚舉目錄中的全部文件和目錄的路徑。枚舉是遞歸併跨越設備邊界的,所以返回的文件和目錄的數量可能多於在開始目錄看到的數目。你能夠跳過任何你不感興趣的目錄內容,經過調用枚舉對象的skipDescendents 方法。 枚舉對象並不解析符號連接或嘗試遍歷指向目錄的符號連接。

Listing 2-4 shows how to use theenumeratorAtURL:includingPropertiesForKeys:options:errorHandler:method to list all the user-visible subdirectories of a given directory, noting whether they are directories or file packages. The keys array tells the enumerator object to prefetch and cache information for each item. Prefetching this information improves efficiency by touching the disk only once. The options argument specifies that the enumeration should not list the contents of file packages and hidden files. The error handler is a block object that returns a Boolean value. If it returns YES, the enumeration continues after the error; if it returns NO, the enumeration stops.

列表2-4 顯示瞭如何使用
 enumeratorAtURL:includingPropertiesForKeys:options:errorHandler: 方法來列出一個給定目錄的全部用戶可見子目錄,注意它們是目錄或是文件包。 keys 數組告訴枚舉對象預取或緩存每一個對象的信息。預取該信息經過一次性接觸磁盤提升了效率。 options 參數指定枚舉不該該列出文件包和隱藏文件的內容。 errorHandler是一個塊對象,它返回一個布爾值。 若是它返回YES, 枚舉在錯誤發生以後繼續進行;若是返回NO,枚舉就在錯誤發生時中止。

Listing 2-4  Enumerating the contents of a directory

列表 2-4 遍歷一個目錄的內容

NSURL *directoryURL = <#An NSURL object that contains a reference to a directory#>;
 
NSArray *keys = [NSArray arrayWithObjects:
    NSURLIsDirectoryKey, NSURLIsPackageKey, NSURLLocalizedNameKey, nil];
 
NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager]
                                     enumeratorAtURL:directoryURL
                                     includingPropertiesForKeys:keys
                                     options:(NSDirectoryEnumerationSkipsPackageDescendants |
                                              NSDirectoryEnumerationSkipsHiddenFiles)
                                     errorHandler:^(NSURL *url, NSError *error) {
                                         // Handle the error.
                                         // Return YES if the enumeration should continue after the error.
                                         return <#YES or NO#>;
                                     }];
 
for (NSURL *url in enumerator) {
 
    // Error-checking is omitted for clarity.
 
    NSNumber *isDirectory = nil;
    [url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:NULL];
 
    if ([isDirectory boolValue]) {
 
        NSString *localizedName = nil;
        [url getResourceValue:&localizedName forKey:NSURLLocalizedNameKey error:NULL];
 
        NSNumber *isPackage = nil;
        [url getResourceValue:&isPackage forKey:NSURLIsPackageKey error:NULL];
 
        if ([isPackage boolValue]) {
            NSLog(@"Package at %@", localizedName);
        }
        else {
            NSLog(@"Directory at %@", localizedName);
        }
    }
}

You can use other methods declared by NSDirectoryEnumerator to determine attributes of files during the enumeration—both of the parent directory and the current file or directory—and to control recursion into subdirectories. The code inListing 2-5 enumerates the contents of a directory and lists files that have been modified within the last 24 hours; if, however, it comes across RTFD file packages, it skips recursion into them.

你可使用由NSDirectoryEnumerotor 聲明的其它方法來決定遍歷時的文件屬性--父目錄和當前目錄--並控制子目錄的遞歸。 列表 2-5的代碼遍歷了一個目錄的內容並列出了在過去24小時被修改過的文件,可是若是它遍歷到RTFD文件包時,不遍歷它們。

Listing 2-5  Looking for files that have been modified recently

NSString *directoryPath = <#Get a path to a directory#>;
NSDirectoryEnumerator *directoryEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:directoryPath];
 
NSDate *yesterday = [NSDate dateWithTimeIntervalSinceNow:(-60*60*24)];
 
for (NSString *path in directoryEnumerator) {
 
    if ([[path pathExtension] isEqualToString:@"rtfd"]) {
        // Don't enumerate this directory.
        [directoryEnumerator skipDescendents];
    }
    else {
 
        NSDictionary *attributes = [directoryEnumerator fileAttributes];
        NSDate *lastModificationDate = [attributes objectForKey:NSFileModificationDate];
 
        if ([yesterday earlierDate:lastModificationDate] == yesterday) {
            NSLog(@"%@ was modified within the last 24 hours", path);
        }
    }
}

Getting the Contents of a Directory in a Single Batch Operation

二、在一個單批量操做中獲取目錄的內容

If you know that you want to look at every item in a directory, you can retrieve a snapshot of the items and iterate over them at your convenience. Retrieving the contents of a directory in a batch operation is not the most efficient way to enumerate a directory, because the file manager must walk the entire directory contents every time. However, if you plan to look at all the items anyway, it is a much simpler way to retrieve the items.

若是你想要一個目錄中的全部數據項,你能夠獲取這些數據項的一個快照並根據須要遍歷它們。 在一個單批量操做裏獲取一個目錄的內容不是遍歷目錄最有效的方法, 由於文件管理器必須每次都遍歷整個目錄。 然而,若是你計劃不管如何都要查看全部數據項,這是獲取全部數據項的一個更簡單方法。

There are two options for doing a batch enumeration of a directory usingNSFileManager:

使用NSFileManager 能夠有2種方法來完成對一個目錄的批量遍歷:

Listing 2-6 shows an example that uses thecontentsOfDirectoryAtURL:includingPropertiesForKeys:options:error:method to enumerate the contents of a directory. One of the benefits of using URLs is that you can also efficiently retrieve additional information about each item. This example retrieves the localized name, creation date, and type information for each item in the directory and stores that information in the corresponding NSURL object. When the method returns, you can proceed to iterate over the items in the arrayvariable and do what you need with them.

列表2-6 顯示了一個例子,該例子使用
contentsOfDirectoryAtURL:includingPropertiesForKeys:options:error: 方法來遍歷一個目錄的內容。 使用URLs的好處之一是你還能夠有效的獲取每一個數據項的額外信息。 該例子獲取了本地化名稱,建立日期,以及在子目錄中每一個數據項的類型信息並把那信息存儲在NSURL 對象中。當方法返回後,你能夠繼續遍歷array變量中的數據項並作你想對它們作的事情。

Listing 2-6  Retrieving the list of items in a directory all at once

列表 2-6 一次性獲取一個目錄中的所有數據項

NSURL *url = <#A URL for a directory#>;
NSError *error = nil;
NSArray *properties = [NSArray arrayWithObjects: NSURLLocalizedNameKey,
                          NSURLCreationDateKey, NSURLLocalizedTypeDescriptionKey, nil];
 
NSArray *array = [[NSFileManager defaultManager]
                   contentsOfDirectoryAtURL:url
                   includingPropertiesForKeys:properties
                   options:(NSDirectoryEnumerationSkipsHiddenFiles)
                   error:&error];
if (array == nil) {
    // Handle the error
}

Determining Where to Store Your App-Specific Files

5、決定在何處存儲您的應用程序特定的文件

The Library directory is the designated repository for files your app creates and manages on behalf of the user. You should consider that these directories may be in different locations if your app is sandboxed. As a result, you should always use theNSFileManager method URLsForDirectory:inDomains: with theNSUserDomainMask as the domain to locate the specific directory that you should use to store this data.

Library 目錄是指定給你的應用程序建立的文件以及管理用戶行爲文件的庫。若是應用程序是沙盒化的,你應該考慮這些目錄可能在不一樣的位置。結果是你應該老是使用NSFileManager對象的 URLsForDirectory:inDomains: 方法,並使用NSUserDomainMask 做爲域來定位你應該用來存儲數據的特定目錄。

  • Use the Application Support directory constantNSApplicationSupportDirectory, appending your <bundle_ID> for:

     使用 Application Support 目錄常量NSApplicationSupportDirectory,爲如下內容添加你的<bundle_ID>:

    • Resource and data files that your app creates and manages for the user. You might use this directory to store app state information, computed or downloaded data, or even user created data that you manage on behalf of the user.

       你的應用程序建立的資源和數據文件,以及對用戶的管理。你可使用該目錄來存儲應用程序狀態信息,計算後或下載的數據,或甚至是你用來管理用戶行爲的用戶建立的數據。

    • Autosave files.

       自動保存的文件。

  • Use the Caches directory constant NSCachesDirectory, appending your<bundle_ID> directory for cached data files or any files that your app can re-create easily.

     用Caches 目錄常量NSCachesDirectory,添加你的<bundle_ID>目錄用來緩存數據文件或任何你的應用程序能夠很簡單從新建立的文件。

  • Read and write preferences using the NSUserDefaults class. This class automatically writes preferences to the appropriate location.

     讀取和寫入偏好設置可使用NSUserDefaults 類。該類自動把偏好設置寫入合適的位置。

  • Use the NSFileManager method URLsForDirectory:inDomains: method to get the directory for storing temporary files. Temporary files are files you intend to use immediately for some ongoing operation but then plan to discard later. You should delete temporary files as soon as you are done with them. If you do not delete temporary files after three days, the system may delete them for you whether you are using them or not.

     使用NSFileManager對象的URLsForDirectory:inDomains: 方法來獲取存儲臨時文件的目錄。 臨時文件是你打算當即使用於一些正在進行的操做,可是計劃在稍後丟棄的文件。你應該在結束對臨時文件以後儘量快的刪除它們。 若是你三天後還不刪除臨時文件,無論你是否正在使用它們,系統均可能替你刪除它們。

Note: The files that you store in these locations are rarely shared amongst apps. As such, they do not require the overhead of using creating and configuring an NSFileCoordination instance.

注意:你存儲在這些位置的文件都不多在應用程序之間共享。所以,它們不須要使用建立和配置一個NSFileCoordination實例的開銷。

Tips for Accessing Files and Directories

6、訪問文件和目錄的提示

Because the file system is a resource shared by all processes, including system processes, you should always use it carefully. Even on systems with solid-state disk drives, file operations tend to be a little slower because of the latency involved in retrieving data from the disk. And when you do access files, it is important that you do so in a way that is secure and does not interfere with other processes.

由於文件系統是由多個進程共享的資源,它包含系統進程,你應該老是當心的使用它。即便是在帶有固態磁盤驅動器的系統中,也會由於從磁盤檢索數據而讓文件操做變得有點慢。而且當你訪問文件時,以安全而且不干擾其它進程的方式運行是很重要的。

Perform File Operations Lazily

一、懶惰地執行文件操做

Operations involving the file system should be performed only when they are absolutely necessary. Accessing the file system usually takes a lot of time relative to other computer-wide tasks. So make sure you really need to access the disk before you do. Specifically:

設計文件系統的操做應該只在它們確實有必要時執行。 訪問文件系統跟別的計算機範圍的任務比經常花費不少時間。因此在此以前,請確保你真的須要訪問磁盤。特別是:

  • Write data to disk only when you have something valuable to save. The definition of what is valuable is different for each app but should generally involve information that the user provides explicitly. For example, if your app creates some default data structures at launch time, you should not save those structures to disk unless the user changes them.

     只在須要存儲有價值數據時才把數據寫入磁盤。對於不一樣應用程序來講,什麼東西有價值的定義是不同的,可是基本上是用戶明確提供的信息。好比,若是你的應用程序在啓動時建立了一些默認數據結構,你不該該保存那些結構到磁盤除非用戶對那些結構作了改變。

  • Read data from disk only when you need it. In other words, load data that you need for your user interface now but do not load an entire file just to get one small piece of data from that file. For custom file formats, use file mapping or read only the few chunks of a file that you need to present your user interface. Read any remaining chunks as needed in response to user interactions with the data. For structured data formats, use Core Data to manage and optimize the reading of data for you.

     只在你須要的時候才從磁盤讀取數據。 換句話說,加載目前你的用戶界面須要的數據,可是若是你只想從那文件獲取一小部分數據,則不用加載整個文件。 對於自定義文件格式,使用文件映射或只從文件讀取你須要呈如今用戶界面的幾塊內容。當響應用戶跟數據交互時,根據須要讀取任何保留的塊。 對於結構數據模式,使用Core Data 來管理並優化讀取的數據。

Use Secure Coding Practices

二、使用安全編碼實踐

There are several principles you can follow to help ensure that you do not have file-based security vulnerabilities in your program:

你能夠遵循幾個原則來幫助肯定你的應用程序中沒有基於文件的安全漏洞:

  • Check the result codes for any functions or methods you call. Result codes are there to let you know that there is a problem, so you should pay attention to them. For example, if you try to delete a directory that you think is empty and get an error code, you might find that the directory is not empty after all.

    檢查你調用的任何函數或方法的結構代碼。 結果代碼在那就是讓你知道那裏有問題,因此你應該關注它們。 好比,若是你試着刪除一個你覺得的空目錄,並獲得一個錯誤代碼,你可能會發現那個目錄根本不是空的。

  • When working in a directory to which your process does not have exclusive access, you must check to make sure a file does not exist before you create it. You must also verify that the file you intend to read from or write to is the same file you created.

     當你的進程工做在一個目錄,而你對該進程沒有獨佔訪問權時,你必須在建立一個文件時,檢查肯定該是否存在。你還必須認證你打算讀取或寫入的文件正是你建立的那個。

  • Whenever possible use routines that operate on file descriptors rather than pathnames. In this way you can be sure you’re always dealing with the same file.

     儘量的在程序中操做文件描述符代替路徑名。這樣你就能肯定你老是在處理同一個文件。

  • Intentionally create files as a separate step from opening them so that you can verify that you are opening a file you created rather than one that already existed

     有意地把建立文件和打開文件分離,把它們做爲單獨的步驟。這樣你就能夠認證你打開的文件是你剛建立的,而不是已經存在的文件。

  • Know whether the function you are calling follows symbolic links. For example, the lstat function gives you the status of a file regardless of whether it’s a normal file or a symbolic link, but the stat function follows symbolic links and, if the specified file was a symbolic link, returns the status of the linked-to file. Therefore, if you use the stat function, you might be accessing a different file than you expected.

    瞭解你正在調用的函數是否跟蹤(follows)符號連接。 好比,lstat函數獲取一個文件的狀態,無論該文件是一個正常文件仍是一個符號連接, 可是stat函數追蹤(follows)符號連接,若是指定的文件是一個符號連接,則返回它連接目標文件的狀態。 

  • Before you read a file, make sure that file has the owner and permissions you expect. Be prepared to fail gracefully (rather than hanging) if it does not.

     在你讀取一個文件以前,確保該文件有你預期的擁有者和權限。 若是它沒有則準備好優雅地失敗(而不是掛起)。

  • Set your process’ file code creation mask (umask) to restrict access to files created by your process. The umask is a bitmask that alters the default permissions of a new file. You do not reset the umask for your app, your process inherits the umask from its parent process. For more information about setting the umask, see umask(2) OS X Developer Tools Manual Page.

     設置進程文件代碼的建立掩碼(umask)來限制訪問你的進程建立的文件。umask使一個位掩碼用來改變一個新文件的默認權限。 你不用重置應用程序的umask,你的進程會從它的父進程繼承該umask。
    關於設置umask的更多信息,請看umask(2) OS X Developer Tools Manual Page.

For additional tips and coding practices, see 「Race Conditions and Secure File Operations」 in Secure Coding Guide.

關於額外提示和代碼實踐,請看Secure Coding Guide 中的「Race Conditions and Secure File Operations」

Assume Case Sensitivity for Paths

三、假設路徑的大小寫敏感

When searching or comparing filenames, always assume that the underlying file system is case sensitive. OS X supports many file systems that use case to differentiate between files. Even on file systems (such as HFS+) that support case insensitivity, there are still times when case may be used to compare filenames. For example, the NSBundle class and CFBundle APIs consider case when searchingbundle directories for named resources.

當查找或比較文件名時,老是應該假設底層文件系統是大小寫敏感的。OS X支持多個文件文件系統,它們都使用大小寫來區分文件。即便是那些支持大小寫不敏感的文件系統(好比 HFS+),仍是可能有不少時候須要用大小寫來比較文件名。好比,NSBundle 類 和CFBundle APIs 在爲已經命名的資源搜索束目錄時考慮大小寫。

Include Filename Extensions for New Files

四、新文件應該包含文件擴展名

All files should have a filename extension that reflects the type of content contained in the file. Filename extensions help the system determine how to open files and also make it easier to exchange files with other computers or other platforms. For example, network transfer programs often use filename extensions to determine the best way to to transfer files between computers.

全部的文件都應該有一個文件擴展名,用來反映文件中內容的類型。文件擴展名幫助系統決定該如何打開文件,並讓文件跟其它電腦或其它平臺的交換變得更加容易。 好比,網絡傳輸程序經常使用文件擴展名來決定跟電腦之間傳輸的最佳方式。

Use Display Names When Presenting Items

五、當顯示數據項時使用顯示名

Whenever you need to present the name of a file or directory in your user interface, always use the item’s display name. Using the display name ensures that what your app presents matches what the Finder and other apps are presenting. For example, if your app shows the path to a file, using the display name ensures that the path is localized in accordance with the current user’s language settings.

每當你須要在用戶界面上呈現文件或目錄的名字時,老是使用數據項的顯示名。 使用顯示名確保應用程序中呈現的內容跟Finder 以及其它應用程序呈現的內容相匹配。 好比,若是應用程序顯示了一個文件的路徑,使用顯示名確保了該路徑根據當前用戶的語言設置作了本地化定位。

For more information about display names, see 「Files and Directories Can Have Alternate Names.」

關於顯示名的更多信息,請看「Files and Directories Can Have Alternate Names.」

Accessing Files Safely From Background Threads

六、從後臺線程安全地訪問文件

In general, the objects and functions used to access and manipulate files can be used from any thread of your app. However, as with all threaded programming, make sure you manipulate files safely from your background threads:

基本上,用來訪問和操做文件的對象和函數能在應用程序的任何線程中使用。然而,就像全部線程編程同樣,請確保你從後臺線程安全地操做文件。

  • Avoid using the same file descriptor from multiple threads. Doing so could cause each thread to disrupt the state of the other.

     避免從多個線程使用一樣的文件描述符。這樣作將致使一個線程打斷另外一個線程的狀態。

  • Always use file coordinators to access files. File coordinators provide notifications when other threads in your program (or other processes) access the same file. You can use these notifications to clean up your local data structures or perform other relevant tasks.

    老是使用文件協調員(file coordinators)來訪問文件。 當程序(或其它進程)中其它線程訪問同一個文件時,文件協調員會提供通知。 你可使用這些通知來清除你的本地數據結構或執行其它相關任務。

  • Create a unique instance of NSFileManager for each thread when copying, moving, linking, or deleting files. Although most file manager operations are thread-safe, operations involving a delegate require a unique instance ofNSFileManager, especially if you are performing those operations on background threads.

    當拷貝,移動,連接,或刪除文件時,爲每一個線程建立一個惟一實例。 儘管大多數文件管理器(file manager)操做都是線程安全的,可是涉及一個委託的操做要求一個NSFileManager 對象的惟一實例,特別是若是你在後臺線程執行那些操做時。

相關文章
相關標籤/搜索