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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/38885622/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 09:54:03  来源:igfitidea点击:

Swift - Protocol extensions - Property default values

iosswiftprotocol-oriented

提问by Axort

Let's say that I have the following protocol:

假设我有以下协议:

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

And that I have the following structs:

而且我有以下结构:

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

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

As you can see, I had to 'conform' to the Identifiable protocol in struct A and struct B. But imagine if I had N more structs that needs to conform to this protocol... I don't want to 'copy/paste' the conformance (var id: Int, var name: String)

如您所见,我必须“符合”结构 A 和结构 B 中的可识别协议。但是想象一下,如果我还有 N 个需要符合该协议的结构......我不想“复制/粘贴” ' 一致性(var id: Int, var name: String)

So I create a protocol extension:

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

extension Identifiable {
    var id: Int {
        return 0
    }

    var name: String {
        return "default"
    }
}

With this extension now I can create a struct that conforms to the Identifiable protocol without having to implement both properties:

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

struct C: Identifiable {

}

Now the problem is that I can't set a value to the id property or the name property:

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

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

This happens because in the Identifiable protocol, id and name are only gettable. Now if I change the id and name properties to {get set}I get the following error:

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

Type 'C' does not conform to protocol 'Identifiable'

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

This error happens because I haven't implemented a setter in the protocol extension... So I change the protocol extension:

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

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

        set {

        }
    }

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

        set {

        }
    }
}

Now the error goes away but if I set a new value to id or name, it gets the default value (getter). Of course, the setter is empty.

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

My question is: What piece of code do I have to put inside the setter?Because if I add self.id = newValueit crashes (recursive).

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

Thanks in advance.

提前致谢。

回答by Luca Angeletti

It seems you want to add a stored propertyto a type via protocol extension. However this is not possible because with extensions you cannot add a stored property.

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

I can show you a couple of alternatives.

我可以向您展示几个替代方案。

Subclassing (Object Oriented Programming)

子类化(面向对象编程)

The easiest way (as probably you already imagine) is using classes instead of structs.

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

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

class A: IdentifiableBase { }

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

Cons: In this case your A class needs to inherit from IdentifiableBaseand since in Swift theres is not multiple inheritance this will be the only class A will be able to inherit from.

缺点:在这种情况下,你的 A 类需要继承IdentifiableBase,因为在 Swift 中没有多重继承,这将是 A 唯一能够继承的类。

Components (Protocol Oriented Programming)

组件(面向协议的编程)

This technique is pretty popular in game development

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

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 }
    }
}

Now you can make your type conform to Identifiablesimply writing

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

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

Test

测试

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

回答by Nick

Protocols and protocol extensions are very powerful, but they tend to be most useful for read-only properties and functions.

协议和协议扩展非常强大,但它们往往对只读属性和函数最有用。

for what you're trying to accomplish (stored properties with a default value), classes and inheritance might actually be the more elegant solution

对于您要完成的任务(具有默认值的存储属性),类和继承实际上可能是更优雅的解决方案

something like:

就像是:

class Identifiable {
    var id: Int = 0
    var name: String = "default"
}

class A:Identifiable {
}

class B:Identifiable {
}

let a = A()

print("\(a.id) \(a.name)")

a.id = 42
a.name = "foo"

print("\(a.id) \(a.name)")

回答by Harsh

This is why you were not able to set the properties.

这就是您无法设置属性的原因。

The property becomes a computed property which means it does not have a backing variable such as _x as it would in ObjC. In the solution code below you can see the xTimesTwo does not store anything, but simply computes the result from x.

该属性成为计算属性,这意味着它没有像在 ObjC 中那样的支持变量,例如 _x。在下面的解决方案代码中,您可以看到 xTimesTwo 不存储任何内容,而只是从 x 计算结果。

See Official docs on computed properties.

请参阅有关计算属性的官方文档。

The functionality you want might also be Property Observers.

您想要的功能也可能是属性观察器。

Setters/getters are different than they were in Objective-C.

Setter/getter 与它们在 Objective-C 中的不同。

What you need is:

你需要的是:

var x:Int

var xTimesTwo:Int {
    set {
       x = newValue / 2
    }
    get {
        return x * 2
    }
}

You can modify other properties within the setter/getters, which is what they are meant for

您可以修改 setter/getter 中的其他属性,这就是它们的用途

回答by DominicMDev

Objective-C Associated Objects

Objective-C 关联对象

You can use Objective-Cassociated objects to basically add a stored propertyto a classor protocol. Note that associated objects only work for class objects.

您可以使用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)
        }   
    }
}

Now you can make your classconform to Identifiableby simply writing

现在您可以通过简单地编写来使您class符合Identifiable

class A: Identifiable {

}

Test

测试

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