Skip to content

Latest commit

 

History

History
1873 lines (1848 loc) · 61.5 KB

FileManager Class Tutorial for macOS Getting Started with the File System.md

File metadata and controls

1873 lines (1848 loc) · 61.5 KB

macOS FileManager类教程:文件系统入门

File Manager Case Tutorial for macOS: Getting Started with the File System

macOS文件系统

macOS 中文件系统是每个app的基础 - 在 FileManager 类有很多来处理这个。你会在 Applications 目录下储存app,在 Documents 目录下储存文档,在 Library 目录下储存偏好和支持文件。

随着文件和数据在文件系统中传播,你的app该如何查找文件和目录,使用文件和目录的路径,甚至读取及写入数据到一个文件中?

那就要靠 FileManager 类了!

在这个教程中,你会学到如何管理目录路径,使用URL,使用通用的文件和目录对话框,展示文件和目录信息等等!

入门

在本教程中,你会从playground开始,然后在掌握基础之后,移步到app中。

macOS 使用了一个分层的文件系统:目录中的文件和目录,目录内部。这意味着找到一个指定的文件是非常复杂的。每个文件都有它自己的地址,定义地址的结构被称为 URL

打开 Xcode 并在 Welcome to Xcode 窗口中单击 Get started with a playground ,或依次选择 File/New/Playground… 将playground的名称设为 Files ,确保平台被设置为 macOS 并单击 Next

选择你的 Desktop 并单击 Create 来保存playground。

New Playground

注意: 在这个教程中,playground的名称必须为 Files ,并且它必须位于你的 Desktop 目录下。如果你给它起了一个其它的名字,或把它保存到了其它地方,请在开始后续教程开始前纠正,否则下面的代码就会无法正常工作!

打开开始的playground后,删除除 import Cocoa 外全部行的代码。

添加下面的代码到你的playground中,但不要担心现在改变了用户名:

let completePath =/Users/sarah/Desktop/Files.playground”

completePath 现在包含了这个playground文件的地址或路径。由于 now contains the address, or path, of this playground file. Since macOS 是基于Unix系统的,而 this is how Unix (以及它的所有变体)就是这么描述文件路径的。第一条斜杠表示根目录,在这个case中是你的启动磁盘。之后,每个斜杠都界定除了一个新的目录或文件。因此, (and all its variants) describe file paths. The first slash indicates the root directory, which in this case is your startup disk. After that, every slash delimits a new folder or file. So Files.playground 文件就位于启动驱动器的 Users 目录下的 sarah 目录下的 Desktop 目录中。

尽管这个字符串描述了这个文件的全路径,但它并不是处理地址的最佳方式。相代替的,你会通过添加下列代码,将地址替换为一个 URL

let completeUrl = URL(fileURLWithPath: completePath)

现在,在playground的结果面板中,你会看到: file:///Users/sarah/Desktop/Files.playground

Create a URL

“稍等!”你喊道。“我以为 URL 是一个像 https://www.raywenderlich.com 这样的网址,而不是目录的路径!”

嗯,是的是的!

URL 代表了 Uniform Resource Locator - 它也可以指向本地的文件和目录。并非 https:// ,而是以 file:// 开头来表示本地文件。在结果面板中,它看起来有3个斜杠,但这是因为这个路径本身就是以斜杠开头的。

FileManager类

你已经使用了一个 字符串 来指定一个文件路径,并将它转换为一个 URL 。但是,虽然它是一个有效的 URL ,但却无法工作 - 除非你的用户名也恰好就是 sarah 。因此,下面的一步就是来创建一个可以在任何人的电脑上work的 URL 了。

要做到这个,你就要使用 FileManager 类,它提供了在macOS中大多数的处理文件相关行为的方法。

第一个任务就是识别你的 Home 目录,并使用你自己的用户名来替换 sarah

添加下列的代码到你的playground中:

let home = FileManager.default.homeDirectoryForCurrentUser

default 返回了 FileManager 类的单例实例,而 homeDirectoryForCurrentUser 包含了当前用户的home目录的 URL

现在你已经有了指向你的home目录的 URL ,你就可以通过添加下列的代码来获取指向playground的路径:

let playgroundPath = "Desktop/Files.playground"
let playgroundUrl = home.appendingPathComponent(playgroundPath)

现在结果面板就会展示在你的家目录下的 URL 了。

添加下列的代码到playground中,来查询各种 URL 的property:

playgroundUrl.path
playgroundUrl.absoluteString
playgroundUrl.absoluteURL
playgroundUrl.baseURL
playgroundUrl.pathComponents
playgroundUrl.lastPathComponent
playgroundUrl.pathExtension
playgroundUrl.isFileURL
playgroundUrl.hasDirectoryPath

pathComponents 这个property非常有趣,它会将所有的目录和文件拆成一个数组。而 lastPathComponent pathExtension property在实践中都相当地有用。

下面是你应该在你的playground中有的:

URL Components

注意: property hasDirectoryPath 的值被设为了 true 。这标记了 URL 是一个目录。但为何playground文件是被标记为一个目录?

这是因为 .playground 文件是“目录bundle”,就像 .app 文件一样。右击playground文件,并选择 Show Package Contents 就可以查看它的内部了。

URL 类使得编辑 URLs 非常得容易。

添加下列的代码到你的playground中:

var urlForEditing = home
urlForEditing.path
urlForEditing.appendPathComponent("Desktop")
urlForEditing.path
urlForEditing.appendPathComponent("Test file")
urlForEditing.path
urlForEditing.appendPathExtension("txt")
urlForEditing.path
urlForEditing.deletePathExtension()
urlForEditing.path
urlForEditing.deleteLastPathComponent()
urlForEditing.path

注意,每次你都会展示 path property,因此很容易就会看到改变了什么。

尽管这些命令恰当地编辑 URL ,你也可以从已存在的创建一个新的 URL

为了了解如何操作,添加下列的命令到你的playground:

let fileUrl = home
.appendingPathComponent("Desktop")
.appendingPathComponent("Test file")
.appendingPathExtension("txt")
fileUrl.path
let desktopUrl = fileUrl.deletingLastPathComponent()
desktopUrl.path

这些方法会返回新的 URLs ,因此将它们连接到一个序列中效果会更好。

这三个 appending 方法实际上可以缩到一个方法中,但我在这里将它们拆分成了独立的步骤,以便清晰地展示给你。

你的playground现在应当看起来像下面的样子:

Append Path to a URL

查看文件和目录

NSString 中有很多处理文件路径的方法,但在Swift的结构体 String 中则不是。相反地,随着苹果向着 Apple File System (APFS) 的转变,你应当使用 URLs 来处理文件路径。在这种方式下处理将会变得更重要,因为。

然而,在下面这个情形下,你仍然需要一个字符串来代表文件 URL :检查是否这个文件或目录存在。获取一个 URL 的字符串版本的最近方式是通过 path property。

添加下列的代码到你的playground中:

let fileManager = FileManager.default
fileManager.fileExists(atPath: playgroundUrl.path)
let missingFile = URL(fileURLWithPath: "this_file_does_not_exist.missing")
fileManager.fileExists(atPath: missingFile.path)

检查一个目录是否存在稍微有一点难懂,因为你必须这个 URL 既是一个有效的资源,又是一个目录。

这就要求我使用一个非常非Swift的机制 - OC版本的inout的Bool值。添加下列的代码:

var isDirectory: ObjCBool = false
fileManager.fileExists(atPath: playgroundUrl.path, isDirectory: &isDirectory)
isDirectory.boolValue

现在,你的playground看起来应当是下面的样子:

FileManager Class Check For File Exists

一个充满注释版本的 playground 可以到这里下载。

既然你已理解了如何使用 URL 来区别文件和目录,关闭playground。是时候来构建app了!

文件间谍

在教程的这一部分,你将要构建 文件间谍 app,你可以用它选择一个目录来查看其内部的每个文件和目录。选择其中任一项目,就可以看到更多的详情。

FileManager Class File Information

下载 起始的app项目 ,在 Xcode 中打开它并在toolbar单击 Play 按钮,或按 Command+R键 运行项目。它的UI已完成,但你需要添加文件管理位。

你第一个任务就是让用户选择一个目录,然后展示它的内容。你会在 Select Folder 按钮上添加一些代码,并使用 NSOpenPanel 类来选择一个目录。

ViewController.swift 中的 Actions 区,找到 selectFolderClicked 并插入下面的代码:

// 1
guard let window = view.window else { return }
// 2
let panel = NSOpenPanel()
panel.canChooseFiles = false
panel.canChooseDirectories = true
panel.allowsMultipleSelection = false
// 3
panel.beginSheetModal(for: window) { (result) in
if result == NSFileHandlingPanelOKButton {
// 4
self.selectedFolder = panel.urls[0]
print(self.selectedFolder)
}
}

上面的代码完成了:

  1. 检查你可否获取window的引用,因为它是 NSOpenPanel 将要展示的地方。
  2. 创建一个新的 NSOpenPanel ,并设置一些property,使其值运行单选,且只能选择目录。
  3. 模态地在window中展示 NSOpenPanel 并使用一个闭包来等待结果。
  4. 如果result表明用户点击了 OK 按钮(实际看到的按钮上,将基于你的本地化带有不同的标签),获取被选择的 URL 并设置指定的 ViewController property。为了临时快速地测试,你会把选择的 URL 输入到控制台。现在忽略这行代码上的警告。

运行项目,单击 Select Folder 按钮并选择一个目录。确认选择的目录的 URL 已打印到控制台上。

再次单击按钮来打开对话框,但这次单击 Cancel 按钮。这时就不会打印 URL

Selecting a Folder

退出app并删除临时的 print 语句。

文件内容

既然你能够选择目录,那接下来你的工作就是找出这个目录的内容并展示出来。

上一部分的代码填充了一个名为 selectedFolder 的property。滚动到 ViewController 定义的顶部,并查看 selectedFolder 的property。它使用了一个 didSet 的property观察者,它会在设置值的时候运行代码。

这里的关键代码是调用 contentsOf(folder:) 。滚动到这个方法这里,它当前返回了一个空的数组。用下面的代码来替换其中的内容:

func contentsOf(folder: URL) -> [URL] {
// 1
let fileManager = FileManager.default
// 2
do {
// 3
let contents = try fileManager.contentsOfDirectory(atPath: folder.path)
<span class="hljs-comment">// 4</span>
<span class="hljs-keyword">let</span> urls = contents.<span class="hljs-built_in">map</span> { <span class="hljs-keyword">return</span> folder.appendingPathComponent($<span class="hljs-number">0</span>) }
<span class="hljs-keyword">return</span> urls

} catch {
// 5
return []
}
}

一步步来看代码:

  1. 和之前一样,获取 FileManager 类的单例。
  2. 由于 FileManager 的方法可能抛出错误,因此你要使用 do...catch 代码块。
  3. 尝试找到目录 contentsOfDirectory(atPath:) 的内容,并返回内部文件和目录名称的数组。
  4. 使用 map 处理返回的数组,并将每个名称,用其父目录转换成一个完整的 URL 然后返回数组。
  5. 如果 contentsOfDirectory(atPath:) 抛出错误的话,返回一个空的数组。

selectedFolder property将 filesList property设置为被选择的目录的内容,但由于你使用了一个table view来展示内容,你就需要定义如何展示每个项目。

向下拖动到 NSTableViewDataSource 的extension。注意 numberOfRows 早已返回了 filesList 数组中 URLs 的数量。现在滚动到
NSTableViewDelegate ,并注意到 tableView(_:viewFor:row:) 返回的是 nil 。你需要在table中出现任何事之前改变这点。

使用下面的代码来替换这个方法:

func tableView(_ tableView: NSTableView, viewFor
tableColumn: NSTableColumn?, row: Int) -> NSView? {
// 1
let item = filesList[row]
// 2
let fileIcon = NSWorkspace.shared().icon(forFile: item.path)
// 3
if let cell = tableView.make(withIdentifier: "FileCell", owner: nil)
as? NSTableCellView {
// 4
cell.textField?.stringValue = item.lastPathComponent
cell.imageView?.image = fileIcon
return cell
}
// 5
return nil
}

你在代码中做的事有:

  1. 获取匹配行序号的 URL
  2. 获取这个 URL 的icon。 NSWorkspace 是另一个非常有用的单例;这个方法对任何 URL 都返回的是Finder的icon。
  3. 获取这个table中对于这个cell的引用。 FileCell 这个标识符是在 Storyboard 中被设置的。
  4. 如果cell存在,就设置它的text field来展示文件名,设置它的image view来展示文件的icon。
  5. 如果没有cell存在,返回 nil

运行项目,选择一个目录,你应当看到一个文件和目录的列表出现了 - 欢呼吧!

Show Folder Contents

但点击一个文件或目录现在还没有给出有用的信息,因此继续下一步。

获取文件信息

打开Finder并按 Command+I键 来打开一个关于文件信息的窗口:创建日期,修改日期,尺寸,权限等等。全部的这些信息,甚至更多,你都可以通过 FileManager 类来获取。

File Information

回到app,仍然在 ViewController.swift 中,查找 tableViewSelectionDidChange 。设置 ViewController 的property: selectedItem

滚动回到顶部,并找到 selectedItem 被定义的地方。和 selectedFolder 一样, didSet 观察者正在观察这个property的变化。当这个property改变时,如果新的值不为 nil ,观察者就会调用 infoAbout(url:) 。这里将是你检索的信息用来展示的地方。

找到 infoAbout ,当前它会返回一个无聊的静态字符串,用下面的代码来替换它:

func infoAbout(url: URL) -> String {
// 1
let fileManager = FileManager.default
// 2
do {
// 3
let attributes = try fileManager.attributesOfItem(atPath: url.path)
var report: [String] = ["(url.path)", ""]
<span class="hljs-comment">// 4</span>
<span class="hljs-keyword">for</span> (key, value) <span class="hljs-keyword">in</span> attributes {
  <span class="hljs-comment">// ignore NSFileExtendedAttributes as it is a messy dictionary</span>
  <span class="hljs-keyword">if</span> key.rawValue == <span class="hljs-string">"NSFileExtendedAttributes"</span> { <span class="hljs-keyword">continue</span> }
  report.append(<span class="hljs-string">"<span class="hljs-subst">\(key.rawValue)</span>:\t <span class="hljs-subst">\(value)</span>"</span>)
}
<span class="hljs-comment">// 5</span>
<span class="hljs-keyword">return</span> report.joined(separator: <span class="hljs-string">"\n"</span>)

} catch {
// 6
return "No information available for (url.path)"
}
}

这里发生了一些不同的事,因此我们一次看一个:


  1. 按照惯例,获取一个 FileManager 单例的引用。
  2. 使用 do...catch 来捕获任何的错误。
  3. 使用 FileManager 类的 attributesOfItem(atPath:) 方法来获取文件的信息。如果成功的话,它就会返回一个 [FileAttributeKey: Any] 类型的字典, FileAttributeKeys 是一个带有字符串 rawValue 的结构体的成员。
  4. 将key的名称和value的值组装成一个tab分隔符字符创的数组。但会忽略掉 NSFileExtendedAttributes 键,因为它包含了一个复杂的但并不是确实有用的字段。
  5. 将整个数组组装成一个单独的字符串,返回它。
  6. 如果 try 语句抛出了错误,就返回一个默认的报告。

Build并再次运行,就像之前一样选择一个目录,然后点击列表中的任一文件或目录:

Folder Information

你现在已经获得了很多关于这个文件或目录有用的信息。但还有更多你可以做的事!

更多特征

这个app正在变得更棒,但仍然缺少一些事情:

  • 点击 Show Invisible Files 不会改变任何事。
  • 双击一个目录应当进入它的内容。
  • Move Up 按钮需要向上移动目录的层次结构。
  • Save Info 应当可以把被选择文件的详情到一个文件中。

接下来你就会出来这些事情。

处理不可见的文件

在Unix系统中,名称以一个句点开头文件和目录将会不可见。你会添加代码来处理这个情况。

前往 contentsOf(folder:) ,并用下列代码替换 map 这行:

let urls = contents
.filter { return showInvisibles ? true : $0.characters.first != "." }
.map { return folder.appendingPathComponent($0) }

上面的代码添加了一个 filter ,当 showInvisibles 的property不为
true 时,就会拒绝隐藏的项目;否则 filter 会返回所有的项目,包括因此的。

找到 ViewController 中的 toggleShowInvisibles 方法,并插入下列代码到函数中:

// 1
showInvisibles = (sender.state == NSOnState)
// 2
if let selectedFolder = selectedFolder {
filesList = contentsOf(folder: selectedFolder)
selectedItem = nil
tableView.reloadData()
}

这些代码完成了:

  1. 根据sender的状态设置 showInvisibles property。由于sender是 NSButton ,所以它的state不是 NSOnState 就是 NSOffState 。由于它是一个checkbox按钮, NSOnState 就表示勾选。
  2. 如果当前有 selectedFolder ,就重新生成 filesList 并更新UI。

运行项目,选择一个目录,并勾选及取消 Show Invisible Files 按钮。依赖于你正在观察的目录,你有可能会在当 Show Invisible Files 被勾选时,看到以一个圆点的文件。

Show Invisible Files

处理双击目录的情况

在storyboard中,table view已被分配了一个叫做 tableViewDoubleClicked 双击动作 。找到 tableViewDoubleClicked 并用以下代码替换:

@IBAction func tableViewDoubleClicked(_ sender: Any) {
// 1
if tableView.selectedRow < 0 { return }
// 2
let selectedItem = filesList[tableView.selectedRow]
// 3
if selectedItem.hasDirectoryPath {
selectedFolder = selectedItem
}
}

一步一步地回顾上面的代码:

  1. 检查双击是否发生在一个已填充的行上。如果点击在table的空白的部分,就会将 tableView的 selectedRow设置为-1。
  2. filesList 中获取匹配的 URL
  3. URL 是一个目录,设置 ViewController的 selectedFolder property。就像当你使用Select Folder按钮来选择一个目录一样,设置这个property就会触发property的观察者来读取目录的内容,并更新UI。如果 URL 不是目录,则不执行任何事。

运行项目,选择一个包含其它目录的目录,然后双击这个列表中的目录进入它。

处理Move Up按钮

一旦你实现了双击进入目录,接下来显然就应该是移回到“树”上了。

找到空的 moveUpClicked 方法并用下列的代码来替换它:

@IBAction func moveUpClicked( sender: Any) {
if selectedFolder?.path == "/" { return }
selectedFolder = selectedFolder?.deletingLastPathComponent()
}

首先,检查 selectedFolder 是否是根目录。如果是的话,你就不能做任何事,如果不是的话,就使用 URL 的方法来去掉URL的最后一部分。编辑 selectedFolder 就会像之前一样触发更新。

Build并再次运行;确认你可以选择一个目录,双击并移动到一个子目录上,并点击 Move Up 来回到目录的层级中。只要你不在根目录上,你甚至可以在双击目录之前向上移动。

Move Up The Folder Tree

注意: 正如你看到的一样,property观察器( didSet )非常得有用。用来更新界面的所有代码都在一个观察者中,因此无论是UI元素或方法来改变被观察的property,更新就发生了,无需做任何其它的事。Sweet!

保存信息

保存数据主要有两个办法:用户发起的保存以及自动保存。对于用户发起的保存,你的app应答让用户选择一个保存数据的位置,来将数据写入到这个位置中。对于自动保存,app自己会决定保存数据的位置。

在这一部分,你会处理当用户点击 Save Info 按钮来发起保存的case。

你已使用 NSOpenPanel 来提示用户选择一个目录。这次,你会使用 NSSavePanel NSOpenPanel NSSavePanel 都是 NSPanel 的子类,因此它们有很多共同的地方。

使用下列代码来替换空方法 saveInfoClicked

@IBAction func saveInfoClicked( sender: Any) {
// 1
guard let window = view.window else { return }
guard let selectedItem = selectedItem else { return }
// 2
let panel = NSSavePanel()
// 3
panel.directoryURL = FileManager.default.homeDirectoryForCurrentUser
// 4
panel.nameFieldStringValue = selectedItem
.deletingPathExtension()
.appendingPathExtension("fs.txt")
.lastPathComponent
// 5
panel.beginSheetModal(for: window) { (result) in
if result == NSFileHandlingPanelOKButton,
let url = panel.url {
// 6
do {
let infoAsText = self.infoAbout(url: selectedItem)
try infoAsText.write(to: url, atomically: true, encoding: .utf8)
} catch {
self.showErrorDialogIn(window: window,
title: "Unable to save file",
message: error.localizedDescription)
}
}
}
}

依次来看每个被标号的注释:

  1. 确认每样你需要的都存在:一个用来展示面板的窗口,已经你将要保存的 URL
  2. 创建一个 NSSavePanel
  3. 设置 directoryURL property,它会指定展示在面板中的发起目录。
  4. 设置 nameFieldStringValue property,来为文件设置一个默认的名称。
  5. 展示面板,并在一个闭包中来等待用户完成。
  6. 如果用户选择了一个有效的数据文件的路径(一个有效的 URL )并点击 OK 按钮,获取文件信息并将它写入到被选择的文件中。如果出现错误,就展示一个对话框。注意如果用户在保持的对话框中点击 Cancel ,你只需忽略操作。

write(to:atomically:encoding) 是一个字符串的方法,它会将字符串写入到被提供的 URL 中。 atomically 选项意味着字符串将会写入到一个临时的文件中,并进行重命名,确保你不会在一个坏掉的文件上结束 - 即使系统在写入过程中崩溃了。在这个文件中,文本的编码方式被设置为 UTF8 ,这是一个通用的标准。

运行项目,从列表中选择一个文件或目录,点击 Save Info 。选择一个待保存的位置,并点击 Save 。你会以一个文本文件结束,看起来就像下面这样:

File Info

注意: 使用 NSSavePanel 的一个很好的特性,就是如果你尝试覆盖一个早已存在的文件,你的app会自动展示一个确认对话框,询问你是否想要替换文件。

这已经闭环了app的特性列表,但这里还有一个很好的特性可以添加:当app重新启动时,记录被选择的目录和项目,将最后被选择的目录再展示出来。

保存App的状态

通常,我会把app状态的数据保存到 UserDefaults 中,它会为你自动保存到 Preferences 目录中。但不允许你对文件系统做任何事。相反,你会保存这个数据到 Application Support 目录中的专用的app目录中。

滚动到 ViewController.swift 的尾部,你会看到一个extension,专门用来保存和恢复用户的选择。

我已经提供了用来进行实际的读写的方法。写入会使用和保存info file相同的 write(to:atomically:encoding) 方法。读取则使用一个 String 构造器,从 URL 创建一个 String

一个非常有趣的事是如何决定去哪里保存数据。你会在 urlForDataStorage 来做这件事,它现在返回的是 nil

使用下列的代码替换 urlForDataStorage

private func urlForDataStorage() -> URL? {
// 1
let fileManager = FileManager.default
// 2
guard let folder = fileManager.urls(for: .applicationSupportDirectory,
in: .userDomainMask).first else {
return nil
}
// 3
let appFolder = folder.appendingPathComponent("FileSpy")
var isDirectory: ObjCBool = false
let folderExists = fileManager.fileExists(atPath: appFolder.path,
isDirectory: &isDirectory)
if !folderExists || !isDirectory.boolValue {
do {
// 4
try fileManager.createDirectory(at: appFolder,
withIntermediateDirectories: true,
attributes: nil)
} catch {
return nil
}
}
// 5
let dataFileUrl = appFolder.appendingPathComponent("StoredState.txt")
return dataFileUrl
}

这些代码都做了些什么?

  1. 又是你的老朋友 FileManager 。:]
  2. FileManager 有一个方法,可以返回对于指定用途的恰当的 URL 的列表。在这个case中,你会在用户当前的目录下查找 applicationSupportDirectory 。这个基本上是不大可能返回超过一个的URL的,并且你只想获取第一个元素。你可以用不同的参数来调用这个方法,来找到更多不同的目录。
  3. 就像你在playground中做的一样,添加一个路径成分来创建一个app指定目录的 URL ,并检查它是否存在。
  4. 如果这个目录不存在,尝试创建它,以及任何由路径决定的中间目录。如果创建失败的话,就返回 nil
  5. 添加另一个路径成分,来创建数据文件的完整的 URL ,并返回它。
注意: .applicationSupportDirectory FileManager.SearchPathDirectory.applicationSupportDirectory 的一个简写的方式。 .userDomainMask 则指向了 FileManager.SearchPathDomainMask.userDomainMask 。虽然简写的方式会更容易输入和阅读,但完整的方式会更有用于了解这些来自于哪里,因此如果需要的话,你可以在文档中找到他们。

运行项目,选择一个目录,然后点击一个目录或文件。使用 Quit 菜单项或 Command-Q 来关闭app。不要通过 Xcode 来退出,否则生命循环的方法就不会触发保存的动作。再次运行app,可以注意到它自动打开了你上次退出时正在查看的文件或目录。

Saved App State

注意: 如果你想查看存储状态的数据文件,就按住 Option 键,然后点击 Finder的Go 菜单并选择 Library 。在新的Finder窗口中,打开 Application Support 并查找 FileSpy 目录。你应该会看到带有你选择的目录和项目的 StoredState.txt 文件。

从这儿去向哪里?

你可以在 这里 下载最后的示例项目。

FileManager 类的教程中:

  1. 你学到了 URL 如何表示本地的文件和目录,以及展示关于文件或目录的有用的属性。
  2. 你学到了如何添加和删除一个 URL 中的 路径成分
  3. 你探究了 FileManager 类,如property homeDirectoryForCurrentUser applicationSupportDirectory ,甚至 attributesOfItem ,它包含了一个文件或目录的详细信息。
  4. 你学到了如何保持一个文件的相关信息。
  5. 你学到了如何查看一个文件或目录是否存在。

有关更多的信息,请访问 苹果FileManager API参考文档 ,你可以找到更多关于 FileManager 类中可用的方法。

现在,你已经可以开始在你自己的app中应用关于文件和目录的知识了。