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

Axo*_*ort 55 ios swift protocol-oriented

假设我有以下协议:

protocol Identifiable {
    var id: Int {get}
    var name: String {get}
}
Run Code Online (Sandbox Code Playgroud)

而且我有以下结构:

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

struct B: Identifiable {
    var id: Int
    var name: String
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我必须"符合"结构A和结构B中的可识别协议.但想象一下,如果我有N个结构需要符合这个协议......我不想'复制/粘贴' '一致性(var id:Int,var name:String)

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

extension Identifiable {
    var id: Int {
        return 0
    }

    var name: String {
        return "default"
    }
}
Run Code Online (Sandbox Code Playgroud)

现在有了这个扩展,我可以创建一个符合Identifiable协议的结构,而不必实现这两个属性:

struct C: Identifiable {

}
Run Code Online (Sandbox Code Playgroud)

现在问题是我无法为id属性或name属性设置值:

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

发生这种情况是因为在可识别协议中,id和name只能获取.现在,如果我将id和name属性更改为{get set},我会收到以下错误:

类型'C'不符合协议'可识别'

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

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

        set {

        }
    }

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

        set {

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

我的问题是: 我必须在setter中添加哪些代码?因为如果我添加self.id = newValue,它会崩溃(递归).

提前致谢.

Luc*_*tti 74

看来你想stored property通过协议扩展添加一个类型.但是这是不可能的,因为使用扩展名无法添加存储的属性.

我可以告诉你几个选择.

子类化(面向对象编程)

最简单的方法(可能是你已经想象过的)是使用类而不是结构.

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

class A: IdentifiableBase { }

let a = A()
a.name  = "test"
print(a.name) // test
Run Code Online (Sandbox Code Playgroud)

缺点:在这种情况下,你的A类需要继承IdentifiableBase,因为在Swift中,theres不是多重继承,这将是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 }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你可以让你的类型符合Identifiable简单的写作

struct A: Identifiable {
    var identifiableComponent = IdentifiableComponent()
}
Run Code Online (Sandbox Code Playgroud)

测试

var a = A()
a.identifiableComponent.name = "test"
print(a.identifiableComponent.name) // test
Run Code Online (Sandbox Code Playgroud)

  • 为什么你有HasIdentifiableComponent协议?为什么不做`协议可识别:var identifiableComponent:IdentifiableComponent {get set}`然后`扩展名可识别{var id:Int {get {return identifiableComponent.id} set {identifiableComponent.id = newValue}} etc ...} (10认同)
  • @BohdanSavych仅仅因为我想将`HasIdentifiableComponent`的概念与`Identifiable`的概念分开.如果您不喜欢这种分离,请随意将所有内容分组到一个协议中. (2认同)

小智 6

Objective-C 关联对象

您可以使用Objective-C关联对象基本上将 a 添加stored property到 aclassprotocol请注意,关联对象仅适用于类对象。

import ObjectiveC.runtime

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

var IdentifiableIdKey   = "kIdentifiableIdKey"
var IdentifiableNameKey = "kIdentifiableNameKey"

extension Identifiable {
    var id: Int {
        get { 
            return (objc_getAssociatedObject(self, &IdentifiableIdKey) as? Int) ?? 0
        }
        set {
            objc_setAssociatedObject(self, &IdentifiableIdKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }   
    }

    var name: String {
        get { 
            return (objc_getAssociatedObject(self, &IdentifiableNameKey) as? String) ?? "default"
        }
        set {
            objc_setAssociatedObject(self, &IdentifiableNameKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }   
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您只需编写即可使您class符合Identifiable

class A: Identifiable {

}
Run Code Online (Sandbox Code Playgroud)

测试

var a = A()
print(a.id)    // 0
print(a.name)  // default
a.id = 5
a.name = "changed"
print(a.id)    // 5
print(a.name)  // changed

Run Code Online (Sandbox Code Playgroud)