引言
在 iOS 之网络请求 一文中已经了解到如何进行网络请求,当通过网络请求获取到数据后需要将其持久性地存储起来时,就需要了解在iOS 开发
中如何进行持久性地存储数据
在iOS 开发
中对数据进行持久性地存储的方式主要有:本地存储
和数据库存储
本地存储
本地存储
是指将数据存储在设备的本地文件系统上,使应用程序能够在没有互联网连接的情况下访问和操作数据。本地存储通常用于存储非敏感
和小规模
的信息
UserDefaults
一种轻量级的本地存储方式,用于存储少量的键值对
数据。通常用于存储应用的偏好设置和用户配置
UserDefaults
是一个单例模式
的类,通过.standard
属性来访问应用程序中的唯一UserDefaults 实例
。这使得在整个应用程序中方便地共享和访问数据
swift
// 存储数据到UserDefaults
let datas = UserDefaults.standard
datas.set("JohnDoe", forKey: "Username")
datas.set(25, forKey: "Age")
datas.set(true, forKey: "IsLoggedIn")
// 读取数据从UserDefaults
let username = datas.string(forKey: "Username")
let age = datas.integer(forKey: "Age")
let isLoggedIn = datas.bool(forKey: "IsLoggedIn")
当从UserDefaults
中读取数据时,需要根据数据类型
来正确读取数据。如果尝试使用不匹配的类型来读取数据,可能会导致运行时错误或返回默认值(如果设置了默认值)
经典单例模式结构
swift
class MySingleton {
static let shared = MySingleton()
private init() {
// 私有的初始化方法,确保不能在外部创建多个实例
}
// 这里可以添加其他属性和方法
}
单例模式的经典结构包括一个私有的构造方法
和一个公共的静态属性(或方法)
,用于访问唯一实例
UserDefaults 单例模式
UserDefaults
的实现方式与经典单例模式类
的结构略有不同。UserDefaults
是Swift 标准库
提供的一个类,它是一个全局单例
,其实现方式不同于传统的单例模式,而是通过Swift 标准库
内部进行管理的,不需要手动实现单例模式
。只需要使用UserDefaults.standard
来访问全局的单例实例
,而不需要自己创建单例。这是Swift标准库为了方便数据持久化存储而提供的一种机制,它隐藏了单例模式
的复杂性
swift
open class UserDefaults : NSObject {
open class var standard: UserDefaults { get }
}
Plists
全称Property Lists
,一种用于存储和序列化数据的XML
或二进制
文件格式,通常以.plist
文件扩展名保存
Plists
文件通常是通过键-值(key-value)
的形式来存储各种类型的数据。每个数据项都有一个唯一的键(key)
,用于标识该数据项,然后根据键来获取对应的值。这种结构适用于存储字典、数组、字符串、整数、浮点数等多种数据类型
Plists
可用于存储结构化的数据,例如配置文件、应用设置、静态数据等
swift
// 创建一个字典,包含要保存到 Plist 文件的数据
let dataToSave: [String: Any] = [
"Username": "JohnDoe",
"Age": 25,
"IsLoggedIn": true
]
// 获取 Plist 文件的 URL,通常是在应用的沙盒目录中
if let plistURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("UserData.plist") {
do {
// 将数据写入 Plist 文件,使用 PropertyListSerialization
let plistData = try PropertyListSerialization.data(fromPropertyList: dataToSave, format: .xml, options: 0)
try plistData.write(to: plistURL)
print("Data saved to \(plistURL)")
// 从 Plist 文件读取数据
let plistDataRead = try Data(contentsOf: plistURL)
if let dictionary = try PropertyListSerialization.propertyList(from: plistDataRead, options: [], format: nil) as? [String: Any] {
if let username = dictionary["Username"] as? String {
print("Username: \(username)")
}
if let age = dictionary["Age"] as? Int {
print("Age: \(age)")
}
if let isLoggedIn = dictionary["IsLoggedIn"] as? Bool {
print("IsLoggedIn: \(isLoggedIn)")
}
}
} catch {
print("Error writing to Plist: \(error)")
}
}
拓展
Plist
文件的数据类型:
- 如果将文件扩展名设置为
.plist
,则数据将存储为XML 格式
的Plist
文件 - 如果将文件扩展名设置为
.bplist
,则数据将存储为二进制格式
的Plist
文件
PropertyListSerialization.data(fromPropertyList: dataToSave, format: .xml, options: 0)
处的format
用于指定输出的格式,有.xml
和.binary
两个值,这里的数据格式不会影响数据写入文件时的实际格式,写入文件的数据类型是与扩展名相关
Archiver
Archiver
是将对象序列化为二进制数据,并将其保存在文件中的过程,以便后续可以还原成原始对象。在iOS 开发
中,可以使用NSKeyedArchiver
和NSKeyedUnarchiver
类来进行归档
和反归档
操作
归档的步骤
- 创建要归档的对象:首先,你需要创建一个自定义的类,该类需要遵循
NSCoding
协议,这个协议要求实现encode(with:)
和init(coder:)
方法,以便对象可以被归档
和反归档
- 归档对象:使用
NSKeyedArchiver
类的实例,可以将对象归档
为二进制数据
并将其保存到文件中 - 反归档对象:使用
NSKeyedUnarchiver
类的实例,可以从文件中读取归档数据,并将其解析
为原始对象
1. 创建要归档的对象
swift
class Person: NSObject, NSCoding {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
// 实现NSCoding协议的方法
required init?(coder aDecoder: NSCoder) {
self.name = aDecoder.decodeObject(forKey: "name") as! String
self.age = aDecoder.decodeInteger(forKey: "age")
}
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
aCoder.encode(age, forKey: "age")
}
}
2. 归档
swift
let person = Person(name: "John", age: 30)
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let archiveURL = documentsDirectory.appendingPathComponent("person.archive")
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: person, requiringSecureCoding: false)
try data.write(to: archiveURL)
print("Person object archived successfully.")
} catch {
print("Error archiving person object: \(error)")
}
3. 反归档
swift
do {
let data = try Data(contentsOf: archiveURL)
if let loadedPerson = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Person {
print("Loaded person: \(loadedPerson.name), Age: \(loadedPerson.age)")
} else {
print("Failed to unarchive person object.")
}
} catch {
print("Error loading person object: \(error)")
}
数据库存储
数据库存储
是指将数据存储在结构化数据库中,以便有效地查询、检索和管理数据。这种方式适用于需要大规模数据存储
和复杂查询的情况
,例如应用程序中的用户数据、购物车数据等
CoreData
:苹果提供的一个高级持久性框架,提供对象关系映射(ORM)
功能,适用于复杂的数据模型
SQLite
:轻量级嵌入式关系型数据库
,适用于复杂的数据结构和查询需求
第三方数据库:使用第三方数据库解决方案,如Realm
、Firebase Realtime Database
、Couchbase Lite
等
持久化存储方案对比
UserDefaults 和 Plists 对比
相似之处
- 键-值存储:
Plist
和UserDefaults
都采用了键-值(key-value)
的存储方式,其中每个数据项都有一个唯一的键用于标识 - 支持多种数据类型: 两者都支持存储多种数据类型,包括字符串、整数、浮点数、数组、字典等
- 持久性存储:
Plist
和UserDefaults
都支持将数据持久性地存储在设备上,以便在应用程序重新启动后保留数据
关键区别
- 存储位置:
Plist
通常是存储在应用的沙盒目录
中的文件,可以轻松地创建、读取和编辑;UserDefaults
则是一个键值对存储系统
,使用更高级的 API
进行访问 - 数据规模:
Plist
通常用于存储较大或结构化的数据;UserDefaults
通常用于存储应用的用户设置和轻量级数据 - 访问方式:
Plist
需要手动读取和写入,通常需要使用PropertyListSerialization API
进行操作;UserDefaults
则提供了更高级别的 API
,使数据的读取和写入更加方便
Plists 和 Archiver 对比
Archiver
:适用于保存自定义对象
、复杂数据模型
、应用状态信息
等需要保留对象类型信息的场景。归档
可以将整个对象图保存到文件中,以便恢复对象的状态Plists
:适用于保存简单的数据结构
,如键-值对、配置信息、用户偏好设置等。Plists
不适用于保存自定义对象
,因为它不会保留对象的类信息
总之,Plist
和Archiver
都可以保存二进制数据
,但是Plist
不具备自动解析并还原自定义对象的功能,而归档(Archiver)
会保留对象的类信息
,使恢复更加容易
拓展
open 和 public 关键字
open
和 public
是Swift中的访问控制关键字,它们用于控制类、结构体、函数、属性等成员的可见性和访问权限。它们之间的主要区别在于访问级别和继承性:
open
: 使用open
关键字修饰的成员具有最高级别的访问权限
,对于其他模块(框架或库)来说是可见的,并且可以被继承
。这意味着其他模块可以扩展和继承open
成员。通常,open
用于创建可扩展的公共接口
,以便其他开发者可以继承和扩展public
: 使用public
关键字修饰的成员也对其他模块可见,但不具有继承性
。这意味着其他模块可以访问public
成员,但不能继承或重写
沙盒目录
沙盒目录(Sandbox Directory)
是iOS 应用程序
的私有文件系统空间
,每个iOS 应用
都有自己的独立沙盒目录
,用于存储应用程序的数据和文件。沙盒目录
的概念是为了确保应用程序之间的隔离性
和安全性
,防止一个应用程序访问其他应用程序的数据或文件
iOS 的文件系统
会根据应用程序
的包标识符
为每个应用程序创建一个独立的沙盒目录
。这意味着不同应用程序的沙盒目录
是相互隔离的,一个应用程序无法直接访问另一个应用程序的沙盒目录
内的文件
沙盒目录包括的主要文件夹
- Documents 目录: 用于存储
用户生成的文件
,例如文档、照片、视频等。这些文件会在应用程序备份时被包括在备份中,并可以通过iCloud
共享 - Library 目录: 该目录包含两个子目录
- Caches 目录: 用于存储可以重新生成的
临时数据
,例如缓存文件。这些文件不会在应用程序备份时被包括在备份中,因为它们可以随时重新生成 - Preferences 目录: 用于存储应用程序的
偏好设置
,通常以plist 文件
的形式存在
- Caches 目录: 用于存储可以重新生成的
- tmp 目录: 用于存储
临时文件
,这些文件不会被应用程序备份,且在应用程序退出后可能会被删除 - 应用程序包目录: 包含了应用程序的
可执行文件
和资源文件
,通常不允许在运行时修改这个目录中的文件
swift
// 获取路径
// for: .documentDirectory, .libraryDirectory, .cachesDirectory, .preferencePanesDirectory
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first{}
小结
关于iOS 开发
中关于数据持久化
部分,主要是以本地存储数据
为主,对比说明了UserDefaults
、Plists
和Archiver
这三种本地存储的方式;对于数据库存储
仅是做了简要概述,感兴趣可以深入了解,后面如果有用到也会更新相关文章的~~