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.
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. –
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. –