关于数据持久化操作一直是大家非常关注的问题,我最近看了好多关于数据存储方式的文章,小编在这里写下我的见解,希望能对大家有所帮助!
谈到数据储存,首先要明确区分两个概念,数据结构和储存方式。所谓数据结构就是数据存在的形式。除了基本的Dictionary、Array和Set这些对象,还有更复杂的如:关系模型、对象图和属性列表多种结构。而存储方式则简单的分为两种:内存与闪存。内存存储是临时的,运行时有效的,但效率高,而闪存则是一种持久化存储,但产生I/O消耗,效率相对低。把内存数据转移到闪存中进行持久化的操作称成为归档。
目前数据存储方式大致上可以归纳为以下几种:
1、UserDefaults;这种存储方式非常常用,它操作方便,这是系统给的一个单例类,直接操作沙盒中的Library中的Preferences中的一个名为本应用的BundleID的plist文件。但是它只适合存储用户的一些偏好设置,不易存储大量数据,它在软件升级后不会被删除。
2、writeToFile写入;该方法是一次性向文件中写入内容,本次写入会覆盖文件中原来的内容,所以适合存储经常不易变动的数据文件,可以配合plist文件使用。
3、NSCoding归档;它跟其他存储方式略有不同,他可以存储对象,能把任何对象都直接保存成文件的方式来存储,但该对象必须遵循NSCoding协议,NSKeyedArchiver存储,NSKeyedUnarchiver读取,由于它可以直接对对象进行存储,所以也很常用,比较受大家喜欢。
4、SQLite3数据库;这种方法最实用但是操作也最麻烦,于是GitHub上就出现了好多关于对SQLite3的封装,其中评星最高的有FMDB(OC)、SQLite.Swift(Swift),这两种我都试着用了一下,感觉还是有点儿麻烦,后来也对这两个库进行了2次封装,凑合着用了,这里我不是专门说它,就不拿出来献丑了,我想好多人对数据库操作不是很了解,关于对数据库的操作,我推荐看它:http://www.runoob.com/sqlite/sqlite-create-table.html。
5、Core Data(苹果官方推荐);它其实是对SQLite3的封装,哈哈,苹果也感觉直接使用SQLite3过于麻烦,于是乎…就出现了Core Data,它更加面向对象,苹果强烈推荐使用,但是每次总是需要建好多的文件,可我不习惯,看个人喜好了。
6、Realm;这是什么鬼,据说它有志于替代SQLite数据库,不过我还没仔细研究过,只是在网上看到过,评价还挺高,怎么用不清楚,但是我想不会比SQLite3操作更麻烦了,附上GitHub上地址。
好了,说了这么多,该回到正题了,今天我主要是说plist,全名PropertyList,即属性列表文件,它是一种用来存储串行化后的对象的文件。这种文件,在ios开发过程中经常被用到。这种属性列表文件的扩展名为.plist,因此通常被叫做plist文件。plist文件用source code方式打开,它其实是xml格式的。Plist文件是以key-value的形式来存储数据。既可以用来存储用户设置,也可以用来存储一些需要经常用到而不经常改动的信息。
plist存储方式操作简单,但是只适合存储小数据,像哪些以M为单位的数据就不易用这种方法了,因为它有个很大的缺点,在上面我也说到了:它每次写入都会覆盖文件中原来的内容。也就是说,每次你都需要先把文件里面的内容全部读取出来先放到内存中,然后修改好root dictionary里的内容之后再写入进去,你说是不是很鸡肋,如果你存储了几十M的东西,这样一下子写来写去的,你内存吃的消吗,况且这么大的数据读取和写入多耗时间啊。纵是如此我还是很喜欢用这种方式,小的数据没问题啊···
下面呢,我专门写了一个类来管理plist文件的操作,使用很简单,给大家分享一下:
import UIKit
public enum Type :Int{
case number
case string
case bool
case array
case dictionary
case null
case unknown
case data
}
class PlistManager: NSObject {
private override init() {
super.init()
}
static let defaltManager = PlistManager()
private let path = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)[0]
/** 读取根目录 plistName:plist文件名 **/
subscript (_ plistName:String) -> Dictionary<String,Any> {
get {
let filePath = "\(path)/MyDatas/\(plistName).plist"
if FileManager.default.fileExists(atPath: filePath) {
let rootDic = NSDictionary.init(contentsOfFile: filePath)
return rootDic as! Dictionary<String, Any>
}
return Dictionary<String,Any>()
}
}
/** 读取数据 plistName:文件名,name:字段名 **/
subscript (_ plistName:String,_ name:String) -> JHJSON {
get {
let filePath = "\(path)/MyDatas/\(plistName).plist"
if FileManager.default.fileExists(atPath: filePath) {
let rootDic = NSDictionary.init(contentsOfFile: filePath)
return JHJSON.init(rootDic![name])
}
return JHJSON.init(nil)
}
}
/** 写入数据 **/
func set(value:Any,forKey:String,plistName:String) {
if forKey == "" || plistName == ""{
return
}
let filePath = "\(path)/MyDatas/\(plistName).plist"
print("plist文件路径 === \(filePath)")
if !(FileManager.default.fileExists(atPath: filePath)) {
if let dict = value as? Dictionary<String,Any> {
//如果存储对象是字典就转化为json字符串,防止字典内存在空值导致写入plist失败
let dic = [forKey:dict.toJsonString()] as NSDictionary
dic.write(toFile: filePath, atomically: true)
}else{
let dic = [forKey:value] as NSDictionary
dic.write(toFile: filePath, atomically: true)
}
}else{
if let dict = value as? Dictionary<String,Any> {
//如果存储对象是字典就转化为json字符串,防止字典内存在空值导致写入plist失败
var dic = self[plistName]
dic[forKey] = dict.toJsonString()
(dic as NSDictionary).write(toFile: filePath, atomically: true)
}else{
var dic = self[plistName]
dic[forKey] = value
(dic as NSDictionary).write(toFile: filePath, atomically: true)
}
}
}
}
/** 用来解析数据 **/
struct JHJSON {
fileprivate var rawArray: [Any]!
fileprivate var rawDictionary: [String : Any]!
fileprivate var rawString: String!
fileprivate var rawNumber: NSNumber!
fileprivate var rawNull: NSNull = NSNull()
fileprivate var rawBool: Bool = false
fileprivate var rawData : Data!
fileprivate var type: Type = .null
public init(_ object: Any?) {
if object != nil {
switch object {
case let object as [Any] where object.count > 0:
self.rawArray = object as [Any]
self.type = .array
break
case let object as Dictionary<String,Any> where object.count > 0:
self.rawDictionary = object
self.type = .dictionary
break
case let object as String:
self.rawString = object
self.type = .string
break
case let object as NSNumber:
self.rawNumber = object
self.type = .number
break
case let object as Bool:
self.rawBool = object
self.type = .bool
break
case let object as Data:
self.rawData = object
self.type = .data
break
default:
self.type = .null
break
}
}
}
/** 转化为String **/
public var stringValue: String {
get {
switch self.type {
case .string:
return self.rawString ?? ""
case .number:
return self.rawNumber.stringValue
case .bool:
return self.rawBool == true ? "1" : "0"
case .data:
return String.init(data: self.rawData, encoding: .utf8)!
default:
return ""
}
}
}
/** 转化为Array **/
public var arrayValue: [Any] {
get {
switch self.type {
case .array:
return self.rawArray
default:
return [Any]()
}
}
}
/** 转化为Dictionary **/
public var dictionaryValue: Dictionary<String,Any> {
get {
switch self.type {
case .dictionary:
return self.rawDictionary
case .data:
do {
let json = try JSONSerialization.jsonObject(with: self.rawData, options: .mutableContainers)
if let dic = json as? Dictionary<String,Any> {
return dic
}else {
print("不是正确的字典格式")
return Dictionary<String,Any>()
}
} catch {
print("不是正确的字典格式:\(error.localizedDescription)")
return Dictionary<String,Any>()
}
case .string:
let jsonData:Data = self.rawString.data(using: .utf8)!
do {
let json = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)
if let dict = json as? Dictionary<String,Any> {
return dict
}else {
print("不是正确的字典格式")
return Dictionary<String,Any>()
}
} catch {
print("不是正确的字典格式:\(error)")
return Dictionary<String,Any>()
}
default:
print("不是正确的字典格式")
return Dictionary<String,Any>()
}
}
}
/** 转化为NSNumber **/
public var numberValue: NSNumber {
get {
switch self.type {
case .string:
let decimal = NSDecimalNumber(string: self.rawString)
if decimal == NSDecimalNumber.notANumber { // indicates parse error
return NSDecimalNumber.zero
}
return decimal
case .number:
return self.rawNumber ?? NSNumber(value: 0)
case .bool:
return NSNumber(value: self.rawBool ? 1 : 0)
default:
return NSNumber(value: 0.0)
}
}
}
/** 转化为Bool **/
public var boolValue: Bool {
get {
switch self.type {
case .bool:
return self.rawBool
case .number:
return self.rawNumber.boolValue
case .string:
return ["true", "y", "t"].contains() { (truthyString) in
return self.rawString.caseInsensitiveCompare(truthyString) == .orderedSame
}
default:
return false
}
}
}
/** 转data **/
public var dataValue: Data {
get {
switch self.type {
case .data:
return self.rawData
default:
return Data.init()
}
}
}
}
使用方法:
PlistManager.defaltManager.set(value: 2, forKey: "id", plistName: "MyPlis")//写入
print(PlistManager.defaltManager["MyPlis"])//读取root dictionary
print(PlistManager.defaltManager["MyPlis","data"].dictionaryValue)//根据字段读取
源文件已分享到github,点击下载,喜欢的话给颗星哈。。