Как потребовать, чтобы протокол мог быть принят только определенным классом

91

Я хочу этот протокол:

protocol AddsMoreCommands {
     /* ... */
}

только для того, чтобы быть принятыми классами, наследующими от класса UIViewController. Эта страница сообщает мне, что я могу указать, что он принимается только классом (в отличие от структуры), написав

protocol AddsMoreCommands: class {
}

но я не понимаю, как требовать, чтобы он принимался только определенным классом. Позже на этой странице рассказывается о добавлении whereпунктов в расширения протокола для проверки соответствия, но я тоже не вижу, как это адаптировать.

extension AddsMoreCommands where /* what */ {
}

Есть ли способ сделать это? Благодарность!

emrys57
источник

Ответы:

115
protocol AddsMoreCommands: class {
    // Code
}

extension AddsMoreCommands where Self: UIViewController {
    // Code
}
Roee84
источник
4
У меня так почти selfSelf
получилось
Для меня это вызывает некоторые синтаксические странности, когда я использую это в сочетании с приведением типов.
Крис Принс
3
Это не сработает, если вам нужно включить свойство в протокол, поскольку расширения не могут содержать сохраненные свойства.
shim
1
Он может быть сохранен, вам нужно только использовать это: objc_getAssociatedObject (self, & KeyName) as? PropertyType
Michał Ziobro
Это также требует приведения типов, когда объявление let / var имеет тип, AddsMoreCommandsно метод, UIViewController
которому
80

Этого также можно добиться без расширения:

protocol AddsMoreCommands: class where Self: UIViewController {
   // code
}

ИЗМЕНИТЬ 2017/11/04 : Как заметил Зиг , это, похоже, генерирует предупреждение в Xcode 9.1. В настоящее время в проекте Swift (SR-6265) сообщается о проблеме, связанной с удалением предупреждения, я буду следить за ней и соответствующим образом обновлю ответ.

EDITED 2018/09/29 : classтребуется, если переменная, которая будет хранить экземпляр, должна быть слабой (например, делегат). Если вам не нужна слабая переменная, вы можете опустить ее classи просто написать следующее, и никаких предупреждений не будет:

protocol AddsMoreCommands where Self: UIViewController {
   // code
}
ргкобаши
источник
5
Какое совпадение, что я нажал на вопрос двухлетней давности и нашел идеальное решение, опубликованное час назад 😲
Оскар Апеланд,
Xcode 9.1 теперь предупреждает меня об этом: Избыточное ограничение компоновки «Self»: ​​«AnyObject». Ограничение ограничения макета «Self»: ​​здесь подразумевается «AnyObject». Изменение моего кода на формат принятого ответа кажется более удачным.
Zig
2
Начиная с Xcode 9.1, протоколы только для классов теперь используют AnyObjectвместо class. protocol AddsMoreCommands: AnyObject where Self: UIViewController { // code }
dodgio 07
@dodgio по-прежнему получает такое же предупреждение, используяAnyObject
rgkobashi
1
@ DávidPásztor, вы правы, однако, если вы хотите использовать его в структурном шаблоне, таком как делегирование, чтобы иметь возможность сделать свойство слабым, необходимо явно добавить «класс» :)
rgkobashi
48

Из-за проблемы в предыдущем ответе я получил это объявление:

protocol AddsMoreCommands where Self : UIViewController { 
    // protocol stuff here  
}

нет предупреждений в Xcode 9.1

Массмейкер
источник
5
Исправьте меня, если я ошибаюсь, но проблема с этим решением выше (которое генерирует предупреждение в Xcode 9.1 и более поздних версиях) заключается в том, что вы не можете объявить делегата слабым?
Кайл Гослан 03
Кроме того , когда я использую это решение с Swift 4.1, мне нужно литые свойствами AddsMoreCommandsк UIViewControllerкоторой я хотел бы избежать ...
fl034
9
Чтобы избежать приведения типов, вы можете сделать следующее:typealias AddsMoreCommandsViewController = UIViewController & AddsMoreCommands
plu
35

Теперь в Swift 5 этого можно добиться:

protocol AddsMoreCommands: UIViewController {
     /* ... */
}

Очень удобно.

Войцех Кулик
источник