Инициализатор протокола Swift исключает добавление хранимых свойств в структуру - PullRequest
0 голосов
/ 16 марта 2020

TL; DR :

Я хочу, чтобы протокол обеспечивал поведение по умолчанию init, но компилятор не дает адаптерам добавлять дополнительные сохраненные свойства. Я решил это с помощью композиции вместо наследования, но что не так с моим исходным подходом?

Мотивация

Я хочу автоматизировать преобразование объектов из спецификаций проекта в спецификации времени выполнения , Я использую пример масштабирования CGSize, но цель более общая, чем просто геометрия c макет. (IOW, например, мое решение не будет принять / отклонить / переписать autolayout.)

Код

Вы можете вставить это прямо в Playground, и он будет работать правильно.

protocol Transformable {
    var size : CGSize { get }               // Will be set automatically;
    static var DESIGN_SPEC : CGSize { get } // could be any type.
    init(size: CGSize)                      // Extension will require this.
}

// A simple example of transforming.
func transform(_ s: CGSize) -> CGSize  {
    CGSize(width: s.width/2, height: s.height/2)
}

// Add some default behavior.
// Am I sinning to want to inherit implementation?
extension Transformable {
    init() { self.init(size: transform(Self.DESIGN_SPEC)) }
     // User gets instance with design already transformed. No muss, fuss.
}

// Adopt the protocol...
struct T : Transformable {
    let size: CGSize
    static let DESIGN_SPEC = CGSize(width: 10, height: 10)
}

// ...and use it.
let t = T()
t.size  // We get (5,5) as expected.

Но у каждого Эдема должна быть змея. Я хочу Transformable с другим свойством:

struct T2 : Transformable {
    // As before.
    let size: CGSize
    static let DESIGN_SPEC = CGSize(width: 10, height: 10)

    let i : Int   // This causes all sorts of trouble.
}

Whaa? Type 'T2' does not conform to protocol 'Transformable'

Мы потеряли синтезированный инициализатор, который устанавливает член size.

Итак ... мы возвращаем его обратно:

struct T3 : Transformable {
    // As before.
    let size: CGSize
    static let DESIGN_SPEC = CGSize(width: 10, height: 10)
    let i : Int

    init(size: CGSize) {
        self.size = size
        self.i = 0  // But this is a hard-coded value.
    }
}

Но теперь наш новый член определяется статически. Поэтому мы пытаемся добавить еще один инициализатор:

struct T4 : Transformable {
    // As before.
    let size: CGSize
    static let DESIGN_SPEC = CGSize(width: 10, height: 10)
    let i : Int
    init(size: CGSize) { self.size = size ; self.i = 0 }

    // Try setting 'i':
    init(i: Int) {
        self.init() // Get the design spec properly transformed.

        self.i = i  // 'let' property 'i' may not be initialized directly;
    }               // use "self.init(...)" or "self = ..." instead
}

Объявление i как var, закрывающее компилятор. Но i неизменен, и я хочу этого. Объясните мне, почему то, что я хочу, так неправильно ... Эта страница слишком мала, чтобы включать все варианты, которые я пробовал, но, возможно, я упустил простой ответ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...