【问题标题】:Swift: How to make Generics code more cleanSwift:如何使泛型代码更干净
【发布时间】:2021-07-11 11:22:56
【问题描述】:

我正在寻找使我的代码更干净、更高效的方法。 我有一个“BaseInfo”,它有一些属性,也是“SomeInfo”和“AnotherInfo”类的父类,它们有自己的附加属性。我制作了一个通用函数,可以从 UserDefaults 中保存和获取这些对象。考虑到它应该使用的信息类型,我有使用这些函数保存和加载信息的 ViewController。我想知道有什么方法可以让我的代码更干净,并在我的 ViewContoller 中摆脱那些类型转换。这是我的信息类:

public class  BaseInfo: Codable{
    var name: String?
    
    init(_ dict: [String: Any]){
        
        name = dict["name"] as? String
    }
}

class AnotherInfo: BaseInfo {
    var secondName: String?
    
    override init(_ dict: [String : Any]) {
        super.init(dict)
        secondName = dict["secondName"] as? String
    }
    
    required init(from decoder: Decoder) throws {
        fatalError("init(from:) has not been implemented")
    }
}

class SomeInfo: BaseInfo {
    var middleName: String?
    
    
    override init(_ dict: [String : Any]) {
        super.init(dict)
        middleName = dict["middleName"]
    }
    
    required init(from decoder: Decoder) throws {
        fatalError("init(from:) has not been implemented")
    }
}

这是我管理信息的班级

protocol InfoManagerProtocol {
    static func getInfo(with type: InfoType) -> BaseInfo?
    static func saveInfo <T: BaseInfo>(with type: InfoType, info: T)
}

class InfoManager: InfoManagerProtocol {
    
    static func getInfo<T: BaseInfo>(with type: InfoType) -> T? {
        if let data = UserDefaults.standard.object(forKey: type.toString()) as? Data, let info = try? JSONDecoder().decode(type.typeOfInfo() as! T.Type, from: data){
            return info
        }
        return nil
    }
    
    
    static func saveInfo<T>(with type: InfoType, info: T) where T : BaseInfo {
        if let encodedData = try? JSONEncoder().encode(info){
            UserDefaults.standard.setValue(encodedData, forKey: type.toString())
        }
    }
}

InfoType 枚举:

enum InfoType: Int{
    case base = 1
    case another = 2
    case some = 3
}

extension InfoType{

    func toString() -> String{
      switch self{
       case .base: 
           return "base"
       case .another: 
           return "another"
       case .some:
           return "some"
      }
    } 
    func typeOfInfo() -> BaseInfo.Type{
        switch self{
        case .base:
            return BaseInfo.self
        case .another:
            return AnotherInfo.self
        case .some:
            return SomeInfo.self
        }
    }
}

还有一些控制器

class ViewCV: UIViewController{
    ......
    // some outlets
    var infoType: InfoType? // info 

    // some func that updates UI
    func updateUI(){
       
       let genericInfo = InfoManager.getInfo(info: .infoType)
       self.someNameOutletLabel.text = genericInfo.name
       self.secondNameOutletLabel?.text = (genericInfo as? AnotherInfo).secondName // here I don't like it
       if let someInfo = genericInfo as? SomeInfo{
          self.secondNameOutletLabel?.text = someInfo.thirdName // or like here I also don't like
       }
   }
}

期待其他批评和建议

【问题讨论】:

    标签: ios swift generics types type-alias


    【解决方案1】:

    您可以通过跳过InfoType 类型来简化您的代码,而是从给定的参数或返回值中定义所使用的类型。

    所以协议变成了

    protocol InfoManagerProtocol {
        static func getInfo<T: BaseInfo>() -> T?
        static func saveInfo <T: BaseInfo>(info: T)
    }
    

    然后执行。请注意,我现在使用类名本身而不是枚举中的 toString 作为键

    class InfoManager: InfoManagerProtocol {
        static func getInfo<T: BaseInfo>() -> T? {
            if let data = UserDefaults.standard.object(forKey: "\(T.self)") as? Data, let info = try? JSONDecoder().decode(T.self, from: data){
                return info
            }
            return nil
        }
    
        static func saveInfo<T>(info: T) where T : BaseInfo {
            if let encodedData = try? JSONEncoder().encode(info){
                print(encodedData)
                UserDefaults.standard.set(encodedData, forKey: "\(T.self)")
            }
        }
    }
    

    这里是一个如何使用它的例子(假设init(from:)已经正确实现了)

    let dict: [String: String] = ["name": "Joe", "secondName": "Doe", "middleName": "Jr"]
    let another = AnotherInfo(dict)
    let some = SomeInfo(dict)
    
    //Here the declaration of the argument tells the generic function what T is
    InfoManager.saveInfo(info: another)
    InfoManager.saveInfo(info: some)
    
    //And here the declaration of the return value tells the generic function what T is
    if let stored:AnotherInfo = InfoManager.getInfo() {
        print(stored, type(of: stored))
    }
    if let stored:SomeInfo = InfoManager.getInfo() {
        print(stored, type(of: stored))
    }
    

    【讨论】:

    • 不要使用setValue,你应该使用func set(_ value: Any?, forKey defaultName: String)。顺便说一句,UserDefaults 有一个特定的方法来获取名为 data(forKey:) 的数据
    • 最好让 InfoManager 方法抛出而不是静默失败
    • @JoakimDanielson set 而不是 setValue 绝对不仅仅是一种改进。如果您愿意,请随时保持原样。它甚至没有在 Apples 文档中列出用于设置 UserDefaults 值 UserDefaults
    猜你喜欢
    • 1970-01-01
    • 2013-12-27
    • 2022-08-13
    • 1970-01-01
    • 2020-05-23
    • 2016-05-27
    • 1970-01-01
    • 1970-01-01
    • 2021-04-20
    相关资源
    最近更新 更多