#ifdef sostitutivo nella lingua Swift

In C / C ++ / Objective-C è ansible definire una macro utilizzando i preprocessori del compilatore. Inoltre, puoi includere / escludere alcune parti di codice usando i preprocessori del compilatore.

#ifdef DEBUG // Debug-only code #endif 

C’è una soluzione simile in Swift?

Si, puoi farlo.

In Swift puoi ancora utilizzare le macro del preprocessore “# if / # else / # endif” (sebbene più limitate), come per i documenti Apple . Ecco un esempio:

 #if DEBUG let a = 2 #else let a = 3 #endif 

Ora, però, devi impostare altrove il simbolo “DEBUG”. Impostalo nella sezione “Swift Compiler – Custom Flags”, “Other Swift Flags”. Si aggiunge il simbolo DEBUG con la voce -D DEBUG .

Come al solito, puoi impostare un valore diverso in Debug o in Release.

L’ho provato in codice reale e funziona; tuttavia non sembra essere riconosciuto in un parco giochi.

Puoi leggere il mio post originale qui .


NOTA IMPORTANTE: -DDEBUG=1 non funziona. Solo -D DEBUG funziona. Sembra che il compilatore stia ignorando una bandiera con un valore specifico.

Come dichiarato in Apple Docs

Il compilatore Swift non include un preprocessore. Al contrario, sfrutta gli attributi in fase di compilazione, le configurazioni di build e le funzionalità del linguaggio per ottenere la stessa funzionalità. Per questo motivo, le direttive del preprocessore non vengono importate in Swift.

Sono riuscito a ottenere ciò che volevo usando le Configurazioni di Build personalizzate:

  1. Vai al tuo progetto / seleziona il tuo objective / Impostazioni di costruzione / ricerca di bandiere personalizzate
  2. Per il tuo target scelto imposta il tuo flag personalizzato usando il prefisso -D (senza spazi bianchi), sia per Debug che per Release
  3. Fai sopra i passaggi per ogni objective che hai

Ecco come controlli la destinazione:

 #if BANANA print("We have a banana") #elseif MELONA print("Melona") #else print("Kiwi") #endif 

inserisci la descrizione dell'immagine qui

Testato usando Swift 2.2

In molte situazioni, non hai davvero bisogno di compilazione condizionale; hai solo bisogno di un comportamento condizionale che puoi triggersre e distriggersre. Per questo, è ansible utilizzare una variabile di ambiente. Questo ha l’enorme vantaggio che in realtà non è necessario ricompilare.

È ansible impostare la variabile di ambiente e triggersrla o distriggersrla facilmente nell’editor di schemi:

inserisci la descrizione dell'immagine qui

È ansible recuperare la variabile di ambiente con NSProcessInfo:

  let dic = NSProcessInfo.processInfo().environment if dic["TRIPLE"] != nil { // ... do secret stuff here ... } 

Ecco un esempio di vita reale. La mia app funziona solo sul dispositivo, perché utilizza la libreria musicale, che non esiste sul simulatore. Come, quindi, prendere schermate sul simulatore per dispositivi che non possiedo? Senza quelle schermate, non posso inviare all’AppStore.

Ho bisogno di dati falsi e un modo diverso di elaborarli . Ho due variabili di ambiente: una che, una volta accesa, dice all’app di generare i dati falsi dai dati reali mentre è in esecuzione sul mio dispositivo; l’altro che, quando acceso, utilizza i dati falsi (non la libreria musicale mancante) mentre è in esecuzione sul simulatore. Attivare / distriggersre ciascuna di queste modalità speciali è facile grazie alle caselle di controllo della variabile di ambiente nell’editor Scheme. E il vantaggio è che non posso usarli accidentalmente nella build del mio App Store, perché l’archiviazione non ha variabili d’ambiente.

Un importante cambiamento ifdef sostituzione di ifdef arrivato con Xcode 8. cioè l’uso delle condizioni di compilazione triggers .

Fare riferimento a Building and Linking in Xcode 8 Release note .

Nuove impostazioni di build

Nuova impostazione: SWIFT_ACTIVE_COMPILATION_CONDITIONS

 “Active Compilation Conditions” is a new build setting for passing conditional compilation flags to the Swift compiler. 

In precedenza, dovevamo dichiarare i flag di compilazione condizionale sotto OTHER_SWIFT_FLAGS, ricordando di anteporre “-D” all’impostazione. Ad esempio, per compilare in modo condizionale con un valore MYFLAG:

 #if MYFLAG1 // stuff 1 #elseif MYFLAG2 // stuff 2 #else // stuff 3 #endif 

Il valore da aggiungere all’impostazione -DMYFLAG

Ora abbiamo solo bisogno di passare il valore MYFLAG alla nuova impostazione. È ora di spostare tutti quei valori di compilazione condizionale!

Si prega di fare riferimento al link sottostante per ulteriori funzionalità di Swift Build Settings in Xcode 8: http://www.miqu.me/blog/2016/07/31/xcode-8-new-build-settings-and-analyzer-improvements/

A partire da Swift 4.1, se tutto ciò che serve è solo verificare se il codice è stato creato con la configurazione di debug o release, è ansible utilizzare le funzioni integrate:

  • _isDebugAssertConfiguration() (true quando l’ottimizzazione è impostata su -Onone )
  • _isReleaseAssertConfiguration() (true quando l’ottimizzazione è impostata su -O ) (non disponibile su Swift 3+)
  • _isFastAssertConfiguration() (true quando l’ottimizzazione è impostata su -Ounchecked )

per esempio

 func obtain() -> AbstractThing { if _isDebugAssertConfiguration() { return DecoratedThingWithDebugInformation(Thing()) } else { return Thing() } } 

Rispetto alle macro del preprocessore,

  • ✓ Non è necessario definire un flag -D DEBUG personalizzato per utilizzarlo
  • ~ In realtà è definito in termini di impostazioni di ottimizzazione, non di configurazione di build Xcode
  • ✗ Non documentato, il che significa che la funzione può essere rimossa in qualsiasi aggiornamento (ma dovrebbe essere protetta da AppStore poiché l’ottimizzatore le trasformsrà in costanti)

    • questi una volta rimossi , ma riportati al pubblico per mancanza dell’attributo @testable , il destino è incerto sul futuro Swift.
  • ✗ Usando in se / else genererà sempre un avviso “Non verrà mai eseguito”.

Xcode 8 e versioni successive

Utilizza l’impostazione Condizioni di compilazione triggers in Impostazioni di compilazione / Compilatore Swift – Bandiere personalizzate .

  • Questa è la nuova impostazione di compilazione per il passaggio di flag di compilazione condizionale al compilatore Swift.
  • Semplice aggiungere flag come questo: ALPHA , BETA ecc.

Quindi controllalo con condizioni di compilazione come questa:

 #if ALPHA // #elseif BETA // #else // #endif 

Suggerimento: puoi anche usare #if !ALPHA ecc.

Non esiste un preprocessore Swift. (Per prima cosa, la sostituzione arbitraria del codice interrompe la sicurezza del tipo e della memoria.)

Swift include anche opzioni di configurazione in fase di costruzione, quindi puoi includere in modo condizionale codice per determinate piattaforms o stili di compilazione o in risposta a flag definiti con argomenti del compilatore -D . A differenza di C, tuttavia, una sezione compilata in modo condizionale del codice deve essere sintatticamente completa. C’è una sezione su questo in Uso di Swift con Cocoa e Objective-C .

Per esempio:

 #if os(iOS) let color = UIColor.redColor() #else let color = NSColor.redColor() #endif 

I miei due centesimi per Xcode 8:

a) Una bandiera personalizzata che usa il prefisso -D funziona bene, ma …

b) Uso più semplice:

In Xcode 8 c’è una nuova sezione: “Condizioni di compilazione triggers”, già con due righe, per il debug e il rilascio.

Aggiungi semplicemente il tuo define WITHOUT -D .

isDebug Costante in base alle condizioni di compilazione attive

Un’altra soluzione, forse più semplice, che risulta ancora in un booleano che è ansible passare in funzioni senza #if condizionamento #if tutta la base di codice è definire DEBUG come una delle Active Compilation Conditions del target di Active Compilation Conditions e includere quanto segue (lo definisco come costante globale):

 #if DEBUG let isDebug = true #else let isDebug = false #endif 

isDebug Costante in base alle impostazioni di ottimizzazione del compilatore

Questo concetto si basa sulla risposta di Kennytm

Il vantaggio principale quando si confronta con kennytm, è che questo non si basa su metodi privati ​​o non documentati.

In Swift 4 :

 let isDebug: Bool = { var isDebug = false // function with a side effect and Bool return value that we can pass into assert() func set(debug: Bool) -> Bool { isDebug = debug return isDebug } // assert: // "Condition is only evaluated in playgrounds and -Onone builds." // so isDebug is never changed to true in Release builds assert(set(debug: true)) return isDebug }() 

Rispetto alle macro del preprocessore e alla risposta di kennytm ,

  • ✓ Non è necessario definire un flag -D DEBUG personalizzato per utilizzarlo
  • ~ In realtà è definito in termini di impostazioni di ottimizzazione, non di configurazione di build Xcode
  • Documentato , il che significa che la funzione seguirà i normali schemi di rilascio / deprivazione dell’API.

  • ✓ L’uso di in if / else non genererà un avviso “Non verrà mai eseguito”.

Dopo aver impostato DEBUG=1 nelle tue GCC_PREPROCESSOR_DEFINITIONS Build Settings, preferisco usare una funzione per effettuare queste chiamate:

 func executeInProduction(_ block: () -> Void) { #if !DEBUG block() #endif } 

E quindi semplicemente racchiudere in questa funzione qualsiasi blocco che voglio omesso nei build di Debug:

 executeInProduction { Fabric.with([Crashlytics.self]) // Compiler checks this line even in Debug } 

Il vantaggio rispetto a:

 #if !DEBUG Fabric.with([Crashlytics.self]) // This is not checked, may not compile in non-Debug builds #endif 

È che il compilatore controlla la syntax del mio codice, quindi sono sicuro che la sua syntax è corretta e costruisce.

Questo si basa sulla risposta di Jon Willis che si basa sull’asserzione, che viene eseguita solo nelle compilation di Debug:

 func Log(_ str: String) { assert(DebugLog(str)) } func DebugLog(_ str: String) -> Bool { print(str) return true } 

Il mio caso d’uso è per la registrazione delle dichiarazioni di stampa. Ecco un punto di riferimento per la versione di rilascio su iPhone X:

 let iterations = 100_000_000 let time1 = CFAbsoluteTimeGetCurrent() for i in 0 ..< iterations { Log ("⧉ unarchiveArray:\(fileName) memoryTime:\(memoryTime) count:\(array.count)") } var time2 = CFAbsoluteTimeGetCurrent() print ("Log: \(time2-time1)" ) 

stampe:

 Log: 0.0 

Sembra che Swift 4 elimina completamente la chiamata di funzione.

! [In Xcode 8 e sopra vai a build impostazioni -> cerca bandiere personalizzate] 1

Nel codice

  #if Live print("Live") #else print("debug") #endif