Swift dilinde #ifdef değiştirme


735

C / C ++ / Objective C'de derleyici ön işlemcilerini kullanarak bir makro tanımlayabilirsiniz. Ayrıca, derleyici ön işlemcilerini kullanarak kodun bazı bölümlerini ekleyebilir / hariç tutabilirsiniz.

#ifdef DEBUG
    // Debug-only code
#endif

Swift'te benzer bir çözüm var mı?


1
Bir fikir olarak, bunu obj-c köprüleme başlıklarınıza koyabilirsiniz ..
Matej

42
Aralarından seçim yapabileceğiniz çok sayıda seçeneğiniz olduğu için gerçekten bir cevap vermelisiniz ve bu soru size çok fazla oy verdi.
David H

Yanıtlar:


1069

Evet, bunu yapabilirsin.

Swift'te, Apple belgelerine göre "# if / # else / # endif" önişlemci makrolarını (daha kısıtlanmış olsa da) kullanabilirsiniz . İşte bir örnek:

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

Şimdi, "DEBUG" sembolünü başka bir yerde ayarlamanız gerekir. "Swift Derleyici - Özel Bayraklar" bölümünde, "Diğer Swift Bayrakları" satırına ayarlayın. DEBUG sembolünü -D DEBUGgiriş ile eklersiniz .

Her zamanki gibi, Hata Ayıklama'da veya Yayındayken farklı bir değer ayarlayabilirsiniz.

Ben gerçek kod test ve çalışıyor; bir oyun alanında tanınmış gibi görünmüyor.

Orijinal yazımı buradan okuyabilirsiniz .


ÖNEMLİ NOT: -DDEBUG=1 çalışmıyor. Sadece -D DEBUGçalışıyor. Derleyici belirli bir değere sahip bir bayrağı yok sayıyor gibi görünüyor.


41
Bu doğru cevaptır, ancak belirli bir değeri değil, yalnızca bayrağın varlığını kontrol edebileceğiniz belirtilmelidir.
Charles Harley

19
Ek not : -D DEBUGYukarıda belirtildiği gibi eklemenin yanı sıra DEBUG=1, Apple LLVM 6.0 - Preprocessing-> öğesini de tanımlamanız gerekir Preprocessor Macros.
Matthew Quiros

38
Biçimlendirmeyi -DDEBUGşu yanıttan değiştirene kadar çalışamadım : stackoverflow.com/a/24112024/747369 .
Kramer

11
@MattQuiros eklemeye gerek yok DEBUG=1etmek Preprocessor Macrossize Objective-C kodunda kullanmak istemiyorsanız,.
derpoliuk

7
@Daniel Standart boole operatörlerini kullanabilirsiniz (örn: #if! DEBUG `)
Jean Le Moignan

353

Apple Dokümanlar'da belirtildiği gibi

Swift derleyicisi bir önişlemci içermez. Bunun yerine, aynı işlevselliği gerçekleştirmek için derleme zamanı özelliklerinden, derleme yapılandırmalarından ve dil özelliklerinden yararlanır. Bu nedenle, önişlemci yönergeleri Swift'e aktarılmaz.

Özel Yapılandırma Yapılandırmaları'nı kullanarak istediğimi elde etmeyi başardım:

  1. Projenize gidin / hedefinizi seçin / Yapı Ayarları / Özel Bayrakları arayın
  2. Seçtiğiniz hedef için, hem Debug hem de Release için -D önekini (beyaz boşluklar olmadan) kullanarak özel bayrağınızı ayarlayın
  3. Sahip olduğunuz her hedef için yukarıdaki adımları uygulayın

Hedefi nasıl kontrol edeceğiniz aşağıda açıklanmıştır:

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

resim açıklamasını buraya girin

Swift 2.2 kullanılarak test edildi


4
1. beyaz alan çalışması ile de, 2. bayrağı sadece Hata Ayıklama için ayarlamalıdır?
c0ming

3
@ c0ming bu sizin ihtiyaçlarınıza bağlıdır, ancak bir şeyin sadece hata ayıklama modunda olmasını istiyorsanız ve sürümde değil, -DDEBUG öğesini Release'den kaldırmanız gerekir.
cdf1982

1
Özel bayrağı ayarladıktan sonra -DLOCAL, benim #if LOCAl #else #endif, #elsebölüme düşer . Orijinal hedefi çoğalttım AppTargetve yeniden adlandırdım AppTargetLocalve özel bayrağını ayarladım.
Perwyl Liu

3
@Andrej, XCTest'in özel bayrakları da nasıl tanıyacağını biliyor musunuz? #if LOCAL Simülatör ile koştuğumda ve #else test sırasında düştüğünde amaçlanan sonucun düştüğünü fark ettim . #if LOCALTest sırasında da düşmesini istiyorum .
Perwyl Liu

3
Bu kabul edilen cevap olmalı. Geçerli kabul edilen cevap Swift için sadece Objective-C için geçerli olduğundan yanlış.
miken.mkndev

171

Birçok durumda, koşullu derlemeye gerçekten ihtiyacınız yoktur ; sadece açıp kapatabileceğiniz koşullu davranışa ihtiyacınız var . Bunun için bir ortam değişkeni kullanabilirsiniz. Bu, aslında yeniden derlemek zorunda olmadığınız büyük bir avantaja sahiptir.

Ortam değişkenini şema düzenleyicide ayarlayabilir ve kolayca açıp kapatabilirsiniz:

resim açıklamasını buraya girin

Ortam değişkenini NSProcessInfo ile alabilirsiniz:

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

İşte gerçek hayattan bir örnek. Uygulamam yalnızca cihazda çalışıyor, çünkü Simülatör'de bulunmayan müzik kitaplığını kullanıyor. Öyleyse, sahip olmadığım cihazlar için Simülatör'de ekran görüntüleri nasıl çekilir? Bu ekran görüntüleri olmadan AppStore'a gönderemiyorum.

Sahte verilere ve işlemenin farklı bir yoluna ihtiyacım var . İki ortam değişkenim var: biri, açıldığında, uygulamaya cihazımda çalışırken gerçek verilerden sahte veriler oluşturmasını söyler; diğeri, açıldığında, Simülatörde çalışırken sahte verileri (eksik müzik kütüphanesini değil) kullanır. Şema düzenleyicideki ortam değişkeni onay kutuları sayesinde bu özel modların her birini açmak / kapatmak kolaydır. Ve bonus, onları yanlışlıkla App Store derlememde kullanamam çünkü arşivlemenin ortam değişkenleri yok.


Nedense ortam değişkenim ikinci uygulama lansmanında sıfır olarak döndü
Eugene

60
Dikkat : Ortam Değişkenleri tüm derleme yapılandırmaları için ayarlanmıştır, ayrı ayrı düzenler için ayarlanamazlar. Bu, bir sürüm veya hata ayıklama derlemesine bağlı olarak değiştirmek için davranışa ihtiyacınız varsa bu geçerli bir çözüm değildir .
Eric

5
@Eric Agreed, ancak bunlar tüm şema eylemleri için ayarlanmadı. Böylece yap-çalıştır üzerinde bir şey ve arşivde farklı bir şey yapabilirsiniz, ki bu genellikle çizmek istediğiniz gerçek yaşam ayrımıdır. Ya da gerçek hayattaki ortak bir desen olan birden fazla şemanız olabilir. Ayrıca, cevabımda söylediğim gibi, bir ortamda ortam değişkenlerini açmak ve kapatmak kolaydır.
matt

10
Ortam değişkenleri arşiv modunda ÇALIŞMAZ. Yalnızca uygulama XCode'dan başlatıldığında uygulanır. Bunlara bir cihazdan erişmeye çalışırsanız uygulama kilitlenir. Zor yolu buldum.
iupchris10

2
@ iupchris10 "Arşivlemenin ortam değişkenleri yok" cevabımın son sözleri yukarıdaki. Cevabımda söylediğim gibi, bu iyi . Bu var nokta .
matt

159

Önemli bir değişiklik ifdefXcode 8 ile geldi. Yani Aktif Derleme Koşullarının kullanımı .

Bakın Bina ve Bağlama içinde Xcode 8 Yayın notu .

Yeni oluşturma ayarları

Yeni ayar: SWIFT_ACTIVE_COMPILATION_CONDITIONS

Active Compilation Conditionsis a new build setting for passing conditional compilation flags to the Swift compiler.

Önceden, koşullu derleme bayraklarınızı OTHER_SWIFT_FLAGS altında bildirmek zorunda kalıyorduk ve ayara "-D" eklemeyi hatırlıyorduk. Örneğin, bir MYFLAG değeriyle koşullu olarak derlemek için:

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

Ayara eklenecek değer -DMYFLAG

Şimdi sadece MYFLAG değerini yeni ayara geçirmemiz gerekiyor. Tüm bu koşullu derleme değerlerini taşıma zamanı!

Xcode 8'deki diğer Hızlı Oluşturma Ayarları özelliği için lütfen aşağıdaki bağlantıya bakın: http://www.miqu.me/blog/2016/07/31/xcode-8-new-build-settings-and-analyzer-improvements/


Oluşturma zamanında ayarlanmış bir Etkin Derleme Koşullarını devre dışı bırakmanın bir yolu var mı? Sınama için hata ayıklama yapılandırmasını oluştururken DEBUG koşulunu devre dışı bırakmam gerekiyor.
Jonny

1
@Jonny Bulduğum tek yol proje için 3. yapı yapılandırması oluşturmak. Proje> Bilgi sekmesi> Konfigürasyonlar'dan '+' tuşuna basın, ardından Hata Ayıklamayı çoğaltın. Daha sonra bu yapılandırma için Etkin Derleme Koşullarını özelleştirebilirsiniz. Yeni oluşturma yapılandırmasını kullanmak için Hedef> Test şemalarınızı düzenlemeyi unutmayın!
matthias

1
Bu doğru cevap olmalı ... Swift 4.x kullanarak xCode 9'da benim için çalışan tek şey!
shokaveli

1
BTW, Xcode 9.3'te Swift 4.1 DEBUG zaten Aktif Derleme Koşullarında var ve DEBUG yapılandırmasını kontrol etmek için hiçbir şey eklemenize gerek yok. Sadece # DEBUG ve #endif.
Denis Kutlubaev

Bence bu hem konu dışı, hem de yapılacak kötü bir şey. Etkin Derleme Koşullarını devre dışı bırakmak istemezsiniz. test için yeni ve farklı bir yapılandırmaya ihtiyacınız vardır. Bu etiketin üzerinde "Hata Ayıkla" etiketi YOKTUR. Şemalar hakkında bilgi edinin.
Motti Shneor

93

Swift 4.1'den itibaren, ihtiyacınız olan tek şey kodun hata ayıklama veya bırakma yapılandırmasıyla oluşturulmuş olup olmadığını kontrol etmekse, yerleşik işlevleri kullanabilirsiniz:

  • _isDebugAssertConfiguration()(optimizasyon olarak ayarlandığında doğrudur -Onone)
  • _isReleaseAssertConfiguration()(optimizasyon olarak ayarlandığında doğrudur -O) (Swift 3+ üzerinde mevcut değildir)
  • _isFastAssertConfiguration()(optimizasyon olarak ayarlandığında doğrudur -Ounchecked)

Örneğin

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

Önişlemci makrolarıyla karşılaştırıldığında,

  • ✓ Kullanmak için özel bir -D DEBUGbayrak tanımlamanız gerekmez
  • ~ Aslında Xcode derleme yapılandırması değil, optimizasyon ayarları açısından tanımlanır
  • ✗ Belgelenmemiş, bu işlev herhangi bir güncellemede kaldırılabileceği anlamına gelir (ancak iyileştirici bunları sabitlere dönüştüreceğinden AppStore için güvenli olmalıdır)

  • İf if / else komutunun kullanılması her zaman "Asla yürütülmez" uyarısı oluşturur.


1
Bu yerleşik işlevler derleme zamanında veya çalışma zamanında değerlendiriliyor mu?
ma11hew28

@MattDiPasquale Optimizasyon süresi. yayınlama modunda if _isDebugAssertConfiguration()değerlendirilir if falseve if truehata ayıklama modudur.
kennytm

2
Gerçi sürümde bazı hata ayıklama değişkenlerini devre dışı bırakmak için bu işlevleri kullanamıyorum.
Franklin Yu

3
Bu işlevler bir yerde belgelenmiş mi?
Tom Harrington

7
Swift 3.0 ve XCode 8'den itibaren bu işlevler geçersizdir.
CodeBender

86

Xcode 8 ve üstü

Derleme ayarları / Swift derleyici - Özel bayraklar'da Etkin Derleme Koşulları ayarını kullanın .

  • Bu, koşullu derleme bayraklarını Swift derleyicisine geçirmek için yeni yapı ayarıdır.
  • Bu gibi basit eklenti bayrakları: ALPHA, BETAvb

Sonra böyle derleme koşulları ile kontrol edin :

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

İpucu: Ayrıca kullanabilirsiniz #if !ALPHA.


77

Swift ön işlemcisi yok. (Bir kere, rastgele kod değiştirme tip ve bellek güvenliğini bozar.)

Swift, derleme zamanı yapılandırma seçeneklerini içerir, böylece belirli platformlar veya derleme stilleri için veya -Dderleyici argümanlarıyla tanımladığınız bayraklara yanıt olarak koşullu olarak kod ekleyebilirsiniz . Bununla birlikte, C'den farklı olarak, kodunuzun koşullu olarak derlenmiş bir bölümünün sözdizimsel olarak tamamlanması gerekir. Bunun hakkında Swift'i Kakao ve Objective-C ile Kullanma bölümünde bulabilirsiniz .

Örneğin:

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

34
"Bir kere, rastgele kod değiştirme tip ve bellek güvenliğini bozar." Bir ön işlemci derleyiciden önce işini yapmaz mı (dolayısıyla adı)? Böylece tüm bu kontroller hala yapılabilir.
Thilo

10
@Thilo Bence kırdığı şey IDE desteği
Aleksandr Dubinsky

1
Bence @rickster, C Önişlemci makrolarının tür hakkında bir anlayışa sahip olmadıkları ve varlıklarının Swift'in tür gereksinimlerini kıracağı. Makroların C'de çalışmasının nedeni, C'nin örtük tür dönüştürmeye izin vermesidir, bu da kabul ettiğiniz INT_CONSTher yere koyabileceğiniz anlamına gelir float. Swift buna izin vermezdi. Ayrıca, var floatVal = INT_CONSTkaçınılmaz olarak yapabilseydiniz , derleyici beklediğinde daha sonra bir yerde çökecektir, Intancak bunu Float(türü floatValolarak çıkartılacaktır Int) olarak kullanırsınız. 10 oyuncu kadrosu ve sadece makroları temizlemek ...
Ephemera

Bunu kullanmaya çalışıyorum ama işe yaramıyor, hala iOS yapılarında Mac kodunu derliyor. Bir yerde ayarlanması gereken başka bir kurulum ekranı var mı?
Maury Markowitz

1
@Hayırlısınız - bir ön işlemci herhangi bir tür veya bellek güvenliğini bozmaz.
16:47, tcurdt

50

Xcode 8 için iki sentim:

a) -DÖneki kullanan özel bir işaret iyi çalışıyor, ancak ...

b) Daha basit kullanım:

Xcode 8'de yeni bir bölüm vardır: Hata ayıklama ve serbest bırakma için zaten iki satırlı "Etkin Derleme Koşulları".

Basitçe tanımlamak OLMADAN ekleyin -D.


HATA AYIKLAMA VE TAHLİYE İÇİN İKİ SIRA olduğunu söylediğin için teşekkürler
Yitzchak

kimse bunu sürümde test etti?
Glenn

Bu, hızlı kullanıcılar için güncellenmiş yanıttır. yani olmadan -D.
Mani

46

Aktif Derleme Koşullarına Dayalı isDebug Sabiti

#ifKod tabanınız boyunca koşullu biber kullanmadan işlevlere geçebileceğiniz bir boole ile sonuçlanan bir başka, belki de daha basit bir çözüm, DEBUGproje oluşturma hedeflerinizden biri olarak tanımlamak Active Compilation Conditionsve aşağıdakileri içerir (küresel bir sabit olarak tanımlarım):

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

Derleyici Optimizasyon Ayarlarına Dayalı isDebug Sabiti

Bu kavram kennytm'in cevabı üzerine kuruludur

Kennytm'lere kıyasla ana avantaj, bunun özel veya belgesiz yöntemlere dayanmamasıdır.

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

Önişlemci makroları ve kennytm'in cevabı ile karşılaştırıldığında ,

  • ✓ Kullanmak için özel bir -D DEBUGbayrak tanımlamanız gerekmez
  • ~ Aslında Xcode derleme yapılandırması değil, optimizasyon ayarları açısından tanımlanır
  • Belgelenmiştir ; bu, işlevin normal API bırakma / kullanımdan kaldırma kalıplarını izleyeceği anlamına gelir.

  • ✓ Başka eğer / in kullanılması değil , bir "idam asla" uyarısı oluşturur.


25

Moignans cevap burada iyi çalışıyor. İşte size yardımcı olması durumunda bir bilgi huzuru daha,

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

Makroları aşağıdaki gibi reddedebilirsiniz,

#if !RELEASE
    let a = 2
#else
    let a = 3
#endif

23

Xcode Sürüm 9.4.1, Swift 4.1 ile oluşturulan Swift projelerinde

#if DEBUG
#endif

Önişlemci Makrolarında DEBUG = 1 zaten Xcode tarafından ayarlandığı için varsayılan olarak çalışır.

Böylece #if DEBUG "kutunun dışında" kullanabilirsiniz.

Bu arada, genel olarak derleme bloklarının nasıl kullanılacağı Apple'ın Swift Programlama Dili 4.1 (Derleyici Kontrol İfadeleri bölümü) kitabında ve derleme bayraklarının nasıl yazılacağı ve Swift'teki C makrolarının karşılığı nedir Başka bir Apple'ın Swift'i Kakao ve Objektif C ile Kullanma (Önişlemci Yönergeleri bölümünde)

Gelecekte Apple, kitapları için daha ayrıntılı içerikler ve endeksler yazacaktır.


17

XCODE 9 VE ÜZERİ

#if DEVELOP
    //
#elseif PRODCTN
    //
#else
    //
#endif

3
vay bu gördüğüm en çirkin kısaltma: p
rmp251

7

Ayarladıktan sonra DEBUG=1sizin de GCC_PREPROCESSOR_DEFINITIONSYapı Ayarlar bu çağrı yapmak için bir işlev kullanmayı tercih:

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

Ve sonra sadece bu işlevi Debug derlemeleri atlamak istediğiniz herhangi bir blok içine:

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

Avantajı:

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

Derleyici benim kod sözdizimini kontrol eder, bu yüzden sözdiziminin doğru olduğundan ve inşa eminim.



3
func inDebugBuilds(_ code: () -> Void) {
    assert({ code(); return true }())
}

Kaynak


1
Bu koşullu derleme değil. Yararlı olsa da, sadece düz eski bir çalışma zamanı koşullu. OP derleme sonrası metaprogramlama için soruyor
Shayne

3
Sadece @inlinableönüne ekleyin funcve bu Swift için en zarif ve deyimsel yol olacaktır. Sürüm yapılarında code()bloğunuz optimize edilecek ve tamamen ortadan kaldırılacaktır. Benzer bir işlev Apple'ın kendi NIO çerçevesinde de kullanılır.
mojuba

1

Bu, Jon Willis'nin yalnızca Debug derlemelerinde yürütülen iddiaya dayanan cevabı üzerine kuruludur:

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

Benim kullanım durumum yazdırma ifadelerini günlüğe kaydetmektir. İşte iPhone X'daki Sürüm sürümü için bir kıyaslama:

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)" )

baskılar:

Log: 0.0

Swift 4, işlev çağrısını tamamen ortadan kaldırıyor gibi görünüyor.


İşlev boş olması nedeniyle, hata ayıklamada değilken çağrıyı tamamen kaldırır. Muhteşem olurdu.
Johan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.