Non ci sono differenze evidenti nei tempi di compilazione per le funzioni inviate staticamente e le funzioni inviate dinamicamente. Sebbene la distribuzione dinamica abbia un sovraccarico extra in fase di esecuzione, in molti casi questo non è evidente: dovrai misurarlo.
Il problema con build incrementali è questo: quando modifico un file, sto potenzialmente cambiando la compatibilità binaria delle cose dichiarate in un file. Per esempio. quando cambio il tipo di ritorno di un metodo, tutto il codice che usa quel metodo dovrà essere ricompilato. Quindi i nostri file sorgente hanno un grafico delle dipendenze di chi usa chi. Quando cambiamo un file che si trova nella parte superiore di questo grafico di dipendenze, devono essere ricompilati anche tutti i file sottostanti e tutti i file che usano quei file e così via. Questo può essere un'enorme cascata.
Quindi il segreto della compilazione rapida è la consapevolezza di queste dipendenze e la rottura delle dipendenze. Idealmente, il tuo grafico delle dipendenze è molto superficiale.
I programmatori C ++ lo sanno molto bene, ma possono tenere traccia delle dipendenze in modo molto preciso vedendo quali file di intestazione sono inclusi. Quando modifico un file di intestazione, anche tutti i file che usano quell'intestazione devono essere ricompilati. Pertanto, i file di intestazione dovrebbero contenere solo dichiarazioni generali che dovrebbero essere stabili e non tutti i dettagli di implementazione che potrebbero essere modificati.
È qui che entra in gioco il dispatch dinamico: quando dipendo direttamente da qualche implementazione concreta, accetto di dover ricompilare quando quell'implementazione cambia, anche se il cambiamento riguarda solo un dettaglio di implementazione. Ma quando dipendo solo da un'interfaccia astratta, devo solo ricompilare quando l'interfaccia cambia. L'implementazione concreta eredita dall'interfaccia e implementa tutti i metodi richiesti da tale interfaccia. Quindi la spedizione dinamica può essere utilizzata per disaccoppiare un client dalle sue dipendenze:
Il client è accoppiato alla sua dipendenza volatile:
+------+ +------------------+
|Client|--->|VolatileDependency|
+------+ +------------------+
Il client è disaccoppiato dalla dipendenza volatile tramite un'interfaccia stabile:
+------+ +---------------+
|Client|--->|StableInterface|
+------+ +---------------+
^
|
+------------------+
|VolatileDependency|
+------------------+
Dissociando le unità di compilazione e mantenendo ogni file piccolo e focalizzato, è possibile ridurre il tempo medio per una compilazione incrementale. Tuttavia, l'aggiunta di ulteriori astrazioni rende il progetto nel suo complesso più complicato. Per Swift, questo richiede anche maggiore vigilanza da parte del programmatore per non utilizzare accidentalmente le dipendenze volatili, poiché non sembra esserci un meccanismo di importazione esplicito per classe per dichiarare le dipendenze.