2014-06-09 12 views
26

Diciamo che ho la seguente classe di Swift (che ha evidenti problemi)metodi di chiamata da Swift initializer

class MyClass { 
    let myProperty: String 

    init() { 
     super.init() 
     self.setupMyProperty() 
    } 

    func setupMyProperty() { 
     myProperty = "x" 
    } 
} 

Questa è eccessivamente semplificata ma sto fondamentalmente cercando di delegare l'inizializzazione del myProperty nel metodo setupMyProperty() . È un pattern che uso spesso per suddividere le diverse parti del setup di una classe.

Ma ovviamente, non posso chiamare self fino a quando non è stato eseguito il super inizializzatore e non riesco a eseguire il super inizializzatore finché non sono state impostate tutte le proprietà, quindi sono in una presa 22. Sopra poiché setupMyProperty() non è considerato un inizializzatore, non sarà in grado di assegnare lo myProperty in ogni caso.

Qualcuno può dirmi come implementare questo modello in Swift?

+0

Non so se questo sarebbe ok per voi, ma è possibile assegnare un valore quando si dichiara la proprietà. 'let myProperty: String =" x "' –

+0

Sì, lo uso laddove ha senso.Ma nei casi in cui queste cose sono inizializzate in base ai parametri dati all'inizializzatore, l'inizializzatore diventa disordinato. Immagino sia un segno che la mia classe abbia troppi attributi. – mprivat

+1

Controlla sessione WWDC 403: Intermediate Swift, circa 29 min in inizializzazione della classe covers. – DogCoffee

risposta

30

dichiarano come un implicito scartare opzionale

class MyClass : NSObject { 
    var myProperty: String! 

    init() { 
     super.init() 
     self.setupMyProperty() 
    } 

    func setupMyProperty() { 
     self.myProperty = "x" 
    } 
} 

pagina 499 di "The Swift Programming Language" manuale

+1

Non ti perdi un po 'di sicurezza facendo questo? Immagino di dover essere sicuro che la proprietà sia inizializzata prima di accedervi altrimenti ci sarà un errore di runtime. E non può essere una costante per questa istanza. Immagino che questo sia il modo migliore che ho visto, semplicemente non perfetto – mprivat

+0

Sarà accessibile prima dell'inizializzazione solo nel caso in cui un altro thread cerchi di accedervi tra il super.init() e il vero "set" ... Non so se puoi bloccare la variabile finché non è completamente inizializzata (vedi http://stackoverflow.com/questions/24045895/what-is-the-swift-equivalent-to-objective-cs-synchronized, ma I ' m non è abbastanza documentato da dire se è possibile e se potrebbe essere un problema usarlo in un inizializzatore). La risposta di Bill sembra un'altra buona alternativa. – LombaX

+0

Quindi non c'è modo di mantenere 'myProperty' una costante (let)? –

2

Non è possibile utilizzare self fino a memoria del l'istanza è completamente inizializzata (a cui si punta impossibile impostare le proprietà costanti), quindi l'ordine in cui l'inizializzatore designato deve fare le cose è:

  1. inizializzare tutte le proprietà aggiunte da questa classe
  2. chiamata superclasse initializer (se v'è una superclasse)
  3. Ora è possibile utilizzare self, i metodi di chiamata, ecc

non sono sicuro se c'è una buona soluzione per il caso di una proprietà costante. Un'alternativa potrebbe essere quella di dichiarare la proprietà come un implicito scartare facoltativa, quindi è inizialmente fissata a zero:

class MyClass { 
    var myProperty: String! 
    init() { 
     super.init() // only if you actually have a superclass, and make sure this does not use `myProperty` 
     self.setupMyProperty() 
    } 

    func setupMyProperty() { 
     myProperty = "x" 
    } 
} 

stare attenti con questo, si perde un po 'di sicurezza di tipo, come myProperty può ora essere nil, e se è quando si tenta di accedervi, causerà un errore di runtime. Pertanto, esegui questa operazione solo se sei sicuro che verrà inizializzato da tutti gli inizializzatori designati, non sarà utilizzato da qualsiasi cosa chiamato prima dello setupMyProperty() nella catena di inizializzazione (ad esempio quando un inizializzatore della superclasse chiama un metodo che esegue l'override che accede a myProperty) e mai impostato su nil esplicitamente.

Inoltre, cfr. the docs, in particolare la sezione sull'ereditarietà delle classi e l'inizializzazione per l'intera roba per gli ordini di chiamata che ho spiegato sopra.

+1

'private (set) var myProperty: String' impedirebbe le nefaste alterazioni esterne al valore della proprietà – bshirley

8

Il numero setupMyProperty deve accedere a se stesso? In caso contrario, è possibile raggiungere questo obiettivo con un metodo di classe:

class MyClass: NSObject { 
    let myProperty: String 

    init() { 
     myProperty = MyClass.setupMyProperty() 
     super.init() 
    } 

    class func setupMyProperty() -> String { 
     return "foo" 
    } 
} 
+2

Il problema è che mentre funziona con l'esempio semplice, l'intero punto è inizializzare un insieme di proprietà in un metodo sub. – mprivat

1

provare a mettere setupMyProperty() in una costante. Quindi è lì prima di inizializzare e puoi chiamarlo da init(). È anche possibile accedere ai parametri come segue:

class MyClass { 
    var myProperty: String 
    let setupMyProperty : (String) -> (void) = { 
     param in 
     self.myProperty = param 
    } 

    init(x: String) { 
    // removed redundant line: super.init() 
     self.setupMyProperty(x) 
    } 
} 
+0

Non è necessario super.init() nella classe in quanto "MyClass" è una classe radice. –

+0

Certo, l'ho fatto senza pensarci. Linea offendente rimossa ora. – Tchelyzt

Problemi correlati