2014-09-18 11 views
14

Per rendere il mio codice più facile da leggere, sto usando alias di tipo in Swift per vari tipi di chiusure. Ho il seguente set di base di chiusure:Come faccio a "generare" un alias del tipo di chiusura in Swift?

public typealias FailureClosure = (error: NSError?) -> Void 
public typealias ProgressClosure = (progress: Float32) -> Void 
public typealias BasicClosure =() -> Void 

vorrei aggiungere una chiusura typealias che supporta gli array generici, ma io non riesco a capire la sintassi per esso. Questo è quanto sono in grado di ottenere, ma ottengo l'errore di compilazione "Utilizzo di tipo non dichiarato 'T'"

public typealias ArrayClosure = <T>(array:[T]?) -> Void 

Qualcuno sa come fare questo? O anche se è possibile?

+0

Un dettaglio che vorrei sottolineare è che è possibile definire "typealias" nelle classi in cui è definito 'T'. Se 'T' è un tipo generico del tuo oggetto, puoi creare e usare' typealias'es come normale all'interno di quella classe. Una nota se lo fai: al di fuori della classe, le 'typealias' non sono definite, quindi vedranno ancora la sintassi completa della chiusura. – vrwim

risposta

10

No, questo non è attualmente possibile. Se fosse possibile, la sintassi che ci si aspetta sarebbe:

public typealias ArrayClosure<T> = (array:[T]?) -> Void 

Si potrebbe quindi utilizzarlo come ArrayClosure<Int>. Ma non è attualmente legale.

Detto questo, non raccomando questi alias di questo tipo. Oscurano più di quanto non illuminino. Confronta questa firma:

func foo(onError: FailureClosure) 

con:

func foo(onError: NSError? -> Void) 

Per salvare solo un paio di caratteri, si forza il chiamante di indovinare cosa FailureClosure passaggi. I tag error o progress non sono di grande aiuto (è comunque necessario utilizzare progress in ...).

L'un caso che fa un sacco di senso è intorno progress, ma penso che il tipo che si desidera c'è:

public typealias Progress = Float32 

Non fraintendetemi qui, tipo aliasing può essere molto utile quando si crea un nuovo tipo. Progress è un tipo che sembra essere implementato come float. Ma molto di quello che stai facendo (sicuramente con ArrayClosure, e in misura minore con gli altri) è solo creando una nuova sintassi senza creare un nuovo tipo, e questo è più spesso confuso che utile.


Per chiamare il vostro esempio specifico, e perché l'uso eccessivo di tipo alias può causare a complicare il vostro disegno:

func foo(failure: ((error: NSError?) ->())? = nil) 

Hai ragione che questo è davvero complicato. Confronta:

func foo(failure: NSError -> Void = {_ in return}) 

Due grandi cambiamenti qui. Non c'è motivo di avere un blocco di errore che richiede un errore opzionale. Passa sempre un errore (se non ci sono errori, perché si dovrebbe chiamare failure?). E non c'è motivo per cui il blocco di errore sia opzionale. Se vuoi davvero un valore predefinito, basta fare in modo che il valore predefinito non faccia nulla. Sono stati eliminati due optionals e tutto il codice di consumo e implementazione diventa più semplice. Pensa sempre attentamente se qualcosa deve assolutamente essere opzionale. Gli optionals aggiungono molta complessità; non aggiungerli leggermente.

Personalmente, mi piacerebbe probabilmente fare questo con un sovraccarico, invece, in molti casi:

func foo(#failure: NSError -> Void) { ... } 

func foo() { 
    foo(failure:{ _ in return }) 
} 

Penso solo che sia un po 'più facile da capire cosa sta succedendo. Ma in entrambi i casi va bene.


EDIT (dicembre 2014): Dopo aver scritto Swift per qualche mese, ho cresciuto più affezionato @ approccio di David nei commenti qui sotto, che è quello di utilizzare un optional per la chiusura, ma non per l'errore. In particolare, data la sintassi di concatenazione opzionale di Swift (failure?()), spesso risulta più chiara.

func foo(failure: (NSError -> Void)? = nil) 
+0

Grazie per questo. Dai tuoi esempi ha senso che l'uso di alias di tipo per chiusure è fondamentalmente solo creando una nuova sintassi, penso che renda il codice più leggibile. Prendiamo ad esempio questa funzione: 'func foo (errore: ((errore: NSError?) ->())? = Nil)', è abbastanza difficile capire cosa sta succedendo qui, per non parlare di tutte le parentesi e la sintassi. Da qui l'uso di un 'typealias'. A meno che tu non possa suggerire un metodo più semplice per dichiarare un tale metodo. –

+0

Nel tuo esempio, non è necessario un optional. Probabilmente mi consiglia di utilizzare qui i sovraccarichi piuttosto che un valore predefinito, ma puoi semplicemente fare in modo che il tuo valore predefinito sia una chiusura che non fa nulla, specialmente dal momento che restituisce 'Void'. Ciò semplificherà anche il tuo codice successivo. Annidare le cose in tipografia favorisce questo tipo di firme complicate, che quindi sono difficili da utilizzare in molti casi. (L'abbiamo visto molto con blocchi molto complessi in ObjC.) –

+0

È vero che se si dispone di un tipo molto complesso, e veramente bisogno di quel tipo, e si usa spesso quel tipo, vale la pena creare un alias per esso. Ma questo non dovrebbe diventare il tuo modello normale. Come vedi, si imbatte in molti piccoli casi d'angolo in cui non funziona nemmeno. –

Problemi correlati