Swift: не удалось присвоить значение свойству протокола?

Класс A предоставляет строковое значение. Класс B имеет внутри себя два члена типа A и предоставляет вычисляемое свойство "v" для выбора одного из них.

class A {
    var value: String

    init(value: String) {
        self.value = value
    }
}

class B {
    var v1: A?
    var v2: A = A(value: "2")

    private var v: A {
        return v1 ?? v2
    }

    var value: String {
        get {
            return v.value
        }
        set {
            v.value = newValue
        }
    }
}

Этот код прост и работает. Поскольку и у A, и у B есть «значение» члена, я делаю такой протокол:

protocol ValueProvider {
    var value: String {get set}
}

class A: ValueProvider {
    var value: String

    init(value: String) {
        self.value = value
    }
}

class B: ValueProvider {
    var v1: ValueProvider?
    var v2: ValueProvider = A(value: "2")

    private var v: ValueProvider {
        return v1 ?? v2
    }

    var value: String {
        get {
            return v.value
        }
        set {
            v.value = newValue // Error: Cannot assign to the result of the expression
        }
    }
}

Если я изменю следующий код

v.value = newValue

to

var v = self.v
v.value = newValue

Он снова работает!

Это ошибка Swift или что-то особенное в свойстве протоколов?


person Ken Zhang    schedule 19.04.2015    source источник


Ответы (1)


Вы должны определить протокол как протокол class:

protocol ValueProvider : class {
    var value: String {get set}
}

затем

var value: String {
    get { return v.value }
    set { v.value = newValue }
}

компилируется и работает должным образом (т. е. присваивает новое значение объекту, на который ссылается v1, если v1 != nil, и объекту, на который ссылается v2, в противном случае).

v — вычисляемое свойство типа ValueProvider, доступное только для чтения. Определив протокол как протокол класса, компилятор знает, что v является ссылочным типом, и поэтому его свойство v.value можно изменить, даже если сама ссылка является константой.

Ваш исходный пример кода работает, потому что свойство v имеет тип A, который является ссылочным типом.

И ваш обходной путь

set {
    var tmp = v1 ?? v2
    tmp.value = newValue
}

работает, потому что (чтение-запись) свойства переменных могут быть установлены в любом случае (тип значения или тип ссылки).

person Martin R    schedule 19.04.2015