Swift - метод класу, який повинен бути замінений підкласом


91

Чи існує стандартний спосіб зробити "чисто віртуальну функцію" в Swift, тобто. той, який повинен бути замінений кожним підкласом, і який, якщо це не так, спричиняє помилку часу компіляції?


Ви можете реалізувати це у супер класі та висловити твердження. Я бачив, як це використовується в Obj-C, Java та Python.
David Skrundz

8
@NSArray Це спричиняє час виконання, а не час компіляції, помилка
JuJoDi

Ця відповідь допоможе і вам. введіть тут опис посилання
Chamath Jeevan

Чисто віртуальна функція реалізується protocolз ( по порівнянні з interfaceз в Java) Якщо вам потрібно використовувати їх як абстрактні методи мають поглянути на це питання / відповідь: stackoverflow.com/a/39038828/2435872
jboi

Відповіді:


147

У вас є два варіанти:

1. Використовуйте протокол

Визначте суперклас як протокол замість класу

Pro : Перевірка часу компіляції, чи кожен "підклас" (а не фактичний підклас) реалізує необхідні методи

Con : "Суперклас" (протокол) не може реалізовувати методи або властивості

2. Затвердити в супер версії методу

Приклад:

class SuperClass {
    func someFunc() {
        fatalError("Must Override")
    }
}

class Subclass : SuperClass {
    override func someFunc() {
    }
}

Pro : Може реалізовувати методи та властивості в суперкласі

Недолік : немає перевірки часу компіляції


3
@jewirth ти все одно не отримаєш перевірку часу компіляції для підкласів
drewag

5
Протокол не може реалізовувати методи, але ви можете надати їх за допомогою методів розширення.
Девід Моулз

2
Станом на Swift 2.0, тепер є розширення протоколів :) Посилання на Apple .
Ephemera

4
Незважаючи на те, fatalErrorщо не забезпечує перевірку часу компіляції, приємно, що компілятор є принаймні достатньо розумним, щоб не вимагати від вас повернення значення методу під час виклику шляху виконання fatalError.
bugloaf

3
Випадок 2: Майте на увазі той факт, що якщо ви зателефонуєте super.someFunc()за методом перевизначення, ви отримаєте помилку, незважаючи на те, що ви її перевизначили. Ви знаєте, що ви не повинні це називати, але хтось інший не повинен цього знати і просто дотримуватися стандартної практики.
Якуб Трухлар

48

Наступне дозволяє успадкувати від класу, а також перевірити час компіляції протоколу :)

protocol ViewControllerProtocol {
    func setupViews()
    func setupConstraints()
}

typealias ViewController = ViewControllerClass & ViewControllerProtocol

class ViewControllerClass : UIViewController {

    override func viewDidLoad() {
        self.setup()
    }

    func setup() {
        guard let controller = self as? ViewController else {
            return
        }

        controller.setupViews()
        controller.setupConstraints()
    }

    //.... and implement methods related to UIViewController at will

}

class SubClass : ViewController {

    //-- in case these aren't here... an error will be presented
    func setupViews() { ... }
    func setupConstraints() { ... }

}

2
мило, typealias на допомогу :)
Chris Allinson

Будь-який спосіб зупинити користувачів цього API від виведення своїх класів clild з ViewControllerClass замість ViewController? Це чудове рішення для мене, оскільки через кілька років я буду виходити із свого псевдоніма, і забуду про те, які функції потрібно замінити на той час.
Девід Ректор

@David Rector, чи можете ви зробити свій клас приватним, а ваші псевдоніми загальнодоступними? На жаль, повідомлення з мого телефону не можуть перевірити себе.
ScottyBlades

1
Ідеальне рішення, дякую за це. Як підкреслював @DavidRector, було б чудово, якби було рішення також зробити це таким чином, щоб лише типиали були загальнодоступними, але, на жаль, це неможливо.
CyberDandy

35

Немає жодної підтримки абстрактних класів / віртуальних функцій, але ви, напевно, можете використовувати протокол для більшості випадків:

protocol SomeProtocol {
    func someMethod()
}

class SomeClass: SomeProtocol {
    func someMethod() {}
}

Якщо SomeClass не реалізує someMethod, ви отримаєте цю помилку часу компіляції:

error: type 'SomeClass' does not conform to protocol 'SomeProtocol'

30
Зауважте, це працює лише для найвищого класу, який реалізує протокол. Будь-які підкласи можуть ігнорувати вимоги протоколу.
memmons

2
Крім того, використання дженериків у протоколах не підтримується = (
Dielson Sales

14

Іншим обхідним шляхом, якщо у вас немає занадто багато "віртуальних" методів, є підклас передавати "реалізації" в конструктор базового класу як об'єкти функції:

class MyVirtual {

    // 'Implementation' provided by subclass
    let fooImpl: (() -> String)

    // Delegates to 'implementation' provided by subclass
    func foo() -> String {
        return fooImpl()
    }

    init(fooImpl: (() -> String)) {
        self.fooImpl = fooImpl
    }
}

class MyImpl: MyVirtual {

    // 'Implementation' for super.foo()
    func myFoo() -> String {
        return "I am foo"
    }

    init() {
        // pass the 'implementation' to the superclass
        super.init(myFoo)
    }
}

1
не настільки корисно, якщо у вас є ще кілька віртуальних методів
Бушра Шахід

@ xs2bush Якщо більша частина ваших методів віртуальна, ніж ні, вам, мабуть, краще оголосити їх у протоколі та надати "невіртуальні" за допомогою методів розширення.
Девід Моулз

1
саме цим я і закінчив займатися
Бушра Шахід

0

Ви можете використовувати протокол проти затвердження , як запропоновано у відповідь тут шляхом drewag. Однак приклад для протоколу відсутній. Я тут покриваю,

Протокол

protocol SomeProtocol {
    func someMethod()
}

class SomeClass: SomeProtocol {
    func someMethod() {}
}

Тепер усі підкласи необхідні для реалізації протоколу, який перевіряється під час компіляції. Якщо SomeClass не реалізує someMethod, ви отримаєте цю помилку часу компіляції:

помилка: тип 'SomeClass' не відповідає протоколу 'SomeProtocol'

Примітка: це працює лише для найвищого класу, який реалізує протокол. Будь-які підкласи можуть ігнорувати вимоги протоколу. - так прокоментував поmemmons

Твердження

class SuperClass {
    func someFunc() {
        fatalError("Must Override")
    }
}

class Subclass : SuperClass {
    override func someFunc() {
    }
}

Однак твердження буде працювати лише під час виконання.


-2

Будучи новим у розробці iOS, я не зовсім впевнений, коли це було впроваджено, але одним із способів отримати найкраще з обох світів є впровадження розширення для протоколу:

protocol ThingsToDo {
    func doThingOne()
}

extension ThingsToDo {
    func doThingTwo() { /* Define code here */}
}

class Person: ThingsToDo {
    func doThingOne() {
        // Already defined in extension
        doThingTwo()
        // Rest of code
    }
}

Розширення - це те, що дозволяє вам мати значення за замовчуванням для функції, тоді як функція в звичайному протоколі все одно надає помилку часу компіляції, якщо не визначена


1
абстрактні функції протилежні реалізаціям за замовчуванням
Hogdotmac
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.