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

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

protocol AddsMoreCommands {
     /* ... */
}

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

protocol AddsMoreCommands: class {
}

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

extension AddsMoreCommands where /* what */ {
}

Есть ли способ сделать это? Спасибо!


person emrys57    schedule 01.02.2016    source источник


Ответы (4)


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

protocol AddsMoreCommands: UIViewController {
    // code
}

Что точно так же, как:

protocol AddsMoreCommands where Self: UIViewController {
   // code
}

Я обычно использую первый вариант, IIRC. Я недавно читал в документации Swift, что это рекомендуемый способ, когда ограничение равно Self, если это другое ограничение, такое как ассоциировать типы — это когда можно использовать where.

Обратите внимание, что поскольку UIViewController также является class, этот протокол можно реализовать для слабых свойств, таких как делегаты.

ОТРЕДАКТИРОВАНО 05/01/2021: в предыдущем опубликованном решении было предупреждение, я удалил его и использую это, которое не выдает никаких предупреждений.

person rgkobashi    schedule 26.10.2017
comment
Какое совпадение, что я нажал на вопрос двухлетней давности и нашел идеальное решение, опубликованное час назад ???? - person Oscar Apeland; 26.10.2017
comment
Xcode 9.1 теперь предупреждает меня об этом высказывании: Избыточное ограничение макета «Я»: «Любой объект». Ограничение макета «Self»: ​​здесь подразумевается «AnyObject». Изменение моего кода в формате принятого ответа кажется более хорошим. - person Zig; 01.11.2017
comment
Начиная с Xcode 9.1 протоколы только для класса теперь используют AnyObject вместо class. protocol AddsMoreCommands: AnyObject where Self: UIViewController { // code } - person dodgio; 07.11.2017
comment
@dodgio все еще получает такое же предупреждение, используя AnyObject - person rgkobashi; 07.11.2017
comment
Да, предупреждение все еще отображается. Вот ошибка Swift для этого. Компилятор жалуется (зашифрованно), что код дважды указывает AnyObject в протоколе. Ошибка перечисляет, почему это плохое предупреждение. - person dodgio; 07.11.2017
comment
Я использую protocol Foo where Self: Bar, по какой причине вы не используете этот шаблон? - person Laffen; 13.11.2017
comment
Протоколы могут быть приняты структурой, перечислением и классами. Но, например, если ваш протокол является источником данных или делегатом, он должен быть слабым, чтобы избежать круга удержания, а для того, чтобы сделать свойство слабым, вам нужно использовать : class. При использовании : class ваш протокол может быть реализован ТОЛЬКО на классах. В этом случае я использовал : class, так как принятый ответ использует его. - person rgkobashi; 13.11.2017
comment
@rgkobashi нет необходимости использовать class, поскольку вы уже ограничиваете соответствие UIViewController подклассам_. protocol RatingControllerDelegate where Self: UIViewController прекрасно компилируется и не выдает никаких предупреждений в Xcode9.1+. - person Dávid Pásztor; 31.05.2018
comment
@DávidPásztor, вы правы, однако, если вы хотите использовать его в структурном шаблоне, таком как делегирование, чтобы сделать свойство слабым, необходимо явно добавить «класс» :) - person rgkobashi; 31.05.2018
comment
Говорит ли это о том, что все, что AddsMoreCommands ДОЛЖНО быть UIViewController, или что все требования этого протокола должны быть реализованы только UIViewControllers? то есть, если у вас есть var addMore: AddsMoreCommands, может ли компилятор предположить, что он имеет дело с UIViewController, который реализует этот протокол. Мой опыт говорит, что компилятор не может этого сделать. - person Giles; 03.08.2018
comment
@ Джайлс, я не уверен, что вы имеете в виду под первым вопросом, но это больше похоже на второй. Все требования этого протокола ДОЛЖНЫ быть реализованы UIViewControllers. Если у вас есть addMore: AddsMoreCommands, компилятор не может предположить, что он имеет дело с UIViewController, что является одним из преимуществ POP, вы снимаете такого рода ограничения. Однако компилятор узнает, когда вы пытаетесь реализовать AddsMoreCommands на не-UIViewController. - person rgkobashi; 03.08.2018

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

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

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

person Massmaker    schedule 01.12.2017
comment
Поправьте меня, если я ошибаюсь, но проблема с этим решением выше (которое генерирует предупреждение в Xcode 9.1 и выше) заключается в том, что вы не можете объявить делегат слабым? - person Kyle Goslan; 03.04.2018
comment
Кроме того, когда я использую это решение со Swift 4.1, мне нужно преобразовать свойства AddsMoreCommands в UIViewController, которых я хотел избежать... - person heyfrank; 12.07.2018
comment
Чтобы избежать приведения типа, вы можете сделать это: typealias AddsMoreCommandsViewController = UIViewController & AddsMoreCommands - person plu; 21.07.2018

Теперь в Swift 5 этого можно добиться следующим образом:

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

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

person Wojciech Kulik    schedule 29.08.2019
comment
В чем разница между этими двумя? extension AddsMoreCommands, где Self: UIViewController { // Код } И протокол AddsMoreCommands: UIViewController { /* ... */ } Заранее спасибо. - person Rahul Patel; 17.12.2020
comment
Я думаю, вы имели в виду protocol вместо extension, если это так, кто-то уже ответил на него - person rgkobashi; 16.02.2021

person    schedule
comment
У меня так чуть не получилось... Я написал self вместо Self :-( Большое спасибо, все отлично работает! - person emrys57; 01.02.2016
comment
Для меня это вызывает некоторые синтаксические странности, когда я использую это в сочетании с приведением типов. - person Chris Prince; 03.08.2017
comment
Это не сработает, если вам нужно включить свойство в протокол, поскольку расширения не могут содержать сохраненные свойства. - person shim; 12.01.2018
comment
Он может иметь сохраненные свойства, вам нужно только использовать это: objc_getAssociatedObject(self, &KeyName) as? Тип свойства - person Michał Ziobro; 23.04.2018
comment
Это также требует приведения, когда объявление let/var имеет тип AddsMoreCommands, но метод, которому вы его передаете, ожидает UIViewController - person GoatInTheMachine; 28.06.2018
comment
Документация изменена на использование AnyObject вместо class -› docs.swift.org /swift-book/LanguageGuide/Protocols.html#ID281. Но обе версии пока ведут себя одинаково без предупреждений об устаревании. - person heyfrank; 12.07.2018
comment
Это просто факт синтаксиса Swift, что вы не можете добавлять к ним переопределения? Например, override var prefersStatusBarHidden? - person Chris Prince; 25.07.2018