小编典典

Swift-协议扩展-属性默认值

swift

假设我有以下协议:

protocol Identifiable {
    var id: Int {get}
    var name: String {get}
}

而且我有以下结构:

struct A: Identifiable {
    var id: Int
    var name: String
}

struct B: Identifiable {
    var id: Int
    var name: String
}

如您所见,我必须“遵循”结构A和结构B中的Identifiable协议。但是想象一下,如果我还有N个需要符合该协议的结构……我不想“复制/粘贴”
‘一致性(变量ID:整数,变量名称:字符串)

所以我创建了一个 协议扩展

extension Identifiable {
    var id: Int {
        return 0
    }

    var name: String {
        return "default"
    }
}

现在,通过此扩展,我可以创建符合Identifiable协议的结构,而不必同时实现这两个属性:

struct C: Identifiable {

}

现在的问题是我不能为id属性或name属性设置值:

var c: C = C()
c.id = 12 // Cannot assign to property: 'id' is a get-only property

发生这种情况是因为在可识别协议中,ID和名称仅是可获取的。现在,如果将id和name属性更改为 {get set}, 则会出现以下错误:

类型“ C”不符合协议“可识别”

发生此错误是因为我尚未在协议扩展中实现setter …所以我更改了协议扩展:

extension Identifiable {
    var id: Int {
        get {
            return 0
        }

        set {

        }
    }

    var name: String {
        get {
            return "default"
        }

        set {

        }
    }
}

现在错误消失了,但是如果我将新值设置为id或name,它将获得默认值(getter)。当然, 二传手是空的

我的问题是: 我必须在设置器中放入哪段代码? 因为如果我添加 self.id = newValue, 它会崩溃(递归)。

提前致谢。


阅读 900

收藏
2020-07-07

共1个答案

小编典典

似乎您想stored property通过协议扩展将a添加到类型中。但是,这是不可能的,因为扩展不能添加存储的属性。

我可以向您展示一些替代方案。

子类化(面向对象编程)

最简单的方法(可能您已经想像到了)是使用类而不是结构。

class IdentifiableBase {
    var id = 0
    var name = "default"
}

class A: IdentifiableBase { }

let a = A()
a.name  = "test"
print(a.name) // test

缺点:在这种情况下,您的A类需要继承,IdentifiableBase并且由于在Swift中没有多重继承,因此这将是A唯一可以继承的类。

组件(面向协议的编程)

该技术在游戏开发中非常流行

struct IdentifiableComponent {
    var id = 0
    var name = "default"
}

protocol HasIdentifiableComponent {
    var identifiableComponent: IdentifiableComponent { get set }
}

protocol Identifiable: HasIdentifiableComponent { }

extension Identifiable {
    var id: Int {
        get { return identifiableComponent.id }
        set { identifiableComponent.id = newValue }
    }
    var name: String {
        get { return identifiableComponent.name }
        set { identifiableComponent.name = newValue }
    }
}

现在您可以使您的类型符合Identifiable简单的书写方式

struct A: Identifiable {
    var identifiableComponent = IdentifiableComponent()
}

测试

var a = A()
a.identifiableComponent.name = "test"
print(a.identifiableComponent.name) // test
2020-07-07