关于数据持久化操作一直是大家非常关注的问题,我最近看了好多关于数据存储方式的文章,小编在这里写下我的见解,希望能对大家有所帮助!

谈到数据储存,首先要明确区分两个概念,数据结构和储存方式。所谓数据结构就是数据存在的形式。除了基本的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,点击下载,喜欢的话给颗星哈。。