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.
在你可以打开一个文件之前,你首先需要在文件系统里找到它。系统框架提供了很多例程来获得对众所周知目录的引用,比如Library 目录以及它的内容。 你还可以通过从已知的目录名建立一个URL或基于字符串的路径手动指定位置。
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
一、选择正确的方法来访问文件
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 列出了系统支持的常用文件类型以及如何访问它们的相关信息。
File Type |
Examples |
Description |
---|---|---|
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 an 文本文件是一个非结构化的ASCII码或Unicode字符序列。你通常把一个文本文件的内容载入一个NSString 对象,但是也可能以原始字节流的方式读取或写入一个文本文件。 For information about using the 关于使用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 文件包是一个目录,它包含了任意数量的自定义数据文件,但是它呈现给用户时是作为一个单一文件显示。应用程序可以使用文件包来实现复杂的文件格式,它包含多个不同的文件,或多个不同类型文件的混合。 比如,如果你的文件格式包含一个二进制数据文件以及一个或多个图像,视频或音频文件,你或许会使用一个文件包来实现。 你可以使用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
二、指定文件或目录的路径
The preferred way to specify the location of a file or directory is to use the NSURL
class. 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
三、在文件系统里定位数据项
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
1、让用户定位一个数据项
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
2、在应用程序束里定位数据项
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
3、在标准目录里定位数据项
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 theNSFileManager
class returns a directory’s location packaged in anNSURL
object. The directory to search for is anNSSearchPathDirectory
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 theURLsForDirectory: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。
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
4、用书签定位文件
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 NSData
object 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 显示了一个简单的例子用来表现使用该方法来创建一个书签数据对象。
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 NSURL
. Listing 2-3 shows the process for converting a bookmark back into a URL.
要想把书签数据对象转为一个URL,使用NSURL的
URLByResolvingBookmarkData:options:relativeToURL:bookmarkDataIsStale:error: 方法。列表 2-3 显示了转换一个书签到一个URL的过程。
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
四、遍历一个目录的内容
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
1、在目录中一次遍历一个文件
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,枚举就在错误发生时停止。
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文件包时,不遍历它们。
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
2、在一个单批量操作中获取目录的内容
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种方法来完成对一个目录的批量遍历:
-
To perform a shallow enumeration, use the
contentsOfDirectoryAtURL:includingPropertiesForKeys:options:error:
orcontentsOfDirectoryAtPath:error:
method.用contentsOfDirectoryAtURL:includingPropertiesForKeys:options:error:
或contentsOfDirectoryAtPath:error:方法来执行一个浅遍历。 -
To perform a deep enumeration and return only subdirectories, use the
subpathsOfDirectoryAtPath:error:
method.用
subpathsOfDirectoryAtPath:error:方法来执行一个深遍历并只返回子目录。
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 array
variable and do what you need with them.
列表2-6 显示了一个例子,该例子使用
contentsOfDirectoryAtURL:includingPropertiesForKeys:options:error: 方法来遍历一个目录的内容。 使用URLs的好处之一是你还可以有效的获取每个数据项的额外信息。 该例子获取了本地化名称,创建日期,以及在子目录中每个数据项的类型信息并把那信息存储在NSURL 对象中。当方法返回后,你可以继续遍历array变量中的数据项并做你想对它们做的事情。
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
五、决定在何处存储您的应用程序特定的文件
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 constantNSCachesDirectory
, 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
methodURLsForDirectory: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
六、访问文件和目录的提示
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
1、懒惰地执行文件操作
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
2、使用安全编码实践
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 thestat
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 thestat
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
3、假设路径的大小写敏感
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
4、新文件应该包含文件扩展名
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
5、当显示数据项时使用显示名
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
6、从后台线程安全地访问文件
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 对象的唯一实例,特别是如果你在后台线程执行那些操作时。