iOS - Cell数据归档与反归档

19 篇文章 2 订阅

待实现需求: 加载Cell先使用本地数据优化用户体验,再请求网络数据重新加载。

归档

归档操作

我们需要将加载App网络请求的数据归档实时写入沙盒中保存。
iOS沙盒机制可以查看此文章
我们这里将网络数据写入到Cache目录下Data目录下的list文件。

  1. 使用NSSearchPathForDirectoriesInDomains取出沙盒路径。
  2. 根据沙盒路径创建需创建文件路径。
  3. 使用FileManager文件管理器创建相应的目录和文件。
  4. 将对象(这里是类型数组)序列化成Data
  5. 将data归档写入文件。

这里有一个概念:序列化

自定义对象是无法写入到文件的,必须转换成二进制流的格式。从对象到二进制数据的过程我们称为序列化,将二进制数据还原成对象的过程,我们称为反序列化。iOS中对象序列化需要实现NSCoding类协议,实现序列化与反序列化方法。

@protocol NSCoding

- (void)encodeWithCoder:(NSCoder *)coder;
- (nullable instancetype)initWithCoder:(NSCoder *)coder; // NS_DESIGNATED_INITIALIZER

@end

我们这里实现NSScureCoding,更加安全。

mport UIKit

// 新闻Model,实现NSSecureCoding协议(序列化与反序列化)
public class NewsModel: NSObject, NSSecureCoding {
    
    public static var supportsSecureCoding: Bool = true
    
    // Coder 转 Obj
    public required init?(coder: NSCoder) {
        author_name = coder.decodeObject(forKey: "author_name") as! String
        category = coder.decodeObject(forKey: "category") as! String
        date = coder.decodeObject(forKey: "date") as! String
        thumbnail_pic_s = coder.decodeObject(forKey: "thumbnail_pic_s") as! String
        title = coder.decodeObject(forKey: "title") as! String
        uniquekey = coder.decodeObject(forKey: "uniquekey") as! String
        url = coder.decodeObject(forKey: "url") as! String
    }
    
    public var author_name: String
    public var category: String
    public var date: String
    public var thumbnail_pic_s: String
    public var title: String
    public var uniquekey: String
    public var url: String
    
    init(author_name: String, category: String, date: String, thumbnail_pic_s: String, title: String, uniquekey: String, url: String) {
        self.author_name = author_name
        self.category = category
        self.date = date
        self.thumbnail_pic_s = thumbnail_pic_s
        self.title = title
        self.uniquekey = uniquekey
        self.url = url
    }
    
    // Obj 转 Coder
    public func encode(with coder: NSCoder) {
        coder.encode(author_name, forKey: "author_name")
        coder.encode(category, forKey: "category")
        coder.encode(date, forKey: "date")
        coder.encode(thumbnail_pic_s, forKey: "thumbnail_pic_s")
        coder.encode(title, forKey: "title")
        coder.encode(uniquekey, forKey: "uniquekey")
        coder.encode(url, forKey: "url")
    }
}

实现好Model的协议方法编写后,我们便能轻松将对象序列化成二进制数据。

	/// 数据归档
    /// - Parameter data: 网络数据Model数组
    private func archiveData(data: [NewsModel]) {
        let cacheArray = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)
        if let cachePath = cacheArray.first {
            let fileManager = FileManager.default
            let dataPath = cachePath.appending("/Data/")
            do {
                try fileManager.createDirectory(atPath: dataPath, withIntermediateDirectories: true, attributes: nil)
                let listDataPath = dataPath.appending("list")
                do {
                    // Obj 转 Data
                    let listData = try NSKeyedArchiver.archivedData(withRootObject: data, requiringSecureCoding: true)
                    fileManager.createFile(atPath: listDataPath, contents: listData, attributes: nil)
                }
            } catch {
                print("error")
            }
        }
    }

归档时机

我们应该是在获取新的数据后去重写本地文件数据,这里指的是网络请求成功后重写本地数据。

/// 网络加载获取数据
    /// - Parameter block: 数据更新闭包
    public func loadNetWork(block: @escaping ((Bool, Array<NewsModel>) -> Void)) {
        
        // 本地数据加载优先于网络加载,优化用户体验
        if let localData = loadDataFromLocal() {
            block(true, localData)
        }
        
        // 网络加载
        let urlString = "http://v.juhe.cn/toutiao/index?type=top&key=97ad001bfcc2082e2eeaf798bad3d54e"
        let url = URL(string: urlString)!
        let session = URLSession.shared
        let task = session.dataTask(with: url) { [self] (data, response, error) in
            guard error == nil else {
                fatalError("Task Error")
            }
            guard let jsonData = data else {
                fatalError("Data Error")
            }
            do {
                // Data 转 JSON
                guard let dic = try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) as? [String : Any] else {
                    fatalError("JSONSerialization Error")
                }
                guard let resultDic = dic["result"] as? [String : Any] else {
                    fatalError("JSONSerialization Error")
                }
                guard let dataArray = resultDic["data"] as? [Any] else {
                    fatalError("JSONSerialization Error")
                }
                var modelArray = [NewsModel]()
                for item in dataArray {
                    let dic = item as! [String: String]
                    let model = NewsModel(author_name: dic["author_name"]!, category: dic["category"]!, date: dic["date"]!, thumbnail_pic_s: dic["thumbnail_pic_s"]!, title: dic["title"]!, uniquekey: dic["uniquekey"]!, url: dic["url"]!)
                    modelArray.append(model)
                }
                archiveData(data: modelArray)
                // 主线程刷新
                DispatchQueue.main.async {
                    block(true, modelArray)
                }
            } catch {
                // error
            }
        }
        task.resume()
    }

在网络数据成功转为对象数组时,我们调用archiveData(data:)方法,来达到效果。

反归档

反归档操作

将本地文件数据转成对象数组。也就是文件数据反序列化转成对象数组。

/// 本地数据加载
    /// - Returns: 本地数据Model数组
    private func loadDataFromLocal() -> [NewsModel]? {
        let cacheArray = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)
        if let cachePath = cacheArray.first {
            // 创建文件管理器
            let fileManager = FileManager.default
            // 文件路径
            let listDataPath = cachePath.appending("/Data/list")
            // 文件数据加载为Data
            if let readListData = fileManager.contents(atPath: listDataPath) {
                // Data 转 Obj
                if let listDataArray = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NewsModel.self, NSArray.self], from: readListData) as? [NewsModel] {
                    return listDataArray
                }
            }
        }
        return nil
    }

反归档时机

我们在网络请求之前,使用本地数据优先加载Cell,优化用户体验。而不是在网络数据请求成功请一直以白色的Cell展示。

public func loadNetWork(block: @escaping ((Bool, Array<NewsModel>) -> Void)) {
        
        // 本地数据加载优先于网络加载,优化用户体验
        if let localData = loadDataFromLocal() {
            block(true, localData)
        }
        
        // 网络加载
        ...
        }
}

总结

通过这次案例,我们利用沙盒序列化与反序列化的知识实现了基础的本地数据存储,这是一种简单基础的数据存储方案。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值