Как объявить переменную, которая имеет тип и реализует протокол?

Мое приложение имеет протокол для controllerов подробных представлений, заявляя, что у них должно быть свойство viewModel :

 protocol DetailViewController: class { var viewModel: ViewModel? {get set} } 

У меня также есть несколько разных classов, которые реализуют протокол:

 class FormViewController: UITableViewController, DetailViewController { // ... } class MapViewController: UIViewController, DetailViewController { // ... } 

Моему controllerу главного представления требуется свойство, которое может быть установлено в любой подclass UIViewController который реализует протокол DetailViewController .

К сожалению, я не могу найти документацию о том, как это сделать. В Objective-C было бы тривиально:

 @property (strong, nonatomic) UIViewController; 

Похоже, что в Swift нет синтаксиса для этого. Самое близкое, что я пришел, – объявить родовое определение моего classа:

 class MasterViewController: UITableViewController { var detailViewController: T? // ... } 

Но затем я получаю сообщение об ошибке: «Класс« MasterViewController »не выполняет требуемые члены суперclassа»

Похоже, это должно быть так же легко сделать в Swift, как в Objective-C, но я ничего не могу найти где-нибудь, что подсказывает, как я могу это сделать.

Начиная с Swift 4, вы можете это сделать.

Swift 4 реализовал SE-0156 ( Экземпляры classа и подтипа).

Эквивалент этого синтаксиса Objective-C:

 @property (strong, nonatomic) UIViewController * detailViewController; 

Теперь выглядит так в Swift 4:

 var detailViewController: UIViewController & DetailViewController 

По существу вы можете определить один class, которому соответствует переменная, и N количество протоколов, которые оно реализует. Более подробную информацию см. В связанном документе.

Я думаю, вы можете добраться туда, добавив (пустое) расширение в UIViewController а затем указав свой атрибут detailViewController используя составленный протокол пустого расширения и ваш DetailViewController . Как это:

 protocol UIViewControllerInject {} extension UIViewController : UIViewControllerInject {} 

Теперь все подclassы UIViewController удовлетворяют протоколу UIViewControllerInject . Тогда с этим просто:

 typealias DetailViewControllerComposed = protocol class MasterViewController : UITableViewController { var detailViewController : DetailViewControllerComposed? // ... } 

Но это не особенно «естественно».

=== Редактировать, добавить ===

На самом деле, вы можете сделать это немного лучше, если вы определите свой DetailViewController используя мой предложенный UIViewControllerInject . Например:

 protocol UIViewControllerInject {} extension UIViewController : UIViewControllerInject {} protocol DetailViewController : UIViewControllerInject { /* ... */ } 

и теперь вам не нужно явно DetailViewControllerComposed что-то (мой DetailViewControllerComposed ) и использовать DetailViewController? как тип detailViewController .

Другим способом было бы ввести промежуточные базовые classы для соответствующих controllerов представления UIKit, которые реализуют протокол:

 class MyUIViewControler : UIViewController, DetailViewController ... class MyUITableViewController : UITableViewController, DetailViewController ... 

Затем вы наследуете свои controllerы представлений от этих controllerов представлений, а не UIKit.

Это тоже не является естественным, но это не заставляет всех ваших UIViewControllers удовлетворять протоколу UIViewControllerInject , как предлагал GoZoner.

  • доступ к файлу с использованием Java с Samba JCIFS
  • Вызывается метод расширения Swift, а не метод, реализованный в подclassе
  • Давайте будем гением компьютера.