Почему в Swift некоторые функции делегата требуют переопределения func

Хорошо, я понимаю делегатов. Я озадачен (в смысле языкового дизайна), что в некоторых недавних работах над Swift я встречал делегатов, которые могут быть реализованы только с квалификатором override. В частности, «controlTextDidEndEditing» как NSTextFieldDelegate элемента пользовательского интерфейса NSTextField. (Да, это разработка OS X, а не IOS).

При реализации этого делегата Xcode настаивает на квалификаторе переопределения для функции func. Если это функция «делегата», то какой код / ​​функциональность выше по иерархии я переопределяю и должно ли это меня волновать? Вставка super.controlTextDidEndEditing в реализацию просто приводила к бесконечному циклу. Мое понимание (возможно, ошибочное / неполное) делегата заключалось в том, что он определяет подпись, которой соответствует реализация.

Контекст - это тестовая панель с TextField и Label, помещенными в ViewController, тривиальное тестовое упражнение. Единственный сценарий, к которому я могу прийти, заключается в том, что где-то в иерархии над ViewController какой-то другой класс уже реализует controlTextDidEndEditing, что приводит к требованию переопределения, как только я пытаюсь его реализовать, что означает, что я разрушаю некоторый существующий уровень функциональность. Я экспериментировал с подклассом NSTextField с тем же результатом, что и требовалось «переопределить».

Может ли кто-нибудь продолжить мое образование, объяснив все тонкости этого?

Спасибо,

Алан.


person altimes    schedule 12.04.2015    source источник


Ответы (2)


Это пример устаревшего шаблона проектирования в старом коде Objective-C, который не соответствует Swift. (Это скорее проблема с самым ранним кодом AppKit, а не с UIKit. Ожидайте, что Apple продолжит устранять эти проблемы со временем.)

controlTextDidEndEditing фактически не является частью NSTextFieldDelegate протокола (или любых других протоколов). Вместо этого он объявлен как неформальный протокол Objective-C:

@interface NSObject(NSControlSubclassNotifications)
- (void)controlTextDidBeginEditing:(NSNotification *)obj;
- (void)controlTextDidEndEditing:(NSNotification *)obj;
- (void)controlTextDidChange:(NSNotification *)obj;
@end

Неформальный протокол подобен расширению класса без реализации, и его использование восходит к тому времени, когда Objective-C не поддерживал формальные протоколы или дополнительные методы протокола.

У Swift нет неофициальных протоколов. Вместо этого он отображается как расширение NSObject.

extension NSObject {
    func controlTextDidBeginEditing(obj: NSNotification)
    func controlTextDidEndEditing(obj: NSNotification)
    func controlTextDidChange(obj: NSNotification)
}

Но Свифт не знает, что это неформальный протокол. В результате Swift позволит вам вызывать controlTextDidEndEditing() для любого NSObject, даже если NSObject не реализует этот метод.

Точно так же, когда вы реализуете один из этих методов в своем собственном классе, Swift заставит вас использовать ключевое слово override, потому что он думает, что вы переопределяете метод в NSObject, даже если это не так.

person Darren    schedule 12.04.2015
comment
Ага, значит, яблочная документация для NSControl, описывающая их как делегатов, описывает скорее функциональное намерение, чем реальность реализации. Хороший ответ, теперь мне комфортно, что требование переопределения - это действительно то, что я считаю синтаксическим шумом. Я скажу, что Swift намного тише в этой области, чем Objective C. Язык, на котором мне уже нравится кодировать. - person altimes; 12.04.2015

Вы не переопределяете делегатов, вы переопределяете геттеры и сеттеры. НО такие классы, как UITableViewController, соответствуют делегату и источнику данных, что означает, что они уже реализуют методы. Вы переопределяете их, потому что да, суперкласс уже их реализует.

Когда вы переопределяете методы делегата, я не думаю, что вы делаете вызов суперкласса, если суперкласс действительно не выполняет какую-то функцию. controlTextDidEndEditing NSTextField, вероятно, реализован самим NSTextField как пустая реализация, поэтому вам нужно переопределить.

person Schemetrical    schedule 12.04.2015
comment
Это согласуется с моим текущим пониманием того, что происходит, но я все еще озадачен тем, почему, как дизайнер, вы должны спроектировать класс для принятия протокола, а затем реализовать необязательную функцию в этом протоколе с пустой реализацией, вынуждая тех, кто использует класс для переопределения функции протокола. Как дизайнер я бы реализовал только те методы протокола, которые имеют определенное значение для создаваемого мной класса. Следовательно, я не могу понять, почему это так - person altimes; 12.04.2015
comment
Это общий шаблон для классов, объявляющих протокол, которому они хотят, чтобы их подкласс соответствовал. Они сами соответствуют ему, имея реализацию по умолчанию (для предотвращения пустой реализации и сбоев), поэтому ваш подкласс автоматически наследуется от реализации и может переопределить определенные методы. Примером является -numberOfSectionsInTableView UITableView, который по умолчанию автоматически возвращает 1. Просто выкройка, ничего особенного. - person Schemetrical; 12.04.2015
comment
Кроме того, controlTextDidEndEditing на самом деле не протокол, я считаю, что это метод класса, который вы наследуете и переопределяете, который вызывается, когда текст заканчивается редактированием. - person Schemetrical; 12.04.2015