substituição #ifdef na linguagem Swift

Em C / C ++ / Objective-C, você pode definir uma macro usando os pré-processadores do compilador. Além disso, você pode include / excluir algumas partes do código usando os pré-processadores do compilador.

#ifdef DEBUG // Debug-only code #endif 

Existe uma solução semelhante no Swift?

Sim você pode fazer isso.

No Swift, você ainda pode usar as macros de pré-processamento “# if / # else / # endif” (embora mais restritas), conforme documentos da Apple . Aqui está um exemplo:

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

Agora, você deve definir o símbolo “DEBUG” em outro lugar, no entanto. Defina-o na seção “Swift Compiler – Custom Flags”, na linha “Other Swift Flags”. Você adiciona o símbolo DEBUG com a input -D DEBUG .

Como de costume, você pode definir um valor diferente quando estiver em Debug ou quando estiver em Release.

Eu testei em código real e funciona; não parece ser reconhecido em um playground embora.

Você pode ler meu post original aqui .


NOTA IMPORTANTE: -DDEBUG=1 não funciona. Apenas o -D DEBUG funciona. Parece que o compilador está ignorando um sinalizador com um valor específico.

Conforme declarado no Apple Docs

O compilador Swift não inclui um pré-processador. Em vez disso, ele tira proveito dos atributos de tempo de compilation, configurações de compilation e resources de idioma para realizar a mesma funcionalidade. Por esse motivo, as diretivas de pré-processador não são importadas no Swift.

Consegui alcançar o que queria usando configurações de compilation personalizadas:

  1. Vá para o seu projeto / selecione seu alvo / Configurações de construção / procure por sinalizadores personalizados
  2. Para o seu alvo escolhido, defina seu sinalizador personalizado usando o prefixo -D (sem espaços em branco), tanto para Debug e Release
  3. Siga as etapas acima para cada alvo que você tiver

Veja como você verifica o alvo:

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

insira a descrição da imagem aqui

Testado usando o Swift 2.2

Em muitas situações, você não precisa realmente de compilation condicional; você só precisa de um comportamento condicional que possa ser ativado e desativado. Para isso, você pode usar uma variável de ambiente. Isso tem a enorme vantagem de que você não precisa realmente recompilar.

Você pode definir a variável de ambiente e ativá-la ou desativá-la facilmente no editor de esquema:

insira a descrição da imagem aqui

Você pode recuperar a variável de ambiente com NSProcessInfo:

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

Aqui está um exemplo da vida real. Meu aplicativo é executado apenas no dispositivo, porque ele usa a biblioteca de músicas, que não existe no Simulador. Como, então, fazer capturas de canvas no simulador para dispositivos que eu não possuo? Sem essas capturas de canvas, não posso enviar para a AppStore.

Preciso de dados falsos e uma maneira diferente de processá-los . Eu tenho duas variables ​​de ambiente: uma que, quando ligada, diz ao aplicativo para gerar os dados falsos dos dados reais enquanto estiver executando no meu dispositivo; o outro que, quando ligado, usa os dados falsos (não a biblioteca de músicas em falta) durante a execução no Simulador. Ligar e desligar cada um desses modos especiais é fácil graças às checkboxs de seleção de variables ​​de ambiente no editor de esquemas. E o bônus é que eu não posso acidentalmente usá-los na minha compilation da App Store, porque o arquivamento não tem variables ​​de ambiente.

Uma grande mudança ifdef substituição do ifdef veio com o Xcode 8. isto é, o uso de Condições de Compilação Ativas .

Consulte Construindo e vinculando na nota de lançamento do Xcode 8 .

Novas configurações de construção

Nova configuração: SWIFT_ACTIVE_COMPILATION_CONDITIONS

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

Anteriormente, precisávamos declarar seus sinalizadores de compilation condicional em OTHER_SWIFT_FLAGS, lembrando-se de inserir “-D” na configuração. Por exemplo, para compilar condicionalmente com um valor MYFLAG:

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

O valor para adicionar à configuração -DMYFLAG

Agora só precisamos passar o valor MYFLAG para a nova configuração. Hora de mover todos esses valores de compilation condicional!

Por favor, consulte o link abaixo para mais resources do Swift Build Settings no Xcode 8: http://www.miqu.me/blog/2016/07/31/xcode-8-new-build-settings-and-analyzer-improvements/

A partir do Swift 4.1, se tudo que você precisa é apenas verificar se o código é construído com a configuração de debugging ou liberação, você pode usar as funções internas:

  • _isDebugAssertConfiguration() (true quando a otimização está definida como -Onone )
  • _isReleaseAssertConfiguration() (true quando a otimização está definida como -O ) (não disponível no Swift 3+)
  • _isFastAssertConfiguration() (true quando a otimização está definida como -Ounchecked )

por exemplo

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

Comparado com macros de pré-processador,

  • ✓ Você não precisa definir um sinalizador -D DEBUG personalizado -D DEBUG para usá-lo
  • ~ Na verdade, é definido em termos de configurações de otimização, não de configuração de compilation do Xcode
  • ✗ Não documentado, o que significa que a function pode ser removida em qualquer atualização (mas deve ser compatível com o AppStore, pois o otimizador as transformará em constantes)

    • estes, uma vez removidos , mas trazidos de volta ao público por falta de atributo @testable , destino incerto no futuro Swift.
  • ✗ O uso em if / else sempre gerará um aviso “Nunca será executado”.

Xcode 8 e acima

Use a configuração Condições de compilation ativas em Configurações de compilation / Compilador Swift – Sinalizadores personalizados .

  • Essa é a nova configuração de compilation para passar sinalizadores de compilation condicional ao compilador Swift.
  • Simples adicione bandeiras como esta: ALPHA , BETA etc.

Então verifique com condições de compilation como esta:

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

Dica: Você também pode usar #if !ALPHA etc.

Não existe um pré-processador Swift. (Por um lado, a substituição arbitrária de códigos quebra o tipo e a segurança da memory.)

No entanto, o Swift inclui opções de configuração de tempo de construção, portanto, você pode include condicionalmente o código para determinadas plataformas ou estilos de construção ou em resposta a sinalizadores definidos com argumentos do compilador -D . Ao contrário do C, no entanto, uma seção compilada condicionalmente do seu código deve estar sintaticamente completa. Há uma seção sobre isso em Using Swift With Cocoa and Objective-C .

Por exemplo:

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

Meus dois centavos para o Xcode 8:

a) Um sinalizador personalizado usando o prefixo -D funciona bem, mas …

b) Uso mais simples:

No Xcode 8 existe uma nova seção: “Active Compilation Conditions”, já com duas linhas, para debug e release.

Simplesmente adicione sua definição SEM -D .

Constante de isDebug baseada em condições de compilation ativas

Outra solução, talvez mais simples, que ainda resulta em um booleano que você pode passar para funções sem salpicar condicionais em toda a sua base de código é definir DEBUG como uma das Active Compilation Conditions de seu projeto e include o seguinte (eu o defino como um constante global):

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

Constante de isDebug com base nas configurações de otimização do compilador

Este conceito baseia-se na resposta do kennytm

A principal vantagem ao comparar com o kennytm é que isso não depende de methods privados ou não documentados.

No 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 }() 

Comparado com as macros do pré-processador e a resposta do kennytm ,

  • ✓ Você não precisa definir um sinalizador -D DEBUG personalizado -D DEBUG para usá-lo
  • ~ Na verdade, é definido em termos de configurações de otimização, não de configuração de compilation do Xcode
  • Documentado , o que significa que a function seguirá os padrões normais de release / deprecation da API.

  • ✓ O uso em if / else não gerará um aviso “Nunca será executado”.

Depois de definir DEBUG=1 em suas GCC_PREPROCESSOR_DEFINITIONS compilation do GCC_PREPROCESSOR_DEFINITIONS prefiro usar uma function para fazer essas chamadas:

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

E então apenas inclua nessa function qualquer bloco que eu queira omitir nas compilações de Depuração:

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

A vantagem quando comparado a:

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

É que o compilador verifica a syntax do meu código, por isso tenho certeza de que sua syntax está correta e é construída.

Isso se baseia na resposta de Jon Willis que se baseia em assert, que só é executado em compilações de Debug:

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

Meu caso de uso é para registrar instruções de impressão. Aqui está uma referência para a versão Release no 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)" ) 

impressões:

 Log: 0.0 

Parece que o Swift 4 elimina completamente a chamada de function.

! [No Xcode 8 e acima vá para configuração de construção -> busca de bandeiras personalizadas] 1

Em código

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