一个协议关联类型实践

需求: 从 plist 文件中读取数据并解码成模型对象使用, 模型定义协议如下:

protocol WhatsNewProtocol {
    var icon: UIImage { get }
    var title: String { get }
    var shortDescription: String { get }
    var fullDescription: String { get }
    var video: Any { get }
}
 
protocol WorkspaceReleaseProtocol {
    var version: String { get }
    var date: Date { get }
    var includes: [WhatsNewProtocol] { get }
}
 
protocol WhatsNewManagerProtocol {
    func getWorkspaceReleases -> [WorkspaceReleaseProtocol]
}

Swift 中不支持协议嵌套, 因此使用关联类型实现.
同时注意到 WhatsNewProtocol 中定义的 icon 是 UIImage 类型.

import UIKit

protocol WhatsNewProtocol {
    var icon: UIImage? { mutating get }
    var video: Any? { get }
    var tittle: String { get }
    var shortDescription: String { get }
    var fullDescription: String { get }
}

protocol WorkspaceReleaseProtocol {
    associatedtype WhatsNew: WhatsNewProtocol
    var version: String { get }
    var date: Date { get }
    var includes: [WhatsNew] { get }
}

protocol WhatsNewManagerProtocol {
    associatedtype WorkspaceRelease: WorkspaceReleaseProtocol
    func getWorkspaceReleases() -> [WorkspaceRelease]
}

1 使用关联类型表示协议中使用的需要遵守其他协议的对象;
2 因为 UIImage 的加载需要耗费一定的内存资源, 因此提供可以更改的 get 方法;

import UIKit

struct WhatsNewItem: WhatsNewProtocol, Codable {
    
    lazy var icon: UIImage? = {
        return UIImage(named: iconUrl)
    }()
    
    var video: Any? {
        return videoUrl as Any
    }
    
    var iconUrl: String
    var videoUrl: String
    var tittle: String
    var shortDescription: String
    var fullDescription: String
    
    enum CodingKeys: CodingKey {
        case iconUrl
        case videoUrl
        case tittle
        case shortDescription
        case fullDescription
    }
}

struct WorkspaceReleaseItem: WorkspaceReleaseProtocol, Codable {
    typealias WhatsNew = WhatsNewItem
    var version: String
    var date: Date
    var includes: [WhatsNewItem]
}

在 struct WhatsNewItem 中提供对 icon 的懒加载属性, 尽量减少内存占用时间. 因为可能以后支持从模型写入 plist 文件, 因此提供支持的 iconUrl 写入到 plist 中, 提供自定义的 CodingKeys 枚举.

因为对 WhatsNew 数据的操作 WhatsNewManager 是一个单例, 需要考虑线程安全, 此时使用一个自定义 DispatchQueue 队列让所有操作同步分发.

class WhatsNewManager: WhatsNewManagerProtocol {

    typealias WorkspaceRelease = WorkspaceReleaseItem
    private let queue = DispatchQueue.init(label: "WhatsNewManager")
    
    func getWorkspaceReleases() -> [WorkspaceReleaseItem] {
        var releaseItems: [WorkspaceReleaseItem]? = [WorkspaceReleaseItem]()
        queue.sync {
            guard let filePath = Bundle.main.path(forResource: "WhatsNew", ofType: "plist"),
                  let versionsData = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else { return }
            releaseItems = try? PropertyListDecoder().decode([WorkspaceReleaseItem].self, from: versionsData)
        }
        return releaseItems ?? []
    }
}

1 将任务同步分发到自定义的串行队列中是否会造成死锁? 不会, 因为分发任务时是在主线程, 同时执行的线程也是在主线程.
2 在主线程中将任务同步分发到 main 队列, 是否造成死锁? 是. 区别在于是否阻塞当前线程.
3 将任务异步分发到自定义串行队列中: 创建一个新的线程, 数据返回时机不确定.
4 将 queue.sync {} return []; 换成 queue.async {} return []; 有什么不同: 异步分发时, 任务可能没有结束, 函数就返回了.
5 queue.async {}; print("test") 顺序: 不确定;

开发要注意线程安全!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,647评论 8 265
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,148评论 1 32
  • OC语言基础 1.类与对象 类方法 OC的类方法只有2种:静态方法和实例方法两种 在OC中,只要方法声明在@int...
    奇异果好补阅读 4,358评论 0 11
  • 面试题参考1 : 面试题[http://www.cocoachina.com/ios/20150803/12872...
    江河_ios阅读 1,761评论 0 4
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,217评论 30 472