Быстрое наследование от общего типа

Я пытаюсь наследовать от родового типа, так что я могу вставить тип в иерархию:

class Foo < T:AnyObject > : T {}

но я получаю ошибку

inheritance from non-protocol, non-class type 'T'

но я гарантирую, что T - класс (даже если я изменяю AnyObject на какой-то другой класс). Это просто невозможно?

Ответ 1

Вы не можете наследовать что-то, если вы не знаете, что это такое, по нескольким причинам.

  • Во-первых, компилятор должен знать, как выложить экземпляр вашего класса в памяти. Для этого ему необходимо знать, какие переменные экземпляра (хранимые свойства) у него есть: как те, которые задает класс, так и те, которые унаследованы от суперкласса.

  • Компилятор также должен знать о свойствах и инициализаторах суперкласса, чтобы убедиться, что экземпляр вашего класса правильно инициализирован. Если свойства суперкласса неизвестны, не существует способа узнать, было ли вы инициализировано все состояние, которое вам нужно (и никому не нужно) до окончания инициализатора. Если инициализаторы суперкласса неизвестны, компилятор не может обеспечить, чтобы вы назвали инициализатор назначенного суперкласса из вашего собственного.

  • Как вы напишете реализации методов, которые ссылаются на свойства и методы суперкласса, если вы не знаете, что такое суперкласс? Если вы пишете код, который ссылается на self.view, то каким-то образом создайте экземпляр версии вашего класса, которая не наследуется от класса с этим свойством, этот код сломается.

Есть, вероятно, еще несколько причин, но их должно быть достаточно.:)

Хотя есть несколько мест, где вы ожидаете подкласса, Cocoa (будь то в Swift или ObjC) в целом поддерживает composition вместо наследования для настройки.

Если вы добавляете кучу функциональности в различные контроллеры представлений и повторно используете код для этого, факторизуя его в свой класс, простое решение состоит в том, чтобы сделать ваши классы контроллера классов собственными экземплярами этого класса пытаясь быть тем классом. Если у вас есть места, где вы хотите, чтобы определенные классы контроллера классов должны были иметь Foo, вы можете определить протокол, который выражает это требование.

Если вы пытаетесь добавить функциональность к существующему классу, вы можете использовать расширение. Возможно, вы не сможете использовать расширение для добавления хранимых свойств, но вы можете его подделать. Например, вы можете использовать вычисленные свойства и определять собственное хранилище в рамках их реализации. Один из способов сделать это может заключаться в использовании функции связанных объектов в среде выполнения ObjC (которая AFAIK по-прежнему доступна из Swift).

Ответ 2

Не CRTP (Любопытно повторяющийся шаблон шаблона) иначе, чем в OP в любом случае?

Совершенно точно создать этот шаблон в Swift (< = 3.0), но в настоящее время он прослушивается и будет блокироваться при инициализации без каких-либо ошибок во время выполнения.

class Template<T> { ... }

class RealThing : Template<RealThing> { ... }

Недавно я обнаружил этот шаблон в мостовом API UIKit. Вы можете прочитать это в моем коротком сообщении в блоге здесь.

Я также перевел пример из wiki в Swift 3.0:

protocol Constructor {
    init()
}

protocol Shape {
    func cloned() -> Shape
}

typealias ShapeProtocols = Constructor & Shape

class Shape_CRTP<T> : ShapeProtocols {

    required init() {}

    func  cloned() -> Shape {
        let new = Shape_CRTP<T>()
        // copy everything to `new` from `self`
        return new
    }
}

extension Shape_CRTP where T : ShapeProtocols {

    func cloned() -> Shape {
        let new = T()
        // copy everything to `new` from `self`
        return new
    }
}

// First non-crtp proof of concept
class Square : ShapeProtocols {

    required init() {}
    func cloned() -> Shape {
        return Square()
    }
}

let clone = Shape_CRTP<Square>().cloned() // creates a new `Shape`
type(of: clone) // Square.Type

// now CRTP but this is bugged: http://blog.devandartist.com/posts/swift-crtp
class Circle : Shape_CRTP<Circle> {}

Circle().cloned()

print("this will never print because of the described bug")