Я, наконец, мотивировал себя обновить свой ответ, вы можете пересмотреть его принятие.
акашивский
@akashivskyy Я принимаю ваш ответ, потому что он лучше демонстрирует все доступные варианты и их плюсы и минусы.
Сельвин
Ответы:
506
1. Использование реализаций по умолчанию (предпочтительно).
protocolMyProtocol{func doSomething()}extensionMyProtocol{func doSomething(){/* return a default value or just leave empty */}}structMyStruct:MyProtocol{/* no compile error */}
преимущества
Нет времени выполнения Objective C (ну, по крайней мере, явно). Это означает, что вы можете соответствовать структурам, перечислениям и не NSObjectклассам. Кроме того, это означает, что вы можете воспользоваться мощной системой генериков.
Вы всегда можете быть уверены, что все требования выполняются при обнаружении типов, соответствующих такому протоколу. Это всегда либо конкретная реализация, либо реализация по умолчанию. Так ведут себя «интерфейсы» или «контракты» на других языках.
Недостатки
В случае отсутствия Voidтребований вам необходимо иметь разумное значение по умолчанию , что не всегда возможно. Однако, когда вы сталкиваетесь с этой проблемой, это означает, что либо у такого требования действительно не должно быть реализации по умолчанию, либо вы допустили ошибку при разработке API.
Вы не можете отличить реализацию по умолчанию от реализации вообще , по крайней мере без решения этой проблемы с помощью специальных возвращаемых значений. Рассмотрим следующий пример:
Если вы предоставляете реализацию по умолчанию, которая просто возвращает true- это нормально на первый взгляд. Теперь рассмотрим следующий псевдокод:
finalclassSomeParser{func parse(data:Data)->[Any]{if/* delegate.validate(value:) is not implemented */{/* parse very fast without validating */}else{/* parse and validate every value */}}}
Нет способа реализовать такую оптимизацию - вы не можете знать, реализует ли ваш делегат метод или нет.
Несмотря на то, что существует ряд различных способов преодоления этой проблемы (использование необязательных замыканий, разные объекты-делегаты для разных операций и многие другие), этот пример ясно представляет проблему.
2. Использование @objc optional.
@objc protocolMyProtocol{@objc optionalfunc doSomething()}classMyClass:NSObject,MyProtocol{/* no compile error */}
преимущества
Реализация по умолчанию не требуется. Вы просто объявляете необязательный метод или переменную, и вы готовы к работе.
Недостатки
Это серьезно ограничивает возможности вашего протокола, требуя совместимости всех соответствующих типов с Objective-C. Это означает, что только классы, которые наследуются, NSObjectмогут соответствовать такому протоколу. Нет структур, нет перечислений, нет связанных типов.
Вы всегда должны проверять, реализован ли необязательный метод , либо вызывая его, либо проверяя, реализует ли его соответствующий тип. Это может привести к множеству шаблонов, если вы часто вызываете дополнительные методы.
Посмотрите на улучшенный способ дополнительных методов в Swift с использованием расширения (ниже)
Даниэль Канаан
1
И как можно проверить поддержку необязательного метода протокола в экземпляре? respondsToSelector?
devios1
3
Только не делай этого! Swift не поддерживает необязательный метод в протоколах по причине.
fpg1503
2
Этот метод не поддерживает опциональные параметры. Т.е. ты не можешь сделать этоoptional func doSomething(param: Int?)
SoftDesigner
4
Конечно, этот ответ по сути неправильный в наши дни, годы спустя. Сегодня в Swift вы просто добавляете расширение для реализации по умолчанию, это основной аспект Swift. (Как показывают все современные ответы ниже.) В настоящее время было бы неправильно добавлять флаг objc.
Толстяк
394
В Swift 2 и далее можно добавлять реализации протокола по умолчанию. Это создает новый способ дополнительных методов в протоколах.
protocolMyProtocol{func doSomethingNonOptionalMethod()func doSomethingOptionalMethod()}extensionMyProtocol{func doSomethingOptionalMethod(){// leaving this empty
}}
Это не очень хороший способ создания дополнительных методов протокола, но дает вам возможность использовать структуры в обратных вызовах протокола.
Это, наверное, самый чистый способ сделать это в Swift. Жаль, что это не работает до Swift 2.0.
Энтальпи
13
@ MattQuiros Я обнаружил, что вам действительно нужно объявить функцию в определении протокола, в противном случае функция расширения без операции не будет переопределена в ваших классах, которые соответствуют протоколу.
Ян Пирс,
3
@IanPearce является правильным, и это, кажется, было разработано. В лекции «Протоколно-ориентированное программирование» (408) на WWDC они рассказывают о методах в основном протоколе, которые представляют собой «точки настройки», предлагаемые для соответствующих типов. Требуемая точка настройки не получает определения в расширении; необязательный пункт делает. Методы протокола, которые, как правило, не следует настраивать, полностью объявляются / определяются в расширении, чтобы запретить настраиваемые типы для настройки, если только вы специально не приведете к его dynamicType, чтобы показать, что вы хотите пользовательскую реализацию конформера.
Матиас
4
@FranklinYu Вы можете сделать это, но затем вы вносите изменения в свой дизайн API с помощью «throws», где это фактически не требуется. Мне больше нравится идея «микро-протоколов». Например, каждый метод подтверждает один протокол, а затем вы можете проверить: является ли объект протоколом
Darko
3
@ Antoine Я думаю, вы должны сделать функцию в расширении общедоступной, так как функции в протоколах общедоступны по определению. Ваше решение не будет работать, когда протокол будет использоваться за пределами его модуля.
Вадим Айзенберг
39
Так как есть несколько ответов о том, как использовать необязательный модификатор и атрибут @objc для определения необязательного протокола требований, я приведу пример того, как использовать расширения протокола для определения необязательного протокола.
Ниже код Swift 3. *.
/// Protocol has empty default implementation of the following methods making them optional to implement:
/// `cancel()`
protocolCancelable{/// default implementation is empty.
func cancel()}extensionCancelable{func cancel(){}}classPlane:Cancelable{//Since cancel() have default implementation, that is optional to class Plane
}let plane =Plane()
plane.cancel()//Print out *UnitedAirlines can't cancelable*
Обратите внимание, что методы расширения протокола не могут быть вызваны кодом Objective-C, и хуже всего то, что команда Swift не исправит это. https://bugs.swift.org/browse/SR-492
Отличная работа! Это должен быть правильный ответ, так как он решает проблему без вовлечения Objective C.
Лукас
действительно хороший!
tania_S
из быстрой документации - «Требования к протоколу с реализациями по умолчанию, предоставляемыми расширениями, отличаются от необязательных требований к протоколу. Хотя соответствующие типы не должны обеспечивать свою собственную реализацию, требования с реализациями по умолчанию могут вызываться без необязательного сцепления».
Mec Os
34
Другие ответы, связанные с пометкой протокола как «@objc», не работают при использовании специфичных для swift типов.
structInfo{var height:Intvar weight:Int}@objc protocolHealth{func isInfoHealthy(info:Info)->Bool}//Error"Method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C"
Чтобы объявить необязательные протоколы, которые хорошо работают с swift, объявите функции как переменные вместо func.
classHuman:Health{var isInfoHealthy:(Info)->(Bool)?={ info inif info.weight <200&& info.height >72{returntrue}returnfalse}//Or leave out the implementation and declare it as:
//var isInfoHealthy: (Info) -> (Bool)?
}
Затем вы можете использовать "?" проверить, была ли реализована функция
С этим решением вы не можете получить доступ к себе из любой функции протокола. Это может вызвать проблемы в некоторых случаях!
Джордж Грин
1
@ GeorgeGreen Вы можете получить доступ к себе. Отметьте переменную функции как ленивую и используйте список захвата внутри замыкания .
Заг
Только классы, протоколы, методы и свойства могут использовать @objc. Если вы используете параметр Enum в определении метода протокола @ objc, вы обречены.
Хуншань
1
@ Khunshan, этот метод не требует пометки @ objc, на что вы ссылаетесь?
Заг
Это информация по теме, которую перечисления нельзя использовать между swift и objc, которые для других операторов можно связать с ключевым словом @objc.
Я думаю, что прежде чем спросить, как вы можете реализовать дополнительный метод протокола, вы должны спросить почему вы должны его реализовать.
Если мы думаем о быстрых протоколах как интерфейсе в классическом объектно-ориентированном программировании, дополнительные методы не имеют особого смысла, и, возможно, лучшим решением будет создание реализации по умолчанию или разделение протокола на набор протоколов (возможно, с некоторыми отношениями наследования). между ними) для представления возможной комбинации методов в протоколе.
Вот очень простой пример ТОЛЬКО для быстрых классов, а не для структур или перечислений. Обратите внимание, что метод протокола, являющийся необязательным, имеет два уровня необязательной цепочки при воспроизведении. Также классу, принимающему протокол, необходим атрибут @objc в своем объявлении.
@objc protocolCollectionOfDataDelegate{optionalfunc indexDidChange(index:Int)}@objc classRootView:CollectionOfDataDelegate{var data =CollectionOfData()init(){
data.delegate =self
data.indexIsNow()}func indexDidChange(index:Int){
println("The index is currently: \(index)")}}classCollectionOfData{var index :Int?weakvar delegate :CollectionOfDataDelegate?func indexIsNow(){
index =23
delegate?.indexDidChange?(index!)}}
Вы можете описать немного больше « два уровня по желанию , в игре » часть, а именно это: delegate?.indexDidChange?(index!)?
Unheilig
2
Если бы мы написали в протоколе такой необязательный метод, как этот: protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) } тогда вы бы вызвали его без вопросительного знака: delegate?.indexDidChange(index!) когда вы устанавливаете необязательное требование для метода в протоколе, тип, который будет соответствовать ему, может НЕ реализовывать этот метод , поэтому ?используется для проверки реализации, и если ее нет, программа не будет аварийно завершена. @Unheilig
Благословение Лопес
weak var delegate : CollectionOfDataDelegate?(обеспечить слабую ссылку?)
Алфи Хансен
@BlessingLopes Можете ли вы добавить свое объяснение delegate?использования в свой ответ? Эта информация должна действительно принадлежать другим в будущем. Я хочу выразить это, но эта информация действительно должна быть в ответе.
Джонатон Саллингер
3
если вы хотите сделать это в чистом swift, лучшим способом будет предоставить реализацию по умолчанию, если вы возвращаете тип Swift, например, struct с типами Swift
Существует два способа создания необязательного метода в быстром протоколе.
1 - Первый вариант - пометить ваш протокол с помощью атрибута @objc. Хотя это означает, что он может быть принят только классами, это означает, что вы помечаете отдельные методы как необязательные, например:
2 - более быстрый способ: этот вариант лучше. Напишите реализации по умолчанию необязательных методов, которые ничего не делают, как это.
protocolMyProtocol{func optionalMethod()func notOptionalMethod()}extensionMyProtocol{func optionalMethod(){//this is a empty implementation to allow this method to be optional
}}
Swift имеет функцию под названием extension, которая позволяет нам предоставлять реализацию по умолчанию для тех методов, которые мы хотим использовать не обязательно.
Определите функцию в протоколе и создайте расширение для этого протокола, затем создайте пустую реализацию для функции, которую вы хотите использовать как опциональную.
Для определения OptionalProtocolв swift вы должны использовать @objcключевое слово перед Protocolобъявлением и attribute/ methodобъявление внутри этого протокола. Ниже приведен пример необязательного свойства протокола.
@objc protocolProtocol{@objc optionalvar name:String?}classMyClass:Protocol{// No error
}
Хотя это может ответить на вопрос, лучше добавить описание того, как этот ответ может помочь решить проблему. Пожалуйста, прочитайте Как я могу написать хороший ответ, чтобы узнать больше.
Ответы:
1. Использование реализаций по умолчанию (предпочтительно).
преимущества
Нет времени выполнения Objective C (ну, по крайней мере, явно). Это означает, что вы можете соответствовать структурам, перечислениям и не
NSObject
классам. Кроме того, это означает, что вы можете воспользоваться мощной системой генериков.Вы всегда можете быть уверены, что все требования выполняются при обнаружении типов, соответствующих такому протоколу. Это всегда либо конкретная реализация, либо реализация по умолчанию. Так ведут себя «интерфейсы» или «контракты» на других языках.
Недостатки
В случае отсутствия
Void
требований вам необходимо иметь разумное значение по умолчанию , что не всегда возможно. Однако, когда вы сталкиваетесь с этой проблемой, это означает, что либо у такого требования действительно не должно быть реализации по умолчанию, либо вы допустили ошибку при разработке API.Вы не можете отличить реализацию по умолчанию от реализации вообще , по крайней мере без решения этой проблемы с помощью специальных возвращаемых значений. Рассмотрим следующий пример:
Если вы предоставляете реализацию по умолчанию, которая просто возвращает
true
- это нормально на первый взгляд. Теперь рассмотрим следующий псевдокод:Нет способа реализовать такую оптимизацию - вы не можете знать, реализует ли ваш делегат метод или нет.
Несмотря на то, что существует ряд различных способов преодоления этой проблемы (использование необязательных замыканий, разные объекты-делегаты для разных операций и многие другие), этот пример ясно представляет проблему.
2. Использование
@objc optional
.преимущества
Недостатки
Это серьезно ограничивает возможности вашего протокола, требуя совместимости всех соответствующих типов с Objective-C. Это означает, что только классы, которые наследуются,
NSObject
могут соответствовать такому протоколу. Нет структур, нет перечислений, нет связанных типов.Вы всегда должны проверять, реализован ли необязательный метод , либо вызывая его, либо проверяя, реализует ли его соответствующий тип. Это может привести к множеству шаблонов, если вы часто вызываете дополнительные методы.
источник
respondsToSelector
?optional func doSomething(param: Int?)
В Swift 2 и далее можно добавлять реализации протокола по умолчанию. Это создает новый способ дополнительных методов в протоколах.
Это не очень хороший способ создания дополнительных методов протокола, но дает вам возможность использовать структуры в обратных вызовах протокола.
Я написал небольшое резюме здесь: https://www.avanderlee.com/swift-2-0/optional-protocol-methods/
источник
Так как есть несколько ответов о том, как использовать необязательный модификатор и атрибут @objc для определения необязательного протокола требований, я приведу пример того, как использовать расширения протокола для определения необязательного протокола.
Ниже код Swift 3. *.
Обратите внимание, что методы расширения протокола не могут быть вызваны кодом Objective-C, и хуже всего то, что команда Swift не исправит это. https://bugs.swift.org/browse/SR-492
источник
Другие ответы, связанные с пометкой протокола как «@objc», не работают при использовании специфичных для swift типов.
Чтобы объявить необязательные протоколы, которые хорошо работают с swift, объявите функции как переменные вместо func.
А затем реализовать протокол следующим образом
Затем вы можете использовать "?" проверить, была ли реализована функция
источник
Вот конкретный пример с шаблоном делегирования.
Настройте свой протокол:
Установите делегата для класса и реализуйте протокол. Смотрите, что необязательный метод не должен быть реализован.
Одна важная вещь заключается в том, что дополнительный метод является необязательным и требует "?" при звонке. Упомяните второй знак вопроса.
источник
В Swift 3.0
Это сэкономит ваше время.
источник
@objc
сейчас нужен всем членам?required
флагом, но с ошибками:required
может использоваться только в объявлениях 'init'.optional
ключевое слово перед каждым методом.источник
@objc
не только протокол.Чистый подход Swift с наследованием протокола:
источник
Чтобы проиллюстрировать механику ответа Антуана:
источник
Я думаю, что прежде чем спросить, как вы можете реализовать дополнительный метод протокола, вы должны спросить почему вы должны его реализовать.
Если мы думаем о быстрых протоколах как интерфейсе в классическом объектно-ориентированном программировании, дополнительные методы не имеют особого смысла, и, возможно, лучшим решением будет создание реализации по умолчанию или разделение протокола на набор протоколов (возможно, с некоторыми отношениями наследования). между ними) для представления возможной комбинации методов в протоколе.
Для дальнейшего чтения см. Https://useyourloaf.com/blog/swift-optional-protocol-methods/ , в котором содержится отличный обзор по этому вопросу.
источник
Немного не по теме из первоначального вопроса, но он основывается на идее Антуана, и я подумал, что это может кому-то помочь.
Вы также можете сделать вычисляемые свойства необязательными для структур с расширениями протокола.
Вы можете сделать свойство по протоколу необязательным
Реализовать фиктивное свойство в расширении протокола
И теперь вы можете использовать структуры, в которых реализовано или не реализовано необязательное свойство.
Я также написал в своем блоге , как настраивать необязательные свойства в протоколах Swift , и я буду постоянно обновлять их в случае изменений в выпусках Swift 2.
источник
Как создать необязательные и обязательные методы делегата.
источник
Вот очень простой пример ТОЛЬКО для быстрых классов, а не для структур или перечислений. Обратите внимание, что метод протокола, являющийся необязательным, имеет два уровня необязательной цепочки при воспроизведении. Также классу, принимающему протокол, необходим атрибут @objc в своем объявлении.
источник
delegate?.indexDidChange?(index!)
?protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) }
тогда вы бы вызвали его без вопросительного знака:delegate?.indexDidChange(index!)
когда вы устанавливаете необязательное требование для метода в протоколе, тип, который будет соответствовать ему, может НЕ реализовывать этот метод , поэтому?
используется для проверки реализации, и если ее нет, программа не будет аварийно завершена. @Unheiligweak var delegate : CollectionOfDataDelegate?
(обеспечить слабую ссылку?)delegate?
использования в свой ответ? Эта информация должна действительно принадлежать другим в будущем. Я хочу выразить это, но эта информация действительно должна быть в ответе.если вы хотите сделать это в чистом swift, лучшим способом будет предоставить реализацию по умолчанию, если вы возвращаете тип Swift, например, struct с типами Swift
пример :
тогда вы можете реализовать протокол без определения каждой функции
источник
Существует два способа создания необязательного метода в быстром протоколе.
1 - Первый вариант - пометить ваш протокол с помощью атрибута @objc. Хотя это означает, что он может быть принят только классами, это означает, что вы помечаете отдельные методы как необязательные, например:
2 - более быстрый способ: этот вариант лучше. Напишите реализации по умолчанию необязательных методов, которые ничего не делают, как это.
Swift имеет функцию под названием extension, которая позволяет нам предоставлять реализацию по умолчанию для тех методов, которые мы хотим использовать не обязательно.
источник
Один из вариантов - сохранить их как необязательные переменные функции:
источник
Определите функцию в протоколе и создайте расширение для этого протокола, затем создайте пустую реализацию для функции, которую вы хотите использовать как опциональную.
источник
Для определения
Optional
Protocol
в swift вы должны использовать@objc
ключевое слово передProtocol
объявлением иattribute
/method
объявление внутри этого протокола. Ниже приведен пример необязательного свойства протокола.источник
Поместите
@optional
перед методами или свойствами.источник
@optional
даже не правильное ключевое слово. Это такoptional
, и вы должны объявить класс и протокол с@objc
атрибутом.