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
}

Как вы можете видеть, мне пришлось «соответствовать» протоколу Identifiable в структуре A и структуре B. Но представьте, если бы у меня было N структур, которые должны соответствовать этому протоколу ... я не хочу «копировать / вставлять» 'соответствие (var id: Int, var name: String)

Итак, я создаюрасширение протокола:

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

Это происходит потому, что в протоколе Identifiable идентификатор и имя доступны только для получения. Теперь, если я изменю свойства id и name на{приготовься} Я получаю следующую ошибку:

Тип «С» не соответствует протоколу «Идентифицируемый»

Эта ошибка возникает из-за того, что я не реализовал установщик в расширении протокола ... Поэтому я изменяю расширение протокола:

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

        set {

        }
    }

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

        set {

        }
    }
}

Теперь ошибка исчезает, но если я установлю новое значение в id или name, он получит значение по умолчанию (getter). Конечно,сеттер пуст.

Мой вопрос:Какой кусок кода я должен поместить в сеттер? Потому что, если я добавлюself.id = newValue он падает (рекурсивно).

Заранее спасибо.

Ответы на вопрос(3)

Свойство становится вычисляемым свойством, что означает, что у него нет резервной переменной, такой как _x, как в ObjC. В приведенном ниже коде решения вы можете увидеть, что xTimesTwo ничего не хранит, а просто вычисляет результат из x.

См. Официальные документы по вычислительным свойствам.

Функцией, которую вы хотите, также могут быть Property Observers.

Установщики / получатели отличаются, чем они были в Objective-C.

Что вам нужно это:

var x:Int

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

Вы можете изменить другие свойства в установщике / получателе, для чего они и предназначены

 Axort11 авг. 2016 г., 17:40
Привет! Спасибо за Ваш ответ. Да, как вы сказали, проблема в том, что вы не можете объявить переменные в расширении (Расширения могут не содержать хранимых свойств), поэтому мне было интересно, есть ли способ (небольшой шанс) с вычисляемыми свойствами: P
Решение Вопроса

stored property к типу через расширение протокола. Однако это невозможно, потому что с расширениями вы не можете добавить сохраненное свойство.

Я могу показать вам пару альтернатив.

Подклассы (объектно-ориентированное программирование)

Самый простой способ (как вы, наверное, уже представляете) - использовать классы вместо структур.

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

class A: IdentifiableBase { }

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

Минусы: в этом случае ваш класс А должен наследоваться отIdentifiableBase и поскольку в Swift нет множественного наследования, это будет единственный класс, который сможет наследовать.

Компоненты (протоколно-ориентированное программирование)

Эта техника довольно популярна в разработке игр

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
 Bohdan Savych24 мая 2017 г., 12:40
Не могли бы вы объяснить, почему вы создаете 2 протокола (HasIdentifiableComponent и Identifiable) в примере компонентов?
 cohen7209 дек. 2018 г., 08:53
@LucaAngeletti, как бы вы справились с этим, когда протокол определяет связанный тип, а также имеет хранимое свойство этого типа.
 Axort11 авг. 2016 г., 17:34
Привет! Спасибо за Ваш ответ. Мне действительно нравится эта техника компонентов. Теперь я пытаюсь принять протоколно-ориентированное программирование, поэтому я искал что-то подобное.
 Luca Angeletti24 мая 2017 г., 18:31
@ BohdanSavych Просто потому, что я хотел отделить концепциюHasIdentifiableComponent от концепцииIdentifiable, Если вам не нравится это разделение, не стесняйтесь группировать все в один протокол.
 Robin Macharg12 мар. 2018 г., 13:36
Если вы рады задействовать среду выполнения Objective C, вы также можете воспользоваться связанными объектами в качестве резервного хранилища для свойств расширения. Увидетьnshipster.com/swift-objc-runtime а такжеmarcosantadev.com/stored-properties-swift-extensions для примеров и обсуждения.
 Above The Gods02 февр. 2017 г., 05:19
Почему у вас есть протокол HasIdentifiableComponent? Почему бы просто не сделатьprotocol Identifiable: var identifiableComponent: IdentifiableComponent { get set }  затемextension Identifiable { var id: Int { get { return identifiableComponent.id } set { identifiableComponent.id = newValue } } etc... }
 Luca Angeletti11 авг. 2016 г., 17:35
@Axort: Пожалуйста. Я также предлагаю вам посмотреть этовидео околоПротоколно-ориентированное программирование из WWDC 2015 года. Очень интересно.
 ekreloff16 янв. 2018 г., 20:25
Я получаю сообщение об ошибке «Не удается присвоить свойству: вызов функции возвращает неизменное значение» в Swift 4 при попытке установить любое из свойств. Аналогичная ошибка при попытке установить эти свойства внутри соответствующего класса, говоря, что self является неизменным. Это должны быть протоколы только для класса?

но они, как правило, наиболее полезны для свойств и функций только для чтения.

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

что-то вроде:

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)")
 Axort11 авг. 2016 г., 17:35
Привет! Спасибо за Ваш ответ. Я искал решение с протоколно-ориентированным программированием вместо объектно-ориентированного программирования.

Ваш ответ на вопрос