xcode - #ifdef الاستبدال في لغة Swift




preprocessor preprocessor-directive (11)

Xcode 8 وما فوق

استخدم الإعداد Active Compilation Conditions (شروط التحويل البرمجي النشط) في إنشاء إعدادات / مترجم Swift - علامات مخصصة .

  • هذا هو إعداد الإنشاء الجديد لتمرير علامات الترجمة الشرطية إلى برنامج التحويل البرمجي Swift.
  • أعلام إضافة بسيطة مثل: ALPHA ، BETA الخ

ثم تحقق من ذلك مع شروط تجميع مثل هذا:

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

نصيحة: يمكنك أيضًا استخدام #if !ALPHA وما إلى ذلك.

في C / C ++ / Objective-C ، يمكنك تعريف الماكرو باستخدام معالجات ما قبل المترجم. علاوة على ذلك ، يمكنك تضمين / استبعاد بعض أجزاء الكود باستخدام المعالجات المسبقة للملقم.

#ifdef DEBUG
    // Debug-only code
#endif

هل هناك حل مماثل في سويفت؟


حدث تغيير كبير في استبدال ifdef مع Xcode 8. أي استخدام شروط التجميع النشطة .

ارجع إلى البناء والارتباط في Xcode 8 Release note .

إعدادات البناء الجديدة

الإعداد الجديد: SWIFT_ACTIVE_COMPILATION_CONDITIONS

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

في السابق ، كان علينا أن نعلن عن علامات الترجمة الشرطية الخاصة بنا تحت OTHER_SWIFT_FLAGS ، متذكرين أن نضيف "-D" إلى الإعداد. على سبيل المثال ، لتجميعها بشكل شرطي مع قيمة MYFLAG:

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

القيمة المطلوب إضافتها إلى الإعداد -DMYFLAG

الآن نحن بحاجة فقط لتمرير القيمة MYFLAG إلى الإعداد الجديد. حان الوقت لنقل كل قيم التجميع المشروطة هذه!

يرجى الرجوع إلى الرابط أدناه لمزيد من ميزة Swift Build Settings في Xcode 8: http://www.miqu.me/blog/2016/07/31/xcode-8-new-build-settings-and-analyzer-improvements/


اعتبارًا من Swift 3.1 ، إذا كان كل ما تحتاج إليه هو فقط التحقق مما إذا تم إنشاء الشفرة مع تصحيح أو تكوين الإصدار ، فيمكنك استخدام الوظائف المضمنة:

  • _isDebugAssertConfiguration() (صحيح عندما يتم تعيين التحسين إلى -Onone )
  • _isReleaseAssertConfiguration() (صحيح عندما يتم تعيين التحسين إلى -O ) (غير متوفر على Swift 3+)
  • _isFastAssertConfiguration() (صحيح عندما يتم تعيين التحسين إلى -Ounchecked )

على سبيل المثال

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

مقارنة مع وحدات الماكرو preprocessor ،

  • ✓ لا تحتاج إلى تعريف علامة -D DEBUG مخصصة لاستخدامها
  • ~ يتم تعريفه بالفعل من حيث إعدادات التحسين ، وليس تكوين بناء Xcode
  • ✗ غير موثقة ، مما يعني أنه يمكن إزالة الوظيفة في أي تحديث (ولكن يجب أن تكون آمنة في AppStore حيث يقوم المحسن بتحويلها إلى ثوابت)

  • ✗ استخدام إذا كان / / سيؤدي دائمًا إلى إنشاء تحذير "لن يتم تنفيذه مطلقًا".


هذا يعتمد على إجابة Jon Willis التي تعتمد على التأكيد ، والتي يتم تنفيذها فقط في مجموعات Debug:

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

حالة الاستخدام الخاصة بي هي لتسجيل بيانات الطباعة. في ما يلي مقياس مرجعي لإصدار الإصدار على 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)" )

مطبوعات:

Log: 0.0

يبدو أن Swift 4 يقضي تمامًا على استدعاء الوظيفة.


isDebug ثابت على أساس شروط التجميع النشط

هناك حل آخر ، ربما أبسط ، لا يزال ينتج عنه قيمة منطقية يمكنك تمريرها إلى وظائف بدون تأطير #if شرطية في جميع أنحاء شفرة الكود الخاصة بك ، وهو تعريف DEBUG كواحد من Active Compilation Conditions المستهدفة في مشروعك وتتضمن ما يلي (أعرِّفه على أنه الثابت العالمي):

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

isDebug ثابت على أساس إعدادات تحسين برنامج التحويل البرمجي

يعتمد هذا المفهوم على إجابة kennytm

الميزة الرئيسية عند مقارنة الاثنين ، هو أن هذا لا يعتمد على الأساليب الخاصة أو غير الموثقة.

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

مقارنة مع وحدات الماكرو preprocessor والجواب kennytm ،

  • ✓ لا تحتاج إلى تعريف علامة -D DEBUG مخصصة لاستخدامها
  • ~ يتم تعريفه بالفعل من حيث إعدادات التحسين ، وليس تكوين بناء Xcode
  • موثقة ، مما يعني أن الوظيفة ستتبع أنماط الإصدار / الإهمال المعتادة لـ API.

  • ✓ استخدام في حالة / في حالة عدم إنشاء تحذير "لن يتم تنفيذه مطلقًا".


نعم يمكنك أن تفعل ذلك.

في Swift ، لا يزال بإمكانك استخدام وحدات الماكرو preprocessor "# if / # else / # endif" (على الرغم من أنها أكثر تقييدًا) ، وفقًا لمستندات Apple . إليك مثال على ذلك:

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

الآن ، يجب عليك تعيين رمز "DEBUG" في مكان آخر ، على الرغم من. تعيينه في قسم "Swift Compiler - Custom Flags" ، سطر "Swift Flags" أخرى. يمكنك إضافة رمز DEBUG مع إدخال -D DEBUG .

كالعادة ، يمكنك تعيين قيمة مختلفة عندما تكون في Debug أو عندما تكون في الإصدار.

لقد اختبرت ذلك في الكود الحقيقي وهو يعمل. يبدو أنه لا يمكن التعرف عليه في الملعب بالرغم من ذلك.

يمكنك قراءة المنشور الأصلي here .

ملاحظة هامة: -DDEBUG=1 لا يعمل. فقط -D DEBUG يعمل. يبدو مترجم يتجاهل علم بقيمة محددة.


لا يوجد معالج مسبق سويفت. (لسبب واحد ، فإن الاستبدال التعسفي للرمز يكسر النوع وسلامة الذاكرة.)

يتضمّن Swift خيارات تكوين وقت الإنشاء ، على الرغم من ذلك ، بحيث يمكنك تضمين التعليمات البرمجية لبعض الأنظمة الأساسية أو أنماط الإنشاء أو الرد على العلامات التي تحددها باستخدام -D compiler. على عكس C ، على الرغم من ذلك ، يجب أن يكون قسم الترجمة برمجيًا من التعليمات البرمجية مكتملًا. هناك قسم حول هذا في استخدام سويفت مع الكاكاو و Objective-C .

فمثلا:

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

بعد تعيين DEBUG=1 في GCC_PREPROCESSOR_DEFINITIONS إعدادات البناء ، أفضل استخدام وظيفة لإجراء هذه المكالمات:

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

ثم أرفق فقط في هذه الوظيفة أي كتلة أرغب في حذفها في إصدارات Debug:

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

الميزة عند مقارنتها بما يلي:

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

هو أن المترجم يقوم بفحص صيغة الشفرة الخاصة بي ، لذلك أنا متأكد من أن تركيبها صحيح وبني.


في كثير من المواقف ، لا تحتاج حقاً إلى تجميع شرطي ؛ تحتاج فقط إلى سلوك شرطي يمكنك تشغيله وإيقاف تشغيله. لذلك ، يمكنك استخدام متغير بيئة. هذا له ميزة ضخمة لا تحتاج في الواقع إلى إعادة تجميعها.

يمكنك ضبط متغير البيئة ، وتشغيله أو إيقاف تشغيله بسهولة ، في محرر النظام:

يمكنك استرداد متغير البيئة باستخدام NSProcessInfo:

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

إليك مثال واقعي. يتم تشغيل التطبيق الخاص بي على الجهاز فقط ، لأنه يستخدم مكتبة الموسيقى ، التي لا توجد في Simulator. كيف ، إذن ، لاتخاذ لقطات الشاشة على جهاز محاكاة لأجهزة لا أملكها؟ بدون هذه اللقطات ، لا يمكنني إرسالها إلى AppStore.

أحتاج إلى بيانات مزيفة وطريقة مختلفة لمعالجتها . لدي متغيرين في البيئة: أحدهما الذي ، عند تشغيله ، يخبر التطبيق بإنشاء البيانات المزيفة من البيانات الحقيقية أثناء التشغيل على جهازي ؛ الآخر ، عند تشغيله ، يستخدم البيانات المزيفة (وليس مكتبة الموسيقى المفقودة) أثناء تشغيله على جهاز Simulator. يعد تشغيل كل وضع من هذه الأوضاع الخاصة / إيقاف التشغيل أمرًا سهلاً بفضل مربعات اختيار متغير البيئة في محرر المخطط. والمكافأة هي أنني لا أستطيع استخدامها عن طريق الخطأ في إنشاء متجر التطبيقات ، نظرًا لأن الأرشفة لا تحتوي على متغيرات بيئة.


بلدي سنتان ل Xcode 8:

أ) علامة مخصصة تستخدم البادئة -D تعمل بشكل جيد ، ولكن ...

ب) استخدام أبسط:

في Xcode 8 يوجد قسم جديد: "شروط التجميع النشطة" ، مع صفان بالفعل ، للتصحيح والإصدار.

ببساطة إضافة التعريف الخاص بك دون -D .


أعتقد أن Extensions هي طريقة أفضل بدلاً من #pragma mark .

الشفرة قبل استخدام Extensions :

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
    ...

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        ...
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        ...
    }

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        ...
    }
}

الرمز بعد استخدام Extensions :

class ViewController: UIViewController {
    ...
}

extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        ...
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        ...
    }
}

extension ViewController: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
       ...
    }
}






swift xcode preprocessor preprocessor-directive