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
неизменен, и я хочу этого. Объясните мне, почему то, что я хочу, так неправильно ... Эта страница слишком мала, чтобы включать все варианты, которые я пробовал, но, возможно, я упустил простой ответ.