tripleCC的技术博客

ʕ•̫͡•ʔ-̫͡-ʕ•͓͡•ʔ-̫͡-ʕ•̫͡•ʔ-̫͡-ʕ•͓͡•ʔ-̫͡-ʔ

在Swift实现Struct归档

1
2
3
4
struct AboutMe {
    var detail: String
    var links: [[String : String]]
}

在Swift中,Struct类型是无法进行归档操作的,只有继承自NSObject并且遵守了NSCoding协议的类才可以进行相应的归档操作。也就是将上面结构体改成类:

1
2
3
4
5
6
7
8
9
10
11
12
class AboutMe: NSObject, NSCoding {
    var detail: String
    var links: [[String : String]]
    required init?(coder aDecoder: NSCoder) {
        aDecoder.decodeObjectForKey("detail")
        aDecoder.decodeObjectForKey("links")
    }
    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(detail)
        aCoder.encodeObject(links)
    }
}

但是如果要对Struct进行归档,可以转换思维,使用按照以下步骤实现。

实现一个归档、解档协议:
1
2
3
4
public protocol Archivable {
    func archive() -> NSDictionary
    init?(unarchive: NSDictionary?)
}

因为NSKeyedArchiver可以直接对Foundation类进行操作,所以可以将结构体中的属性都转换成字典,然后进行后续操作;archive函数返回一个归档好的字典,而可失败构造函数传入一个需要解档的字典。

让AboutMe遵守并实现以上声明的协议
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extension AboutMe: Archivable{
    func archive() -> NSDictionary {
        return ["detail" : detail, "links" : links]
    }

    init?(unarchive: NSDictionary?) {
        guard let values = unarchive else { return nil }
        if let detail = values["detail"] as? String,
            links = values["links"] as? [[String : String]] {
                self.detail = detail
                self.links = links
        } else {
            return nil
        }
    }
}

这里使用扩展进行归解档方法的添加,可以看到,原先结构体的属性在接口上都是以字典的形势在传输。

实现归解档函数
1
2
3
4
5
6
7
public func unarchiveObjectWithFile<T: Archivable>(path: String) -> T? {
    return T(unarchive: NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? NSDictionary)
}

public func archiveObject<T: Archivable>(object: T, toFile path: String) {
    NSKeyedArchiver.archiveRootObject(object.archive(), toFile: path)
}

对AboutMe进行字典化后,NSKeyedArchiver可以直接对其进行操作,所以这个实现并不复杂。

完成以上步骤,就可以对Struct进行归档和接档操作了:

1
2
3
4
5
let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomainMask.UserDomainMask, true).first! + "/Person"
let aboutMe = AboutMe(detail: "tripleCC", links: [["github" : "https://github.com/tripleCC"], ["gitblog" : "http://triplecc.github.io/"]])
archiveObject(aboutMe, toFile: path)
let unAboutMe: AboutMe? = unarchiveObjectWithFile(path)
debugPrint(unAboutMe)

如果要进行集合操作,可以添加以下函数:

1
2
3
4
5
6
7
8
9
public func archiveObjectLists<T: Archivable>(lists: [T], toFile path: String) {
    let encodedLists = lists.map{ $0.archive() }
    NSKeyedArchiver.archiveRootObject(encodedLists, toFile: path)
}

public func unarchiveObjectListsWithFile<T: Archivable>(path: String) -> [T]? {
    guard let decodedLists = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? [NSDictionary] else { return nil }
    return decodedLists.flatMap{ T(unarchive: $0) }
}

参考博客

1.NSCoding And Swift Structs
2.Property Lists And User Defaults in Swift

Comments