协议中的默认初始化程序需要不需要的可变属性

Posted

技术标签:

【中文标题】协议中的默认初始化程序需要不需要的可变属性【英文标题】:Default initialiser in protocol requires unwanted mutable properties 【发布时间】:2017-03-29 14:23:42 【问题描述】:

在 Swift 3 中: 想象一下,您希望您的模型在整个应用程序中都是值类型(结构)。但是您也希望使用 Core Data/Realm 持久化所述模型,这需要您创建类。然后,您可以使用 JSON 将结构体转换为类,反之亦然(这需要结构体和类都支持 JSON 反序列化和序列化)。

如果您不必在两个地方编写 JSON 反序列化(类似地用于序列化,但我在这里专注于反序列化),那不是很整洁,而是在 协议中使用 put 反序列化,你的结构和类都使用。

使用结构,我们希望我们的 JSON 模型具有不可变字段,因此所有属性都是 let 常量。但是使用 protocol 的反序列化实现不允许这种 AFAIK。

下面的代码示例可以运行,但它很难看,因为代码中的 cmets 中标记了所有不需要的要求 (UR)。

struct JSON 
    let json: [String: Any]
    func value<Value>(_ key: String) throws -> Value 
        guard let value = json[key] as? Value else  throw NSError() 
        return value
    


protocol JSONDeserializable 
    init(json: JSON) throws


protocol UserModel: JSONDeserializable 
    var username: String  get set  // Unwanted requirement (UR) #1: property needs "set" so that it can be initialized within protocol
    init() // UR2: needs empty init, because of default implementation of `init(json: JSON)` in `extension UserModel`


extension UserModel 
    init(json: JSON) throws 
        self.init() // UR3: Needs to call this otherwise compilation error: `'self' used before chaining to another self.init requirement`
        username = try json.value("username")
    


struct UserStruct: UserModel 
    // UR4: property cannot be `let`, beause of `set` in protocol.
    var username: String = "" // UR5: Property have to have default value because of it being a empty init
    init() 


final class UserClass: NSObject, UserModel 
    // UR6: analogue with UR4
    var username: String = "" // UR7: analogue with UR5


let json: JSON = JSON(json: ["username": "Sajjon"])
let u1 = try UserStruct(json: json)
let u2 = try UserClass(json: json)
print(u1.username) // prints "Sajjon"
print(u2.username) // prints "Sajjon"

是否有其他方法可以实现这一目标,同时减少不必要的需求? 或者是零 UR 的最佳解决方案? ????

【问题讨论】:

只需在UserModel 中有一个init(username: String) 要求,而不是init() 感谢您的建议,我更新了@Hamish 的问题,它使代码重复... 使用structs 的好处是在这种情况下你可以只依赖默认的成员初始化器:) 所以你可以删除UserStruct 的初始化器。 你是绝对正确的@Hamish!这实际上还不错。 @Sajjon 您可以将您的编辑发布为答案并将其标记为解决方案,这将帮助其他有同样问题的用户注意到您解决了问题;) 【参考方案1】:

感谢@hamish 所指出的,最好的解决方案是(struct JSONprotocol JSONDeserializable 与问题中的相同)。这不是一个完美的解决方案,因为您必须实现类的初始化程序。简洁的部分是您不必为该结构实现任何初始化程序,因为它隐含地有一个。

protocol UserModel: JSONDeserializable 
    var username: String  get 
    var firstname: String  get 
    var country: String  get 
    init(
        username: String,
        firstname: String,
        country: String
    )


extension UserModel 
    init(json: JSON) throws 
        self.init(
            username: try json.value("username"),
            firstname: try json.value("firstname"),
            country: try json.value("country")
        )
    


struct UserStruct: UserModel 
    let username: String
    let firstname: String
    let country: String
    // struct has default initializer


final class UserClass: UserModel 
    let username: String
    let firstname: String
    let country: String
    init(
        username: String,
        firstname: String,
        country: String
        ) 
        self.username = username
        self.firstname = firstname
        self.country = country
    


let json: JSON = JSON(json: [
    "username": "Sajjon",
    "firstname": "Alexander",
    "country": "Sweden"
    ])
let u1 = try UserStruct(json: json)
let u2 = try UserClass(json: json)
print(u1.username) // prints "Sajjon"
print(u2.username) // prints "Sajjon"

【讨论】:

以上是关于协议中的默认初始化程序需要不需要的可变属性的主要内容,如果未能解决你的问题,请参考以下文章

对类(class)中的已有属性进行修改方法1

python函数默认参数为可变对象的理解

java中的不可变类型的探究

Java程序员需掌握常见面试题

java集合:ArrayList

NSManagedObject 子类的 Swift 链接器错误实现具有所需初始化的协议