Swift iOS : Archive

RecoReco發表於2019-03-02

Archive

想把物件序列化到檔案,可以首先令此物件符合NSCoding協議,然後使用歸檔類完成序列化。

如下案例,可以把物件User序列化到檔案內:

import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window : UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        window = UIWindow()
        window!.rootViewController = Page()
        window!.rootViewController!.view.backgroundColor = .blue
        window!.makeKeyAndVisible()
        return true
    }
}
class User : NSObject,NSCoding{
    var id : Int32
    var name : String
    init(_ id: Int32 ,_ name : String) {
        self.id = id
        self.name = name
    }
    required convenience init?(coder decoder: NSCoder) {
        guard let id = decoder.decodeInt32(forKey: "id") as? Int32,
            let name = decoder.decodeObject(forKey:"name") as? String
            else { return nil }

        self.init(id,name)
    }
    func encode(with coder: NSCoder) {
        coder.encode(self.id, forKey: "id")
        coder.encode(self.name, forKey: "name")
    }
    override public var description: String { return "{id:(id),name:(name)}" }
}
class Page: UIViewController {
    let filename = "/todo.archive"
    override func viewDidLoad() {
        super.viewDidLoad()
        bar()
    }
    func bar(){
        do {
            let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
            let path = "(documentDirectory)(filename)"
            let u = User(1,"user")
            NSKeyedArchiver.archiveRootObject(u, toFile: path)
            let user1 = NSKeyedUnarchiver.unarchiveObject(withFile: path) as? User
            print(user1)
        }catch {print("(error)")}
    }
}複製程式碼

執行後輸出的結果:

Optional({id:1,name:user})複製程式碼

協議NSCoding需要實現兩個方法:

public protocol NSCoding {
    public func encode(with aCoder: NSCoder)
    public init?(coder aDecoder: NSCoder) // NS_DESIGNATED_INITIALIZER
}複製程式碼

方法encode(with:)是從NSCoder內解碼資料,方法init?(coder:)是編碼資料到NSCoder內。大體上函式內的實現就是一個個的用類似的程式碼把當前物件的成員做編碼和解碼。

然後這個物件就可以傳遞給函式:

    NSKeyedArchiver.archiveRootObject複製程式碼

完成歸檔,然後使用:

    NSKeyedUnarchiver.unarchiveObject(withFile: path) as? User複製程式碼

完成反歸檔。

更強大的是,如果物件內有成員指向到其他物件,歸檔類可以把這些指向物件一起歸檔,比如Man類通過成員wife指向一個Woman類,那麼可以連同此成員物件一起做歸檔。方法如下:

import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window : UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        window = UIWindow()
        window!.rootViewController = Page()
        window!.rootViewController!.view.backgroundColor = .blue
        window!.makeKeyAndVisible()
        return true
    }
}
class Man : NSObject,NSCoding{
    var id : Int32
    var name : String
    var wife : Woman
    init(_ id: Int32 ,_ name : String,_ wife : Woman) {
        self.id = id
        self.name = name
        self.wife = wife
    }
    required convenience init?(coder decoder: NSCoder) {
        guard let id = decoder.decodeInt32(forKey: "id") as? Int32,
            let name = decoder.decodeObject(forKey:"name") as? String,
            let todo = decoder.decodeObject(forKey:"wife") as? Woman
            else { return nil }

        self.init(id,name,todo)
    }
    func encode(with coder: NSCoder) {
        coder.encode(self.id, forKey: "id")
        coder.encode(self.name, forKey: "name")
        coder.encode(wife,forKey:"wife")

    }
    override public var description: String { return "Man:{id:(id),name:(name)},(wife)" }
}

class Woman : NSObject,NSCoding{
    var id : Int32
    var name : String
    init(_ id: Int32 ,_ name : String) {
        self.id = id
        self.name = name
    }
    required convenience init?(coder decoder: NSCoder) {
        guard let id = decoder.decodeInt32(forKey: "id") as? Int32,
            let name = decoder.decodeObject(forKey:"name") as? String
            else { return nil }

        self.init(id,name)
    }
    func encode(with coder: NSCoder) {
        coder.encode(self.id, forKey: "id")
        coder.encode(self.name, forKey: "name")
    }
    override public var description: String { return "wife:{id:(id),name:(name)}" }
}

class Page: UIViewController {
    let filename = "/man.archive"
    override func viewDidLoad() {
        super.viewDidLoad()
        bar()
    }
    func bar(){
        do {
            let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
            let path = "(documentDirectory)(filename)"
            let u = Man(1,"Man1",Woman(1,"Wife1"))
            NSKeyedArchiver.archiveRootObject(u, toFile: path)
            let user1 = NSKeyedUnarchiver.unarchiveObject(withFile: path) as? Man
            print(user1)
        }catch {print("(error)")}
    }
}複製程式碼

要點在於,在NSCoder協議需要的兩個函式中,通過NSCoder物件,歸檔和反歸檔此成員物件,並且此成員物件也必須指向NSCoder協議即可。

如果成員物件是一個物件的陣列,做法是類似的。如下案例有兩個類,Department和Task。Department通過成員Tasks包含一個Task的陣列。把Task陣列整體作為一個物件來處理,使用NSCoder.encode函式即可做歸檔,使用NSCoder.decodeObject即可反歸檔:

import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window : UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        window = UIWindow()
        window!.rootViewController = Page()
        window!.rootViewController!.view.backgroundColor = .blue
        window!.makeKeyAndVisible()
        return true
    }
}
class Department: NSObject, NSCoding {
    var name = ""
    var manager = ""

    var tasks: [Task]?

    func encode(with aCoder: NSCoder) {
        aCoder.encode(name, forKey: "name")
        aCoder.encode(manager, forKey: "manager")
        aCoder.encode(tasks, forKey: "taskArray")
    }

    required convenience init?(coder aDecoder: NSCoder){
        self.init()

        name = aDecoder.decodeObject(forKey: "name") as! String
        manager = aDecoder.decodeObject(forKey: "manager") as! String
        tasks = aDecoder.decodeObject(forKey: "taskArray") as? [Task]
    }
    override public var description: String { return "dept:{name:(name),tasks:(tasks)" }
    override init() {
        super.init()
        name = "D1"
        manager = "TJ"
        tasks = []
        tasks?.append(Task("A1","N1"))
        tasks?.append(Task("A2","N2"))
        tasks?.append(Task("A3","N3"))
    }
}

class Task: NSObject, NSCoding {
    var title = ""
    var notes = ""
    override public var description: String { return "{title:(title)}" }
    func encode(with aCoder: NSCoder)  {
        // Methods
        aCoder.encode(title, forKey: "title")
        aCoder.encode(notes, forKey: "notes")
    }

    required convenience init?(coder aDecoder: NSCoder) {
        // Methods

        let title = aDecoder.decodeObject(forKey: "title") as! String
        let notes = aDecoder.decodeObject(forKey: "notes") as! String
        self.init(title,notes)
    }

    init(_ title:String, _ notes : String) {
        self.title = title
        self.notes = notes
        super.init()
    }
}

class Page: UIViewController {
    let filename = "/man.archive"
    override func viewDidLoad() {
        super.viewDidLoad()
        bar()
    }
    func bar(){
        do {
            let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
            let path = "(documentDirectory)(filename)"
            let u = Department()
            NSKeyedArchiver.archiveRootObject(u, toFile: path)
            let user1 = NSKeyedUnarchiver.unarchiveObject(withFile: path) as? Department
            print(user1)
        }catch {print("(error)")}
    }
}複製程式碼

在控制檯上期望輸出的是:

Optional(dept:{name:D1,tasks:Optional([{title:A1}, {title:A2}, {title:A3}]))複製程式碼

出品

Vue.js小書 www.ituring.com.cn/book/1956

相關文章