无法分配给属性:<enum:case>不可设置

时间:2018-03-26 21:43:13

标签: swift enums

例如我们有简单的枚举

public enum CXActionSheetToolBarButtonItem {
    case cancel
    case done
    case now

    private static var titles: [CXActionSheetToolBarButtonItem: String] = [
        .cancel: "Cancel",
        .done: "Done",
        .now: "Now",
    ]

    public var title: String  {
        get { return CXActionSheetToolBarButtonItem.titles[self] ?? String(describing: self) }
        // what am I want to do
        set(value) { CXActionSheetToolBarButtonItem.titles[self] = value }
    }

    // what am I forced to do
    public static func setTitle(_ title: String, for item: CXActionSheetToolBarButtonItem) {
        CXActionSheetToolBarButtonItem.titles[item] = title
    }
}

为什么我不能像这样设置新标题

CXActionSheetToolBarButtonItem.cancel.title = "asd"

编译器响应错误 - 无法分配属性:'取消'不可设置,但我可以用功能设置标题

CXActionSheetToolBarButtonItem.setTitle("asd", for: .cancel)

我应该如何改变我的var作为可设置的? .cancel.title = "asd"

3 个答案:

答案 0 :(得分:6)

使用枚举似乎不合适,但我会以面值来解决问题。您需要将您的setter标记为nonmutating,以便可以在枚举的非var实例上调用它:

public enum CXActionSheetToolBarButtonItem {

    // ...

    public var title: String  {
        get { return CXActionSheetToolBarButtonItem.titles[self] ?? String(describing: self) }
        nonmutating set(value) { CXActionSheetToolBarButtonItem.titles[self] = value }
    }
}

CXActionSheetToolBarButtonItem.cancel.title = "foo" // Works... but why would you want this?!

答案 1 :(得分:0)

我认为你不能这样做的主要原因是因为Swift编译器在默认情况下将枚举案例视为不可变的(类似于用struct声明的不可变let),这里你是试图改变它。

如果您尝试将变异函数添加到此枚举

,这是一个很好的方法
mutating public func setTitle2(_ newValue: String) {
    CXActionSheetToolBarButtonItem.titles[self] = newValue
}

然后您将收到错误消息

error: cannot use mutating member on immutable value: 'cancel' returns immutable value

如何解决此问题

获得类似行为的一种方法是将此枚举更改为一组静态变量(这与您尝试实现的更加一致)。

public struct CXActionSheetToolBarButtonItem {

    var title: String

    static var cancel = CXActionSheetToolBarButtonItem(title: "Cancel")
    static var done = CXActionSheetToolBarButtonItem(title: "Done")
    static var now = CXActionSheetToolBarButtonItem(title: "Now")

}

现在您可以使用以下

CXActionSheetToolBarButtonItem.cancel.title = "asd"

此外,您仍然可以使用点语法

let buttonItem: CXActionSheetToolBarButtonItem = .cancel // it works

希望有所帮助!

TD; DR:因为enum是不可变对象。

答案 2 :(得分:0)

首先:无论你想在这里做什么 - 它肯定不是一个好方法而且非常危险。正如克雷格在评论中指出的那样,你正在搞乱实例属性和静态属性。您可以拥有枚举的多个实例 - 但是当您想要更改特定实例的标题时,您还可以更改所有其他实例的标题。这是意想不到的行为,您应该考虑另一种解决方案。

话虽这么说,你的代码确实工作 - 只需稍作修改:而不是

CXActionSheetToolBarButtonItem.cancel.title = "asd"

你可以写

var item = CXActionSheetToolBarButtonItem.cancel
item.title = "asd"

这将编译。

背后的原因是枚举是一个值类型。每次创建枚举类型的实例时,例如var item = CXActionSheetToolBarButtonItem.cancel,将枚举值复制到新变量item中。您可以使用varlet选择是否希望该值可变。这就是Swift枚举的用法。

单个枚举案例如CXActionSheetToolBarButtonItem.cancel根据定义是不可变的。

CXActionSheetToolBarButtonItem.cancel.title = "asd"

没有任何意义,因为没有可以分配标题的实例。枚举案例.cancel未绑定到变量。