2019-03-22Swift4.2中使用归档解档存储用户数据(
2019-03-25 本文已影响0人
Daniel梁
我们先看看使用归档和解档需要遵守的几个条件
条件:
- 归档解档存储的对象必须是class,而不是结构体。使用结构体可以用协议方法去实现,请有意者自行查资料。
- 该class需要继承NSObject,才可以使用KVC的方法,通过key拿到值或者通过key赋值。这是一个牺牲,不想继承的使用UserDafult方法去存储,毕竟Swift里面少点oc东西好
- 如果想通过运行时一次获得所有属性列表,更方便的进行归档和解档里面的kvc操作,需要在类前面加上@objcMembers修饰符 , 注意使用kvc获得json数据的时候需要在每个属性上面加上@objc 不然会出现key not founds错误
- 如@objcMembers class UserAccount :NSObject,NSCoding,Codable{...}这样去声明一个类。
所谓运行时获取属性,就是能一次性拿到该类的所有属性名字(他们都被存储在该对象isa指针指向的objc_class)里面,是里面的一个数组。
下面看获得属性列表的方法
func getPropertyNameList() -> [String] {
var count : UInt32 = 0
var names : [String] = []
let properties = class_copyPropertyList(type(of: self), &count)
guard let propertyList = properties else {
return []
}
for i in 0..<count{
let property = propertyList[Int(i)]
let char_b = property_getName(property)
//这里转一下encoding,转成utf8的key
if let key = String.init(cString: char_b, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) as String?{
names.append(key)
}
}
return names
}
获得我们这个属性列表后,我们就可以在重写的encode和decode方法的时候,直接去使用这个数组去初始化所有成员变量了。
下面是encode和decode方法
///归档数据存储到磁盘
func encode(with aCoder: NSCoder) {
let propertyList = getPropertyNameList()
print(propertyList)
propertyList.forEach { (p_name) in
print("\(p_name) + \(String(describing: value(forKey: p_name)))")
aCoder.encode(value(forKey: p_name), forKey: p_name)
}
//下面是没有通过ivars获得属性列表的情况
//aCoder.encode(access_token, forKey: "access_token")
// aCoder.encode(expiresDate, forKey: "expiresDate")
// aCoder.encode(uid, forKey: "uid")
// aCoder.encode(screen_name, forKey: "screen_name")
// aCoder.encode(avatar_large, forKey: "avatar_large")
}
required init?(coder aDecoder: NSCoder) {
super.init()
let propertyList = getPropertyNameList()
print(propertyList)
propertyList.forEach { (p_name) in
let value = aDecoder.decodeObject(forKey: p_name)
print("\(p_name) + \(String(describing: value))")
setValue(value, forKey: p_name)
}
//access_token = aDecoder.decodeObject(forKey: "access_token") as? String
// expiresDate = aDecoder.decodeObject(forKey: "expiresDate") as? Date
// uid = aDecoder.decodeObject(forKey: "uid") as? String
// screen_name = aDecoder.decodeObject(forKey: "screen_name") as? String
// avatar_large = aDecoder.decodeObject(forKey: "avatar_large") as? String
}
然后这时候我们在外面把该类对象写入磁盘(归档)和解档就会调用上面的encode 和 decode方法
下面来介绍一下Swift4.2的NSKeyedArchiver和NSKeyedUnArchiver的新使用方法
NSKeyedArchiver.archiveRootObject(, toFile: )
这个方法已经过期了。
注意方法返回是可能throw的,我们需要用try去使用这个函数
swift更希望我们使用下面这种do catch的结合,这样我们可以更好的对不同错误进行补抓和处理,而且代码结构也更好看。
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: account, requiringSecureCoding: false)
do{
try data.write(to: self.accountPath)
}
catch{
assert(true, "无法把account写入path")
}
}catch{
assert(true, "无法生成归档数据")
}
我们只能通过先把对象归档成一个data,再把data写入磁盘,而不是像之前那种一步写入。
下面看看解档
do {
let data = try Data.init(contentsOf: accountPath)
do{
// NSKeyedArchiver.archivedData(withRootObject: account, requiringSecureCoding: false)与下面方法配套使用
account = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? UserAccount
}
catch{
assert(true, "用户数据解档失败")
}
} catch {
assert(true, "用户数据解档路径错误")
}
注意NSKeyedArchiver.archivedData(withRootObject:,requiringSecureCoding)和 NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data)
要配套使用! 要配套使用!要配套使用!
你怎么归的档,就怎么解档,不然乱搭配他会无法解档,虽然你文件就是在那个目录,我那天就是因为用错方法调试了一天。。。 还有data.writeTo(Path:)这个要注意你传入URL的初始化的时候,是文件路径还是网络路径,这个也别搞错了,不然归档和解档可能出现位置错误。
Swift会越来越有自己的编程规范~~ 希望ios越做越好!