В Swift я могу явно указать тип переменной, объявив ее следующим образом:
var object: TYPE_NAME
Если мы хотим пойти дальше и объявить переменную, которая соответствует нескольким протоколам, мы можем использовать protocol
декларативную:
var object: protocol<ProtocolOne,ProtocolTwo>//etc
Что, если я хочу объявить объект, который соответствует одному или нескольким протоколам, а также относится к определенному типу базового класса? Эквивалент Objective-C будет выглядеть так:
NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;
В Swift я ожидал, что это будет выглядеть так:
var object: TYPE_NAME,ProtocolOne//etc
Это дает нам гибкость, позволяющую иметь дело с реализацией базового типа, а также добавленного интерфейса, определенного в протоколе.
Есть ли другой более очевидный способ, которым я мог бы упустить?
пример
В качестве примера скажем, что у меня есть UITableViewCell
фабрика, которая отвечает за возврат ячеек, соответствующих протоколу. Мы можем легко настроить универсальную функцию, которая возвращает ячейки, соответствующие протоколу:
class CellFactory {
class func createCellForItem<T: UITableViewCell where T:MyProtocol >(item: SpecialItem,tableView: UITableView) -> T {
//etc
}
}
позже я хочу удалить эти ячейки из очереди, используя как тип, так и протокол
var cell: MyProtocol = CellFactory.createCellForItem(somethingAtIndexPath) as UITableViewCell
Это возвращает ошибку, потому что ячейка табличного представления не соответствует протоколу ...
Я хотел бы указать, что ячейка является a UITableViewCell
и соответствует MyProtocol
в объявлении переменной?
Обоснование
Если вы знакомы с фабричным шаблоном, это будет иметь смысл в контексте возможности возвращать объекты определенного класса, реализующие определенный интерфейс.
Как и в моем примере, иногда нам нравится определять интерфейсы, которые имеют смысл при применении к определенному объекту. Мой пример ячейки табличного представления - одно из таких оправданий.
Хотя предоставленный тип не совсем соответствует упомянутому интерфейсу, объект, возвращаемый фабрикой, соответствует, и поэтому мне хотелось бы гибкости во взаимодействии как с типом базового класса, так и с объявленным интерфейсом протокола.
источник
NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;
. Этот объект кажется совершенно бесполезным, так какNSSomething
уже знает, чему он соответствует. Если он не соответствует одному из протоколов,<>
вы получитеunrecognised selector ...
сбои. Это вообще не обеспечивает безопасности типов.Ответы:
В Swift 4 теперь можно объявить переменную, которая является подклассом типа и одновременно реализует один или несколько протоколов.
Чтобы сделать необязательную переменную:
или как параметр метода:
Apple объявила об этом на WWDC 2017 в Сессии 402: Что нового в Swift
источник
Вы не можете объявить переменную вроде
и не объявлять тип возвращаемого значения функции, например
Вы можете объявить как этот параметр функции, но это, в основном, преобразование.
На данный момент все, что вы можете сделать, это примерно так:
При этом технически
cell
идентиченasProtocol
.Но, что касается компилятора, он
cell
имеетUITableViewCell
только интерфейс , аasProtocol
имеет только интерфейс протоколов. Итак, когда вы хотите вызватьUITableViewCell
методы, вы должны использоватьcell
переменную. Если вы хотите вызвать метод протоколов, используйтеasProtocol
переменную.Если вы уверены, что ячейка соответствует протоколам, использовать их необязательно
if let ... as? ... {}
. лайк:источник
-> UITableViewCell<MyProtocol>
, это неверно, потому чтоUITableViewCell
это не общий тип. Я думаю, это даже не компилируется.cell
есть только методы протоколов (для компилятора).К сожалению, Swift не поддерживает соответствие протоколу объектного уровня. Однако есть несколько неудобный обходной путь, который может послужить вашим целям.
Затем, где бы вам ни понадобилось сделать что-либо, что есть в UIViewController, вы получите доступ к аспекту .viewController структуры и ко всему, что вам нужно, в аспекте протокола, вы должны ссылаться на .protocol.
Например:
Теперь, когда вам понадобится mySpecialViewController для выполнения чего-либо, связанного с UIViewController, вы просто ссылаетесь на mySpecialViewController.viewController, и всякий раз, когда вам нужно выполнить какую-либо функцию протокола, вы ссылаетесь на mySpecialViewController.protocol.
Надеюсь, Swift 4 позволит нам объявлять объект с привязанными к нему протоколами в будущем. Но пока это работает.
Надеюсь это поможет!
источник
Может я ошибаюсь, но разве вы не говорите о добавлении в
UITableCellView
класс соответствия протоколу ? В этом случае протокол распространяется на базовый класс, а не на объект. См. Документацию Apple по объявлению принятия протокола с расширением, которое в вашем случае будет примерно таким:В дополнение к уже упомянутой документации по Swift см. Также статью Нейта Кука « Общие функции для несовместимых типов» с дополнительными примерами.
Протокол Adoption сделает именно это, заставит объект придерживаться данного протокола. Однако имейте в виду обратную сторону: переменная данного типа протокола ничего не знает вне протокола. Но это можно обойти, определив протокол, в котором есть все необходимые методы / переменные / ...
Если вы хотите, чтобы общий метод, переменная соответствовала типам протокола и базового класса, вам может не повезти. Но похоже, что вам нужно определить протокол достаточно широким, чтобы иметь необходимые методы соответствия, и в то же время достаточно узким, чтобы иметь возможность адаптировать его к базовым классам без особых усилий (т.е. просто объявив, что класс соответствует протокол).
источник
Однажды у меня была похожая ситуация, когда я пытался связать мои общие подключения к взаимодействующим устройствам в раскадровках (IB не позволяет подключать выходы к протоколам, только экземпляры объектов), которую я обошел, просто замаскировав общедоступный ivar базового класса с частным вычисленным свойство. Хотя это не мешает кому-либо делать незаконные назначения как таковые, это действительно удобный способ безопасно предотвратить любое нежелательное взаимодействие с несоответствующим экземпляром во время выполнения. (т.е. предотвращать вызов методов делегата для объектов, не соответствующих протоколу.)
Пример:
«OutputReceiver» объявлен необязательным, как и частный «protocolOutputReceiver». Всегда обращаясь к outputReceiver (также известному как делегат) через последний (вычисляемое свойство), я эффективно отфильтровываю любые объекты, которые не соответствуют протоколу. Теперь я могу просто использовать необязательную цепочку для безопасного вызова объекта делегата, независимо от того, реализует ли он протокол или даже существует.
Чтобы применить это к вашей ситуации, вы можете иметь общедоступный ivar типа YourBaseClass? (в отличие от AnyObject) и используйте частное вычисляемое свойство для обеспечения соответствия протоколу. FWIW.
источник