iOS应用中的持久存储与文档管理
1. User Defaults
UserDefaults类用于持久存储用户的偏好设置,本质上是NSDictionary属性列表文件的一种特殊情况。可以将UserDefaults标准对象当作字典来使用,它有键和值,通过键来设置和获取值。字典会自动保存为属性列表文件,但无需关心保存的位置和时间。
由于用户默认设置实际上是属性列表文件,因此只能存储属性列表值。如果对象类型不是属性列表类型,需要将其归档为Data对象才能存储在用户默认设置中。如果对象类型是Cocoa类并采用了NSCoding协议,可以通过NSKeyedArchiver进行归档;如果对象类型是自定义的,可以让其采用Codable协议并通过PropertyListEncoder进行归档。
为了在用户有机会设置之前为键提供默认值,可以调用
register(defaults:)
方法。以下是一个示例代码:
UserDefaults.standard.register(defaults: [
Default.hazyStripy : HazyStripy.hazy.rawValue,
Default.cardMatrixRows : 4,
Default.cardMatrixColumns : 3,
Default.color1 : try! NSKeyedArchiver.archivedData(
withRootObject: UIColor.blue, requiringSecureCoding:true),
Default.color2 : try! NSKeyedArchiver.archivedData(
withRootObject: UIColor.red, requiringSecureCoding:true),
Default.color3 : try! NSKeyedArchiver.archivedData(
withRootObject: UIColor.green, requiringSecureCoding:true),
])
这个方法通常在应用启动时尽早调用。如果应用之前运行过且用户已经设置了这些偏好,调用该方法没有影响;否则,会为这些偏好提供初始值。
提供用户与默认设置进行交互的方式有两种:
- 应用提供自定义界面,例如某个游戏应用通过标签栏界面让用户设置偏好。
- 提供设置包,主要由一个或多个属性列表文件组成,描述界面以及对应的用户默认键和初始值。设置包的编写可参考Apple的相关编程指南。使用设置包的优点是可以使应用界面更简洁,无需编写协调偏好界面和用户默认值的代码,并且用户可以在应用未运行时设置偏好。但缺点是用户需要离开应用来访问偏好设置,并且对界面的控制不如在应用内自定义界面那样灵活。还可以通过以下代码将用户从应用直接带到设置应用中的应用偏好设置界面:
let url = URL(string:UIApplication.openSettingsURLString)!
UIApplication.shared.open(url)
UserDefaults还有一些其他常见用途:
- 作为全局“存储点”,方便不同实例之间传递信息。
- 作为基于视图控制器的内置状态保存和恢复机制的轻量级替代方案,例如某个游戏应用会将游戏板和牌组的状态记录到用户默认设置中,以便在应用终止后重新启动时恢复游戏状态。
- 用于应用与其扩展之间的数据通信。在配置应用和扩展组成应用组后,两者都可以访问与应用组关联的UserDefaults。
2. 简单的文件共享和预览
iOS提供了基本途径,使文件能够安全地进出应用沙盒。文件共享允许用户操作应用的Documents目录内容,UIDocumentInteractionController允许用户将文档副本传递给其他应用,或让其他应用将文档副本传递给当前应用,还可以预览与Quick Look兼容的文档。
2.1 文件共享
文件共享意味着应用的Documents目录可以通过iTunes被用户访问。用户可以向该目录添加文件,将文件和文件夹保存到计算机,还可以重命名和删除文件和文件夹。要支持文件共享,需要将Info.plist中的“Application supports iTunes file sharing”(UIFileSharingEnabled)键设置为YES。当整个Documents目录对用户可见后,通常不适合再用它来存储私有文件,可以考虑使用Application Support目录。应用不会自动收到用户更改Documents目录内容的通知,需要自行检测并做出相应响应。
2.2 文档类型和接收文档
应用可以声明愿意打开特定类型的文档。为此,需要在Info.plist中配置“Document types”(CFBundleDocumentTypes)键。这是一个数组,每个条目是一个字典,通过“Document Content Type UTIs”(LSItemContentTypes)、“Document Type Name”(CFBundleTypeName)等键指定文档类型。
例如,要声明应用可以打开PDF和文本文件,可在Xcode的目标Info选项卡中编辑文档类型部分。当用户在邮件中收到PDF文件时,Mail应用会显示该文件,用户还可以通过活动视图将文件复制到其他应用,其中可能包括当前应用。如果用户点击将PDF发送到当前应用的按钮,应用委托的
application(_:open:options:)
方法会被调用,应用需要处理传入文档的打开操作。系统会将文档复制到Documents目录下的Inbox文件夹中。以下是处理文档打开的示例代码:
// 应用委托中的代码
func application(_ app: UIApplication, open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {
let vc = self.window!.rootViewController as! ViewController
vc.displayDoc(url: url)
return true
}
// 视图控制器中的代码
func displayDoc (url:URL) {
let req = URLRequest(url: url)
self.wv.loadRequest(req)
}
在实际应用中,可能需要检查传入的文档是否为预期类型,并且要考虑应用当前的运行状态。如果应用是通过传入URL启动的,
application(_:didFinishLaunchingWithOptions:)
方法会被调用,可以根据需要处理URL并决定是否阻止
application(_:open:options:)
方法的调用。
2.3 传递文档
当应用获取到文档并希望用户将其副本传递给其他应用时,可以使用UIDocumentInteractionController类。该类异步操作,通常将其实例存储在属性中。以下是一个示例代码:
let dic = UIDocumentInteractionController()
self.dic.url = url
let v = sender as! UIView
self.dic.presentOpenInMenu(from:v.bounds, in: v, animated: true)
有两种活动视图可供选择,分别通过不同的方法调用:
| 方法 | 描述 |
| ---- | ---- |
|
presentOpenInMenu(from:in:animated:)
presentOpenInMenu(from:animated:)
| 显示列出可复制文档的应用的活动视图 |
|
presentOptionsMenu(from:in:animated:)
presentOptionsMenu(from:animated:)
| 显示列出可复制文档的应用以及其他可能操作(如消息、邮件、复制和打印)的活动视图 |
2.4 预览文档
UIDocumentInteractionController还可以用于预览文档。要预览文档,需要调用
presentPreview(animated:)
方法,并为其设置委托,委托必须实现
documentInteractionControllerViewControllerForPreview(_:)
方法,返回一个包含预览视图控制器的现有视图控制器。以下是示例代码:
self.dic.url = url
self.dic.delegate = self
self.dic.presentPreview(animated:true)
func documentInteractionControllerViewControllerForPreview(
_ controller: UIDocumentInteractionController) -> UIViewController {
return self
}
如果返回的视图控制器是UINavigationController,预览视图控制器会被推送到其上;否则,预览视图控制器是一个带有完成按钮的呈现视图控制器。预览界面还包含一个共享按钮,可让用户召唤选项活动视图。另外,如果调用
presentOptionsMenu
方法,并且委托实现了
documentInteractionControllerViewControllerForPreview(_:)
方法,活动视图会包含一个Quick Look图标,用户点击该图标可以召唤预览界面。还可以通过一些委托方法跟踪交互过程中的关键阶段结束情况。
2.5 Quick Look预览
预览实际上是通过Quick Look框架提供的。可以跳过UIDocumentInteractionController,直接通过QLPreviewController进行预览。以下是一个示例代码,用于预览Documents目录中的PDF和文本文件:
self.docs = [URL]()
do {
let fm = FileManager.default
let docsurl = try fm.url(for:.documentDirectory,
in: .userDomainMask, appropriateFor: nil, create: false)
let dir = fm.enumerator(at: docsurl, includingPropertiesForKeys: nil)!
for case let f as URL in dir {
if self.exts.contains(f.pathExtension) {
if QLPreviewController.canPreview(f as QLPreviewItem) {
self.docs.append(f)
}
}
}
guard self.docs.count > 0 else { return }
let preview = QLPreviewController()
preview.dataSource = self
preview.currentPreviewItemIndex = 0
self.present(preview, animated: true)
} catch {
print(error)
}
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return self.docs.count
}
func previewController(_ controller: QLPreviewController,
previewItemAt index: Int) -> QLPreviewItem {
return self.docs[index] as QLPreviewItem
}
QLPreviewController的数据源负责提供要预览的文档信息。通过为QLPreviewController设置委托,可以使其从界面中的某个视图缩放显示出来。对于自定义的文档类型,还可以提供自己的Quick Look预览。
3. 文档架构
如果应用的基本操作依赖于打开、保存和维护特定类型的文档,可以利用文档架构。文档架构的核心是UIDocument类,一个UIDocument实例管理着应用内部模型数据与存储该数据的文档文件之间的关系。
UIDocument可以无缝处理与存储文档文件交互时的一些棘手问题:
- 在后台线程进行数据的读写操作,避免阻塞主线程。
- 提供自动保存功能,确保数据更改时自动写入文件。
- 使用NSFileCoordinator确保在多应用访问文档时进行读写操作的一致性。
- 参与NSFilePresenter协议,防止文档打开时信息过时。
- 自动处理安全范围URL,以便打开其他应用沙盒中的文档。
- 作为应用文档参与iCloud同步的网关。
要使用UIDocument,需要声明一个UIDocument子类,并重写两个方法:
-
load(fromContents:ofType:)
:在从文件打开文档时调用,需要将内容值转换为应用可以使用的模型对象,并存储在实例属性中。
-
contents(forType:)
:在将文档保存到文件时调用,需要将应用的模型对象转换为Data实例(如果文档是包,则为FileWrapper)并返回。
要实例化一个UIDocument,调用其指定的初始化方法
init(fileURL:)
,该方法会设置fileURL属性,并将UIDocument与该URL对应的文件关联起来。通常将UIDocument实例存储在实例属性中,用于创建、打开、保存和关闭文档文件。创建新文档的示例代码如下:
let doc = MyDocument(fileURL: url)
doc.save(to: url, for: .forCreating) { success in
if success {
print("Document created successfully.")
} else {
print("Failed to create document.")
}
}
在这个过程中,
contents(forType:)
方法会被调用,需要在UIDocument子类中提供表示无数据时模型数据的默认值。
以下是关于文档操作流程的mermaid流程图:
graph TD;
A[开始] --> B[创建UIDocument子类];
B --> C[重写load和contents方法];
C --> D[实例化UIDocument];
D --> E{是否创建新文档};
E -- 是 --> F[调用save方法创建文档];
E -- 否 --> G[调用open方法打开文档];
F --> H[保存成功];
G --> I[打开成功];
H --> J[进行文档操作];
I --> J;
J --> K{是否需要保存};
K -- 是 --> L[调用save方法保存文档];
K -- 否 --> M[继续操作];
L --> N[保存完成];
N --> O[关闭文档];
M --> K;
O --> P[结束];
总结来说,iOS提供了多种方式来处理用户偏好设置、文件共享、文档传递和预览以及文档管理。合理使用这些功能可以提高应用的用户体验和数据管理效率。在实际开发中,需要根据应用的具体需求选择合适的方法和技术。
iOS应用中的持久存储与文档管理
4. 综合应用示例
为了更好地理解上述持久存储和文档管理的概念,下面给出一个综合应用示例。假设我们要开发一个简单的笔记应用,用户可以创建、保存和查看笔记,并且可以将笔记共享给其他应用。
4.1 用户偏好设置
首先,我们可以使用UserDefaults来存储用户的一些偏好设置,例如笔记的默认字体大小和颜色。
// 设置默认偏好
UserDefaults.standard.register(defaults: [
"defaultFontSize": 16,
"defaultFontColor": try! NSKeyedArchiver.archivedData(
withRootObject: UIColor.black, requiringSecureCoding: true)
])
// 获取偏好设置
let fontSize = UserDefaults.standard.integer(forKey: "defaultFontSize")
if let colorData = UserDefaults.standard.data(forKey: "defaultFontColor"),
let fontColor = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: colorData) {
// 使用字体大小和颜色
}
4.2 文档管理
我们使用UIDocument来管理笔记文档。定义一个笔记文档类:
class NoteDocument: UIDocument {
var noteText: String = ""
override func load(fromContents contents: Any, ofType typeName: String?) throws {
if let data = contents as? Data,
let text = String(data: data, encoding: .utf8) {
noteText = text
}
}
override func contents(forType typeName: String) throws -> Any {
return noteText.data(using: .utf8)!
}
}
创建和保存笔记文档:
func createAndSaveNote() {
let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("note.txt")
let noteDoc = NoteDocument(fileURL: fileURL)
noteDoc.noteText = "这是一条新笔记。"
noteDoc.save(to: fileURL, for: .forCreating) { success in
if success {
print("笔记保存成功。")
} else {
print("笔记保存失败。")
}
}
}
打开笔记文档:
func openNote() {
let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("note.txt")
let noteDoc = NoteDocument(fileURL: fileURL)
noteDoc.open { success in
if success {
print("笔记内容:\(noteDoc.noteText)")
} else {
print("笔记打开失败。")
}
}
}
4.3 文档共享
使用UIDocumentInteractionController来实现笔记文档的共享。
func shareNote() {
let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("note.txt")
let dic = UIDocumentInteractionController(url: fileURL)
let button = UIButton(type: .system)
dic.presentOpenInMenu(from: button.bounds, in: button, animated: true)
}
以下是这个笔记应用的操作步骤列表:
1. 应用启动时,使用UserDefaults加载用户偏好设置。
2. 用户创建笔记时,实例化NoteDocument并调用save方法保存笔记。
3. 用户打开笔记时,实例化NoteDocument并调用open方法读取笔记内容。
4. 用户共享笔记时,使用UIDocumentInteractionController展示共享菜单。
5. 性能优化与注意事项
在使用这些持久存储和文档管理技术时,还需要注意一些性能优化和其他事项。
5.1 UserDefaults性能
- UserDefaults的读写操作相对较快,但如果存储大量数据,可能会影响性能。因此,只存储少量的用户偏好设置,避免存储大对象。
-
频繁调用
register(defaults:)方法会增加不必要的开销,应在应用启动时尽早调用一次。
5.2 文档管理性能
- 对于大文件的读写操作,应考虑在后台线程进行,避免阻塞主线程,影响用户体验。
- 合理使用自动保存功能,避免过于频繁的保存操作,可设置适当的保存间隔。
5.3 安全问题
- 当处理其他应用沙盒中的文档时,要确保使用安全范围URL,以防止权限问题。
- 对于敏感的用户偏好设置和文档内容,可考虑进行加密存储。
以下是性能优化和注意事项的总结表格:
| 类别 | 优化建议 |
| ---- | ---- |
| UserDefaults | 存储少量数据,启动时调用一次register方法 |
| 文档管理 | 后台线程处理大文件读写,合理设置自动保存间隔 |
| 安全问题 | 使用安全范围URL,加密敏感数据 |
6. 未来趋势与拓展
随着iOS系统的不断发展,持久存储和文档管理技术也在不断演进。未来可能会有以下趋势和拓展:
- 更强大的云存储支持 :iCloud的功能可能会进一步增强,使得应用的文档在不同设备之间的同步更加快速和稳定。
- 新的文档格式和标准 :可能会出现新的文档格式和标准,以满足更复杂的应用需求。
- 更好的用户交互体验 :在文件共享和预览方面,可能会提供更直观、便捷的用户界面和操作方式。
开发者可以关注这些趋势,及时更新应用的功能和技术,以提供更好的用户体验。
在实际开发中,开发者还可以根据应用的具体场景进行拓展。例如,结合机器学习技术对笔记内容进行分析和分类,或者增加对更多文件类型的支持。
总之,iOS的持久存储和文档管理为开发者提供了丰富的工具和功能。通过合理运用这些技术,并关注未来的发展趋势,开发者可以开发出更加高效、安全和用户友好的应用。
以下是未来趋势与拓展的mermaid流程图:
graph TD;
A[当前技术] --> B[云存储增强];
A --> C[新文档格式];
A --> D[更好交互体验];
B --> E[快速稳定同步];
C --> F[满足复杂需求];
D --> G[直观便捷操作];
E --> H[应用更新];
F --> H;
G --> H;
H --> I[更好用户体验];
通过以上内容,我们全面了解了iOS应用中的持久存储和文档管理技术,包括UserDefaults的使用、文件共享和预览、文档架构以及综合应用示例。同时,也探讨了性能优化、安全问题和未来趋势。希望这些内容对开发者在实际项目中有所帮助。
iOS应用持久存储与文档管理全解析
超级会员免费看
45

被折叠的 条评论
为什么被折叠?



