2014-09-06 19 views
6

Voglio implementare un protocollo pigro/in linea in Swift. Così nel punto dell'attuazione avrò accesso alle variabili al di fuori del campo di applicazione del protocollo,Lazy/inline implementa un protocollo in Swift

Idem come implementare un'interfaccia in Java senza dichiarare una classe:

class MyClass:UIView { 
    var someComponent:SomeInnerComponent = SomeInnerComponent(); 
    var count:Int = 0; 

    var a = :SomeProtocol { //<----- IS THIS POSSIBLE, IF YES HOW ? 
     func a0() {MyClass.count--} 
     func a1() {MyClass.count++} 
    } 

    someComponenet.delegate = a; 
} 

protocol SomeProtocol { 
    func a0() 
    func a1() 
} 

editing ----

grazie a questa soluzione, e non ho visto come accedere a una variabile della classe genitore. tutti gli esempi mostrano una classe anonima ma nessuno degli esempi accede alle variabili padre.

+0

Si noti che tutte le risposte di quest'altra domanda che discutere di chiusure affronta anche le tue domande su "l'accesso alle variabili al di fuori del campo di applicazione del protocollo." Le chiusure possono catturare e modificare quelle variabili. –

+0

grazie a questa soluzione, e non ho visto come accedere a una variabile della classe genitore. tutti gli esempi mostrano una classe anonima ma nessuno degli esempi accede alle variabili padre. –

risposta

3

Quello che stai cercando è una classe interna (non necessariamente anonimo), dichiarata in un ambito che consente di accedere alla variabile count di un'istanza MyClass e che adotta un protocollo definito in un ambito diverso. In questo momento Swift ha alcuni di quei pezzi, ma non sembra che tu possa metterli tutti insieme in un modo così conciso come quello che potresti cercare.

Si potrebbe pensare che dichiara una classe interna:

class MyView: UIView { 
    let someComponent = SomeInnerComponent() // type SomeInnerComponent is inferred 
    var count = 0 // type Int is inferred 
    class Helper: SomeProtocol { 
     func a0() { count-- } // ERROR 
     // ... 
    } 
    init() { 
     someComponent.delegate = Helper() 
    } 
} 

Ma non funzionerà, perché count è implicitamente self.count, dove self è un'istanza Helper, non l'istanza MyView che "possiede" il Helper esempio. E non c'è un modo per fare riferimento all'istanza MyView (o alle sue proprietà) dai metodi di Helper, perché si potrebbe anche costruire un MyView.Helper() senza avere un'istanza esistente MyView. Le classi interne (o tipi annidati in generale) in Swift annidano solo in ambito lessicale, non in proprietà esistenziale. (O per dirla in un altro modo, dato che hai fatto riferimento a Java: tutte le classi interne di Swift sono come classi interne statiche in Java. Non esiste una classe interna non statica.) Se questa è una caratteristica che ti piacerebbe, però, it's probably worth telling Apple you want it.

Si potrebbe anche provare a dichiarare Helper all'interno di MyView.init() - in Swift è possibile annidare le definizioni dei tipi ovunque, comprese funzioni interne o metodi di altri tipi. Definito lì, può fare riferimento alle proprietà di MyView. Tuttavia, ora le informazioni sul tipo per Helper sono visibili solo all'interno di MyView.init(), quindi quando lo si assegna a someComponent.delegate (il cui tipo è solo SomeProtocol), non è possibile utilizzarlo ... questo arresta il compilatore, anche. (Questo è un altro bug to report, ma è difficile dire se il bug è in realtà "il crash del compilatore sull'uso valido" o "il codice è cattivo, ma il compilatore si blocca invece di produrre errori".)

La soluzione più vicina che posso venire con simile a questa:

class SomeInnerComponent { 
    var delegate: SomeProtocol? 
} 
protocol SomeProtocol { 
    func a0() 
    func a1() 
} 
class MyClass { 
    var someComponent = SomeInnerComponent() 
    var count = 0 
    struct Helper: SomeProtocol { 
     var dec:() ->() 
     var inc:() ->() 
     func a0() { dec() } 
     func a1() { inc() } 
    } 
    init() { 
     someComponent.delegate = Helper(
      dec: { self.count -= 1 }, // see note below 
      inc: { self.count += 1 } 
     ) 
    } 
} 

Come funziona:

  • Helper è una struct interna (potrebbe essere una classe, ma una struct è più semplice)
  • Implementa i metodi a0 e a1, soddisfacendo i requisiti di SomeProtocol
  • Le implementazioni di a0 e a1 chiamata attraverso le chiusure dec e inc, che sono memorizzati proprietà (aka variabili di istanza) del Helper struct
  • Si scrive (o altro specificare) queste chiusure quando si costruisce un'istanza Helper (utilizzando il predefinito inizializzatore membro-saggio, Helper(dec: (Void -> Void), inc: (Void -> Void)))
  • Poiché è possibile scrivere le chiusure durante l'inizializzazione di un Helper, tali dispositivi di chiusura in grado di catturare le variabili in cui si sta chiamando l'inizializzazione, tra cui l'implicita self che fa riferimento all'istanza MyClass creazione del Helper.

Hai bisogno di entrambi a0/a1 e dec/inc perché è necessario chiusure (il secondo), non i metodi, per catturare lo stato di inclusione. E anche se chiusure e funzioni/metodi sono in molti modi intercambiabili, non è possibile creare un'implementazione metodo/func assegnando una chiusura a un nome metodo/funzione. (Sarebbe una storia diversa, se necessario SomeProtocol proprietà di chiusura, invece di metodi, ma sto assumendo SomeProtocol non è qualcosa sotto il vostro controllo.)

In ogni caso, questo è una specie di un sacco di testo standard e uno strato di astrazione che potresti non aver realmente bisogno, quindi probabilmente vale la pena esaminare altri modi per progettare il tuo codice.


Nota: il mio esempio utilizza la chiusura { self.count -= 1 } in cui ci si potrebbe aspettare { self.count-- }. Quest'ultimo non funziona perché è un'espressione con un valore, quindi Swift lo interpreterà come abbreviazione del valore di ritorno della chiusura. Quindi si lamenterà che è stata assegnata una chiusura () -> Int a una proprietà che si aspetta una chiusura () ->() (alias Void -> Void). Utilizzare -= 1 invece funziona attorno a questo problema.

+0

grazie !, bel trucco :), questo esempio può servire a me, perché ho bisogno di metodi 'dec', 'inc'. posso usare a0, 01 direttamente? è una limitazione in rapido? –

1

vorrei andare per un approccio diverso, so che questo è un argomento piuttosto vecchio, ma solo nel caso in cui qualcun altro lotte con questo problema:

class MyClass:UIView { 
    var someComponent:SomeInnerComponent = SomeInnerComponent(); 
    var count:Int = 0; 

    init(){ 
    // Assign the delegate or do it somewhere else to your preference: 
    someComponenet.delegate = ProtocolImplementation(myClass: self); 
    } 

    private class ProtocolImplementation: SomeProtocol { 
     let selfReference: MyClass   

     init(myClass: MyClass){ 
     selfReference = myClass 
     } 

     public func a0(){ 
     selfReference.count-- 
     } 

     public func a1(){ 
      selfReference.count++ 
     } 
    } 
} 

protocol SomeProtocol { 
    func a0() 
    func a1() 
} 

Seguendo questo approccio è anche possibile includere la stessa multipla protocollo volte, diciamo che il tuo protocollo supporta un generico e vuoi implementarlo due volte. SomeProtocol < SomeObject> e SomeProtocol < OtherObject> potrebbero essere utilizzati in questo modo se necessario.

Cordiali saluti

Problemi correlati