È possibile in Swift? In caso contrario, esiste una soluzione alternativa per farlo?
È possibile in Swift? In caso contrario, esiste una soluzione alternativa per farlo?
Risposte:
protocol MyProtocol {
func doSomething()
}
extension MyProtocol {
func doSomething() {
/* return a default value or just leave empty */
}
}
struct MyStruct: MyProtocol {
/* no compile error */
}
vantaggi
Nessun runtime Objective-C è coinvolto (beh, non almeno esplicitamente). Ciò significa che puoi conformare strutture, enumerazioni e non NSObject
classi ad esso. Inoltre, questo significa che puoi sfruttare il potente sistema di generici.
Puoi sempre essere sicuro che tutti i requisiti siano soddisfatti quando incontri tipi conformi a tale protocollo. È sempre un'implementazione concreta o di default. Ecco come "interfacce" o "contratti" si comportano in altre lingue.
svantaggi
Per i non Void
requisiti, è necessario disporre di un valore predefinito ragionevole , che non è sempre possibile. Tuttavia, quando si riscontra questo problema, significa che o tale requisito non dovrebbe avere un'implementazione predefinita o che l'utente ha commesso un errore durante la progettazione dell'API.
Non è possibile distinguere tra un'implementazione predefinita e nessuna implementazione , almeno senza affrontare quel problema con valori di ritorno speciali. Considera il seguente esempio:
protocol SomeParserDelegate {
func validate(value: Any) -> Bool
}
Se si fornisce un'implementazione predefinita che restituisce true
, va bene a prima vista. Ora, considera il seguente pseudo codice:
final class SomeParser {
func parse(data: Data) -> [Any] {
if /* delegate.validate(value:) is not implemented */ {
/* parse very fast without validating */
} else {
/* parse and validate every value */
}
}
}
Non c'è modo di implementare tale ottimizzazione: non puoi sapere se il tuo delegato implementa un metodo o meno.
Sebbene esistano diversi modi per ovviare a questo problema (utilizzando chiusure opzionali, oggetti delegati diversi per diverse operazioni per citarne alcuni), questo esempio presenta chiaramente il problema.
@objc optional
.@objc protocol MyProtocol {
@objc optional func doSomething()
}
class MyClass: NSObject, MyProtocol {
/* no compile error */
}
vantaggi
svantaggi
Limita fortemente le capacità del protocollo richiedendo che tutti i tipi conformi siano compatibili con Objective-C. Ciò significa che solo le classi che ereditano NSObject
possono conformarsi a tale protocollo. Nessuna struttura, nessun enumerazione, nessun tipo associato.
È sempre necessario verificare se un metodo opzionale è implementato chiamando facoltativamente o controllando se il tipo conforme lo implementa. Questo potrebbe introdurre molta caldaia se chiamate spesso metodi opzionali.
respondsToSelector
?
optional func doSomething(param: Int?)
In Swift 2 e versioni successive è possibile aggiungere implementazioni predefinite di un protocollo. Questo crea un nuovo modo di metodi opzionali nei protocolli.
protocol MyProtocol {
func doSomethingNonOptionalMethod()
func doSomethingOptionalMethod()
}
extension MyProtocol {
func doSomethingOptionalMethod(){
// leaving this empty
}
}
Non è un modo davvero carino per creare metodi di protocollo opzionali, ma ti dà la possibilità di usare le strutture nei callback di protocollo.
Ho scritto un piccolo riassunto qui: https://www.avanderlee.com/swift-2-0/optional-protocol-methods/
Poiché ci sono alcune risposte su come utilizzare il modificatore opzionale e l'attributo @objc per definire il protocollo dei requisiti facoltativi, fornirò un esempio su come utilizzare le estensioni di protocollo per definire il protocollo opzionale.
Il codice seguente è Swift 3. *.
/// Protocol has empty default implementation of the following methods making them optional to implement:
/// `cancel()`
protocol Cancelable {
/// default implementation is empty.
func cancel()
}
extension Cancelable {
func cancel() {}
}
class Plane: Cancelable {
//Since cancel() have default implementation, that is optional to class Plane
}
let plane = Plane()
plane.cancel()
// Print out *United Airlines can't cancelable*
Si noti che i metodi di estensione del protocollo non possono essere invocati dal codice Objective-C e, peggio ancora, il team Swift non lo risolverà. https://bugs.swift.org/browse/SR-492
Le altre risposte qui che riguardano la marcatura del protocollo come "@objc" non funzionano quando si utilizzano tipi specifici di Swift.
struct Info {
var height: Int
var weight: Int
}
@objc protocol Health {
func isInfoHealthy(info: Info) -> Bool
}
//Error "Method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C"
Al fine di dichiarare protocolli opzionali che funzionano bene con swift, dichiarare le funzioni come variabili invece di funzioni.
protocol Health {
var isInfoHealthy: (Info) -> (Bool)? { get set }
}
E quindi implementare il protocollo come segue
class Human: Health {
var isInfoHealthy: (Info) -> (Bool)? = { info in
if info.weight < 200 && info.height > 72 {
return true
}
return false
}
//Or leave out the implementation and declare it as:
//var isInfoHealthy: (Info) -> (Bool)?
}
È quindi possibile utilizzare "?" per verificare se la funzione è stata implementata o meno
func returnEntity() -> Health {
return Human()
}
var anEntity: Health = returnEntity()
var isHealthy = anEntity.isInfoHealthy(Info(height: 75, weight: 150))?
//"isHealthy" is true
Ecco un esempio concreto con il modello di delega.
Configura il tuo protocollo:
@objc protocol MyProtocol:class
{
func requiredMethod()
optional func optionalMethod()
}
class MyClass: NSObject
{
weak var delegate:MyProtocol?
func callDelegate()
{
delegate?.requiredMethod()
delegate?.optionalMethod?()
}
}
Impostare il delegato su una classe e implementare il protocollo. Verifica che non è necessario implementare il metodo opzionale.
class AnotherClass: NSObject, MyProtocol
{
init()
{
super.init()
let myInstance = MyClass()
myInstance.delegate = self
}
func requiredMethod()
{
}
}
Una cosa importante è che il metodo opzionale è opzionale e necessita di un "?" quando si chiama. Menziona il secondo punto interrogativo.
delegate?.optionalMethod?()
In Swift 3.0
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
Ti farà risparmiare tempo.
@objc
è necessaria per tutti i membri ora?
required
flag, ma con errori: required
può essere utilizzato solo su dichiarazioni "init".
optional
parola chiave prima di ogni metodo.@objc
, non solo il protocollo.
Per illustrare i meccanismi della risposta di Antoine:
protocol SomeProtocol {
func aMethod()
}
extension SomeProtocol {
func aMethod() {
print("extensionImplementation")
}
}
class protocolImplementingObject: SomeProtocol {
}
class protocolImplementingMethodOverridingObject: SomeProtocol {
func aMethod() {
print("classImplementation")
}
}
let noOverride = protocolImplementingObject()
let override = protocolImplementingMethodOverridingObject()
noOverride.aMethod() //prints "extensionImplementation"
override.aMethod() //prints "classImplementation"
Penso che prima di chiederti come implementare un metodo di protocollo opzionale, dovresti chiederti perché dovresti implementarne uno.
Se pensiamo di protocolli veloci come interfaccia nella classica programmazione orientata agli oggetti, metodi facoltativi non hanno molto senso, e forse una soluzione migliore sarebbe quella di creare implementazione di default, o separare il protocollo in una serie di protocolli (magari con alcune relazioni di ereditarietà tra loro) per rappresentare la possibile combinazione di metodi nel protocollo.
Per ulteriori informazioni, consultare https://useyourloaf.com/blog/swift-optional-protocol-methods/ , che offre un'eccellente panoramica sull'argomento.
Leggermente fuori tema rispetto alla domanda originale, ma sviluppa l'idea di Antoine e ho pensato che potesse aiutare qualcuno.
È inoltre possibile rendere facoltative le proprietà calcolate per le strutture con estensioni di protocollo.
È possibile rendere facoltativa una proprietà sul protocollo
protocol SomeProtocol {
var required: String { get }
var optional: String? { get }
}
Implementare la proprietà calcolata fittizia nell'estensione del protocollo
extension SomeProtocol {
var optional: String? { return nil }
}
E ora puoi usare le strutture che hanno o non hanno implementata la proprietà opzionale
struct ConformsWithoutOptional {
let required: String
}
struct ConformsWithOptional {
let required: String
let optional: String?
}
Ho anche scritto come fare proprietà opzionali nei protocolli Swift sul mio blog , che terrò aggiornato nel caso in cui le cose cambino attraverso le versioni di Swift 2.
Come creare metodi delegati facoltativi e richiesti.
@objc protocol InterViewDelegate:class {
@objc optional func optfunc() // This is optional
func requiredfunc()// This is required
}
Ecco un esempio molto semplice SOLO per le classi veloci e non per strutture o enumerazioni. Si noti che il metodo del protocollo è facoltativo, ha due livelli di concatenamento opzionale in gioco. Anche la classe che adotta il protocollo necessita dell'attributo @objc nella sua dichiarazione.
@objc protocol CollectionOfDataDelegate{
optional func indexDidChange(index: Int)
}
@objc class RootView: CollectionOfDataDelegate{
var data = CollectionOfData()
init(){
data.delegate = self
data.indexIsNow()
}
func indexDidChange(index: Int) {
println("The index is currently: \(index)")
}
}
class CollectionOfData{
var index : Int?
weak var delegate : CollectionOfDataDelegate?
func indexIsNow(){
index = 23
delegate?.indexDidChange?(index!)
}
}
delegate?.indexDidChange?(index!)
:?
protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) }
lo chiameresti senza il punto interrogativo: delegate?.indexDidChange(index!)
quando imposti un requisito opzionale per un metodo in un protocollo, il Tipo che sarà conforme a esso potrebbe NON implementare quel metodo , quindi ?
viene usato per verificare l'implementazione e se non ce n'è nessuno il programma non si arresterà in modo anomalo. @Unheilig
weak var delegate : CollectionOfDataDelegate?
(assicurare un riferimento debole?)
delegate?
sull'utilizzo alla tua risposta? Tali informazioni dovrebbero davvero appartenere agli altri in futuro. Voglio votare questo, ma le informazioni dovrebbero davvero essere nella risposta.
se si desidera farlo in modo rapido, il modo migliore è fornire un'implementazione predefinita in particolare se si restituisce un tipo Swift come ad esempio struct con tipi Swift
esempio :
struct magicDatas {
var damagePoints : Int?
var manaPoints : Int?
}
protocol magicCastDelegate {
func castFire() -> magicDatas
func castIce() -> magicDatas
}
extension magicCastDelegate {
func castFire() -> magicDatas {
return magicDatas()
}
func castIce() -> magicDatas {
return magicDatas()
}
}
quindi è possibile implementare il protocollo senza definire ogni funzione
Esistono due modi per creare un metodo opzionale nel protocollo rapido.
1 - La prima opzione è quella di contrassegnare il protocollo usando l'attributo @objc. Sebbene ciò significhi che può essere adottato solo dalle classi, significa che contrassegni i singoli metodi come facoltativi in questo modo:
@objc protocol MyProtocol {
@objc optional func optionalMethod()
}
2 - Un modo più veloce: questa opzione è migliore. Scrivi implementazioni predefinite dei metodi opzionali che non fanno nulla, come questo.
protocol MyProtocol {
func optionalMethod()
func notOptionalMethod()
}
extension MyProtocol {
func optionalMethod() {
//this is a empty implementation to allow this method to be optional
}
}
Swift ha una funzione chiamata extension che ci consente di fornire un'implementazione predefinita per quei metodi che vogliamo essere facoltativi.
Un'opzione è di memorizzarli come variabili di funzione opzionali:
struct MyAwesomeStruct {
var myWonderfulFunction : Optional<(Int) -> Int> = nil
}
let squareCalculator =
MyAwesomeStruct(myWonderfulFunction: { input in return input * input })
let thisShouldBeFour = squareCalculator.myWonderfulFunction!(2)
Definire la funzione nel protocollo e creare l'estensione per quel protocollo, quindi creare un'implementazione vuota per la funzione che si desidera utilizzare come facoltativa.
Per definire Optional
Protocol
rapidamente è necessario utilizzare la @objc
parola chiave prima della Protocol
dichiarazione e attribute
/ / method
dichiarazione all'interno di quel protocollo. Di seguito è riportato un esempio di Proprietà facoltativa di un protocollo.
@objc protocol Protocol {
@objc optional var name:String?
}
class MyClass: Protocol {
// No error
}
Metti @optional
di fronte metodi o proprietà.
@optional
non è nemmeno la parola chiave corretta. È optional
, e devi dichiarare la classe e il protocollo con l' @objc
attributo.