Подтвердить что ты не робот

Swift - требовать, чтобы классы, реализующие протокол, были подклассами определенного класса

Я создаю несколько классов NSView, все из которых поддерживают специальную операцию, которую мы назовем transmogrify. На первый взгляд это кажется идеальным местом для протокола:

protocol TransmogrifiableView {
    func transmogrify()
}

Однако этот протокол не гарантирует, что каждый TransmogrifiableView должен быть NSView. Это означает, что любые методы NSView, которые я вызываю на TransmogrifiableView, не будут проверять:

let myView: TransmogrifiableView = getTransmogrifiableView()
let theSuperView = myView.superView // error: TransmogrifiableView does not have a property called 'superview'

Я не знаю, как требовать, чтобы все классы, реализующие мой протокол, также были подклассами NSView. Я пробовал это:

protocol TransmogrifiableView: NSView {
    func transmogrify()
}

но Swift жалуется, что протоколы не могут наследовать от классов. Это не помогает превратить протокол в протокол только для классов, используя

protocol TransmogrifiableView: class, NSView {
    func transmogrify()
}

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

Как мне потребовать, чтобы все TransmogrifiableView также были NSView? Я действительно не хочу перенести свой код с помощью "as" конверсий, которые являются плохими и отвлекающими.

4b9b3361

Ответ 1

Я думаю, что вы после подкласса NSView. Попробуйте следующее:

protocol TransmogrifiableView {
    func transmogrify()
}

class MyNSView: NSView, TransmogrifiableView {
    // do stuff.
}

И позже в коде принимают объекты типа MyNSView.

Изменить

Возможно, вам понадобится Extension, см. this

extension NSView: TransmogrifiableView {
    // implementation of protocol requirements goes here
}
  • Обратите внимание, что вы не сможете получить NSView без этого дополнительного метода.
  • Вы можете отдельно расширить подклассы NSView, чтобы переопределить этот новый метод.

Еще одним вариантом является создание класса, который содержит указатель на NSView и реализует дополнительные методы. Это также заставит вас проксировать все методы из NSView, которые вы хотите использовать.

class NSViewWrapper: TransmogrifiableView {
    var view : NSView!
    // init with the view required.
    //  implementation of protocol requirements goes here.
    .....
   // proxy all methods from NSView.
   func getSuperView(){
       return self.view.superView
   }
}

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

Ответ 2

Существует временное решение, использующее связанные типы для принудительного применения подкласса:

protocol TransmogrifiableView {
    associatedtype View: NSView = Self
    func transmogrify()
}

class MyView: NSView, TransmogrifiableView { ... } // compiles
class MyOtherClass: TransmogrifiableView { ... } // doesn't compile

Ответ 3

Начиная с Swift 4, вы можете определить это следующим образом:

let myView: NSView & TransmogrifiableView

За дополнительной информацией обращайтесь к вопросу # 156 Экземпляры подкласса

Ответ 4

В соответствии с определением протокол просто декларирует требования "методов, свойств и других требований". И "другими требованиями" означает, что суперкласс не является его частью.

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

Сейчас я не вижу чистого решения. Можно использовать where -clause для определения типа, который является NSView и соответствует TransmogrifiableView следующим образом:

class MyClass<T where T: NSView, T: TransmogrifiableView> {
    var aTransmogrifiableNSView: T
}

Или вы можете использовать другой суперкласс:

protocol TransmogrifiableViewProtocol {
    func transmogrify()
}

class TransmogrifiableView: NSView, TransmogrifiableViewProtocol {
    func transmogrify() {
        assert(false, "transmogrify() must be overwritten!")
    }
}

class AnImplementedTransmogrifiableView: TransmogrifiableView {
    func transmogrify() {
        println("Do the transmogrification...")
    }
}

В конце концов, оба решения не чисты и не удовлетворяют меня. Может быть, когда-нибудь в Swift будет добавлено abstract -keyword?

Ответ 5

Вы можете использовать что-то вроде этого:

protocol TransmogrifiableView where Self:NSView {}

Для этого требуется, чтобы все созданные экземпляры, которые соответствуют протоколу TransmogrifiableView, подклассифицированы с помощью NSView

Ответ 6

Все еще не идеальное решение, но здесь шаблон, который я использую иногда:

  • Определите протокол с помощью методов, которые вы хотите применить.
  • Определите свой базовый класс, реализуя все, что вы хотите, чтобы дети получали бесплатно.
  • В этом базовом классе (из # 2) есть необязательная переменная "делегировать" протокола, который вы сделали в # 1.
  • Определите все дочерние классы, чтобы они наследовали базовый класс и реализовали протокол.

Это позволяет вашему базовому классу вызывать методы протокола, заставляя их выполнять их (например, self.myDelegate?.myProtocolMethod).