iOS持久化 - NSCoding

NSCoding 是把数据存储在 iOS 和 MacOs 上的一种及其简单和方便的方式,它可以直接将一个模型对象转变成一个文件 然后再把这个文件加载到内存中,并不需要任何的文件解析和序列化的逻辑。

如果要实现利用 NSCoding 实现数据存储,首先要了解 NSoder 类和 NSCoding 协议。

NSCoder不可以直接实例化,我们需要继承 NSCoding 协议实现数据存储的功能,NSCoding 只有两个方法,从 一个是从 NSCoder 中读取数据,保存到响应的变量中,即反序列化数据,另一个是读取实例变量,并把这些数据写入到 Coder 中,即序列化输入。

1
2
3
4
5
6
7
8
9
10
class Book: NSObject, NSCoding {
required init?(coder aDecoder: NSCoder) {
print("从 Coder 中读取")
}
func encodeWithCoder(aCoder: NSCoder) {
print("保存到 Coder 中")
}
}

如果要实现数据的存储,我们还需要了解数据的写入,我们存储简单信息,最常用的就是 NSUserDefaults 类,它可以让我们用很方便的形式以 键 - 值 的形式存储信息。比如我们可以这样:

1
NSUserDefaults.standardUserDefaults().setObject("Swift", forKey: "Book")

使用起来非常简单,当我们需要使用这个存储的值的时候,我们可以这样获取:

1
NSUserDefaults.standardUserDefaults().objectForKey("Book")

但是有下面这样一种情况:

1
2
3
4
5
6
7
8
class Book {
var name:String?
var image:UIImage?
}
let book = Book()
NSUserDefaults.standardUserDefaults().setObject(book, forKey: "book")

这时 NSUserDefaults 是无法直接存储一个类对象的,那么这里需要用到的就是 NSKeyedArchiver 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Book: NSObject, NSCoding {
var name:String?
var image:UIImage?
override init() {
}
required init?(coder aDecoder: NSCoder) {
print("从 Coder 中读取")
}
func encodeWithCoder(aCoder: NSCoder) {
print("保存到 Coder 中")
}
}
let book = Book()
let data = NSKeyedArchiver.archivedDataWithRootObject(book)
NSUserDefaults.standardUserDefaults().setObject(data, forKey: "book")

我们这里用到了一个中间类 NSKeyedArchiver,先是将 Book 通过 NSKeyedArchiver 编码成了 NSData。 然后再将这个 NSData 存入到 NSUserDefaults 中。

NSKeyedArchiver 只能对实现了 NSCoding 协议的类进行编码,将它们转换成二进制数据,然后再存入NSUserDefaults 中。

这样,NSCodingNSKeyedArchiver 在一起使用,不但能够进行 NSUserDefaults 的存储,还能够进行对象的一些实体化和数据传输操作。

事实上,NSKeyedArchiver 有一个对应的类 NSKeyedUnarchiver,分别对应归档和解档,需要注意的是,这是两个类方法:

1
2
3
4
5
func objectToFile() {
NSKeyedArchiver.archiveRootObject(self, toFile: path)
NSKeyedUnarchiver.unarchiveObjectWithFile(path)
}

下面我们通过代码来认识 NSCoder 协议,首先声明一个类,继承 NSCoding 协议并声明两个属性,密令和用户编号:

1
2
3
4
5
6
7
8
9
10
class User: NSObject, NSCoding {
var accessToken: String?
var uid: String?
override init() {
}
}

下面我们使用 encodeWithCoder 将实例属性传入到 Coder,使用 ininWithCoder 将 Coder 中的数据取出赋值给实例属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class User: NSObject, NSCoding {
var accessToken: String?
var uid: String?
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(self.accessToken, forKey: "accessToken")
aCoder.encodeObject(self.uid, forKey: "uid")
print("传值给Coder")
}
required init?(coder aDecoder: NSCoder) {
self.accessToken = aDecoder.decodeObjectForKey("accessToken") as? String
self.uid = aDecoder.decodeObjectForKey("uid") as? String
print("从Coder取值")
}
override init() {
}
}

下面创建一个工具类 利用NSKeyedArchiver的存储和读取功能,首先声明两个属性,用户和路径:

1
2
3
4
5
6
class UserTools: NSObject {
var account: User?
let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first?.stringByAppendingString("/user")
}

下面我们来做一个单例:

1
2
3
4
5
6
7
class UserTools: NSObject {
var account: User?
let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first?.stringByAppendingString("/user")
static let singleton = UserTools()
}

下面我们实现将账号存到磁盘中的指定路径,以及将账号从磁盘中取出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class UserTools: NSObject {
var account: User?
let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first?.stringByAppendingString("/user")
static let singleton = UserTools()
func saveAccount(account: User) {
self.account = account
NSKeyedArchiver.archiveRootObject(account, toFile: path!)
print("写入到磁盘")
}
func readAccount() -> User{
self.account = NSKeyedUnarchiver.unarchiveObjectWithFile(path!) as? User
print("从磁盘取出")
return self.account!
}
}

通过上面的两个类,我们就实现了存储和读取功能,下面是存储方法:

1
2
3
4
5
6
7
8
9
10
11
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var account = User() //实例化用户类
account.accessToken = "PassWord"
account.uid = "UserName"
UserTools.singleton.saveAccount(account) //调用工具类方法 将用户写入文件
}
}

现在我们就可以读取到写入到磁盘的一个类对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var account = User() //实例化用户类
account.accessToken = "PassWord"
account.uid = "UserName"
UserTools.singleton.saveAccount(account) //调用工具类方法 将用户写入文件
account = UserTools.singleton.readAccount()
print(account)
}
}