#ifdef การแทนที่ในภาษา Swift


735

ใน C / C ++ / Objective C คุณสามารถกำหนดแมโครโดยใช้ตัวประมวลผลล่วงหน้าคอมไพเลอร์ ยิ่งกว่านั้นคุณสามารถรวม / แยกบางส่วนของรหัสโดยใช้ตัวประมวลผลล่วงหน้าคอมไพเลอร์

#ifdef DEBUG
    // Debug-only code
#endif

มีวิธีแก้ไขปัญหาที่คล้ายคลึงกันใน Swift หรือไม่


1
ในฐานะที่เป็นความคิดคุณสามารถใส่สิ่งนี้ไว้ในส่วนเชื่อมโยง obj-c ของคุณ ..
Matej

42
คุณควรให้รางวัลคำตอบเมื่อคุณมีหลายแบบให้เลือกและคำถามนี้ทำให้คุณได้รับคะแนนโหวตจำนวนมาก
David H

คำตอบ:


1069

ใช่คุณสามารถทำได้

ในสวิฟท์คุณยังสามารถใช้ "# ถ้า / # อื่น / # endif" preprocessor แมโคร (ถึงแม้จะมีข้อ จำกัด เพิ่มเติม) ตามเอกสารแอปเปิ้ล นี่คือตัวอย่าง:

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

ตอนนี้คุณต้องตั้งค่าสัญลักษณ์ "DEBUG" ที่อื่น ตั้งค่าในส่วน "Swift Compiler - Custom Flags" บรรทัด "Other Swift Flags" คุณเพิ่มสัญลักษณ์การดีบักด้วย-D DEBUGรายการ

ตามปกติคุณสามารถตั้งค่าที่แตกต่างกันเมื่ออยู่ใน Debug หรือเมื่ออยู่ใน Release

ฉันทดสอบด้วยโค้ดจริงและใช้งานได้ มันดูเหมือนจะไม่เป็นที่รู้จักในสนามเด็กเล่น

คุณสามารถอ่านโพสต์เดิมของฉันที่นี่


หมายเหตุสำคัญ: -DDEBUG=1ไม่ทำงาน ใช้-D DEBUGงานได้เท่านั้น คอมไพเลอร์ Seems ไม่สนใจการตั้งค่าสถานะด้วยค่าเฉพาะ


41
นี่คือคำตอบที่ถูกต้องแม้ว่าควรสังเกตว่าคุณสามารถตรวจสอบการมีอยู่ของธง แต่ไม่ใช่ค่าเฉพาะ
Charles Harley

19
หมายเหตุเพิ่มเติม : ด้านบนของการเพิ่ม-D DEBUGตามที่ระบุไว้ข้างต้นคุณยังจำเป็นต้องกำหนดDEBUG=1ใน->Apple LLVM 6.0 - Preprocessing Preprocessor Macros
Matthew Quiros

38
ฉันไม่สามารถรับการทำงานจนกว่าฉันจะเปลี่ยนรูปแบบไป-DDEBUGจากคำตอบนี้: stackoverflow.com/a/24112024/747369
Kramer

11
@MattQuiros ไม่จำเป็นต้องมีการเพิ่มเป็นDEBUG=1ไปPreprocessor Macrosถ้าคุณไม่ต้องการที่จะใช้ในวัตถุประสงค์รหัส C
derpoliuk

7
@Daniel คุณสามารถใช้โอเปอเรเตอร์บูลีนมาตรฐาน (เช่น: # #! DEBUG `)
Jean Le Moignan

353

ตามที่ระบุในApple Docs

คอมไพเลอร์ Swift ไม่มีตัวประมวลผลล่วงหน้า แต่จะใช้ประโยชน์จากคุณลักษณะเวลาคอมไพล์สร้างการกำหนดค่าและคุณสมบัติภาษาเพื่อทำหน้าที่การทำงานเดียวกัน ด้วยเหตุผลนี้คำสั่ง preprocessor จะไม่ถูกนำเข้าใน Swift

ฉันจัดการเพื่อให้บรรลุสิ่งที่ฉันต้องการโดยใช้การกำหนดค่า Build ที่กำหนดเอง:

  1. ไปที่โครงการของคุณ / เลือกเป้าหมาย / การตั้งค่าการสร้าง / ค้นหาธงที่กำหนดเอง
  2. สำหรับเป้าหมายที่คุณเลือกตั้งค่าสถานะที่กำหนดเองของคุณโดยใช้คำนำหน้า -D (ไม่มีช่องว่างสีขาว) สำหรับทั้ง Debug และ Release
  3. ทำตามขั้นตอนข้างต้นสำหรับทุกเป้าหมายที่คุณมี

นี่คือวิธีตรวจสอบเป้าหมาย:

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

ป้อนคำอธิบายรูปภาพที่นี่

ทดสอบโดยใช้ Swift 2.2


4
1. ด้วย white space ทำงานด้วย 2. ควรตั้งค่าสถานะเฉพาะ Debug หรือไม่
c0ming

3
@ c0 การใช้มันขึ้นอยู่กับความต้องการของคุณ แต่ถ้าคุณต้องการบางสิ่งบางอย่างเกิดขึ้นเฉพาะในโหมดดีบั๊กและไม่ปล่อยคุณต้องลบ -DDEBUG จาก Release
cdf1982

1
หลังจากที่ฉันตั้งค่าสถานะที่กำหนดเอง-DLOCALบนของฉัน#if LOCAl #else #endifมันอยู่ใน#elseส่วน ฉันทำซ้ำเป้าหมายดั้งเดิมAppTargetและเปลี่ยนชื่อเป็นAppTargetLocal& ตั้งค่าสถานะที่กำหนดเอง
Perwyl Liu

3
@Andrej คุณรู้วิธีที่จะทำให้ XCTest รู้จักการตั้งค่าสถานะที่กำหนดเองเช่นกัน? ฉันรู้ว่ามันตกอยู่ใน#if LOCAL ผลลัพธ์ที่ตั้งใจเมื่อฉันทำงานกับเครื่องจำลองและตกลงไป#else ในระหว่างการทดสอบ ฉันต้องการให้มันตกอยู่ใน#if LOCALระหว่างการทดสอบ
Perwyl Liu

3
นี่ควรเป็นคำตอบที่ยอมรับได้ คำตอบที่ได้รับการยอมรับในปัจจุบันนั้นไม่ถูกต้องสำหรับ Swift เนื่องจากจะใช้กับ Objective-C เท่านั้น
miken.mkndev

171

ในหลาย ๆ สถานการณ์ที่คุณไม่ได้จริงๆต้องมีเงื่อนไขรวบรวม ; คุณแค่ต้องการพฤติกรรมตามเงื่อนไขที่คุณสามารถเปิดและปิด สำหรับสิ่งนั้นคุณสามารถใช้ตัวแปรสภาพแวดล้อม นี่เป็นข้อได้เปรียบที่คุณไม่ต้องทำการคอมไพล์ซ้ำ

คุณสามารถตั้งค่าตัวแปรสภาพแวดล้อมและเปิดหรือปิดได้อย่างง่ายดายในตัวแก้ไขโครงการ:

ป้อนคำอธิบายรูปภาพที่นี่

คุณสามารถดึงข้อมูลตัวแปรสภาพแวดล้อมด้วย NSProcessInfo:

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

นี่คือตัวอย่างในชีวิตจริง แอพของฉันทำงานบนอุปกรณ์เท่านั้นเนื่องจากใช้ไลบรารีเพลงซึ่งไม่มีอยู่ในเครื่องมือจำลอง ถ้าเช่นนั้นฉันจะถ่ายภาพหน้าจอบน Simulator สำหรับอุปกรณ์ที่ฉันไม่ได้เป็นเจ้าของได้อย่างไร ฉันจะไม่สามารถส่งไปที่ AppStore ได้หากไม่มีภาพหน้าจอเหล่านั้น

ฉันต้องการข้อมูลปลอมและวิธีการที่แตกต่างกันของการประมวลผล ฉันมีตัวแปรสภาพแวดล้อมสองตัว: หนึ่งตัวซึ่งเมื่อเปิดสวิตช์บอกให้แอปสร้างข้อมูลปลอมจากข้อมูลจริงขณะที่ทำงานบนอุปกรณ์ของฉัน อีกอันหนึ่งซึ่งเมื่อเปิดเครื่องจะใช้ข้อมูลปลอม (ไม่ใช่ไลบรารีเพลงที่หายไป) ในขณะที่ทำงานบน Simulator การสลับโหมดเปิด / ปิดแต่ละโหมดนั้นเป็นเรื่องง่ายด้วยช่องทำเครื่องหมายตัวแปรสภาพแวดล้อมในตัวแก้ไข Scheme และโบนัสก็คือฉันไม่สามารถใช้มันในการสร้าง App Store ของฉันโดยไม่ได้ตั้งใจเพราะการเก็บถาวรไม่มีตัวแปรสภาพแวดล้อม


ด้วยเหตุผลบางอย่างตัวแปรสภาพแวดล้อมของฉันกลับมาเป็นศูนย์ในการเปิดตัวแอปที่สอง
Eugene

60
ระวัง : ตัวแปรสภาพแวดล้อมได้รับการตั้งค่าสำหรับการกำหนดค่าการสร้างทั้งหมดพวกเขาไม่สามารถตั้งค่าสำหรับแต่ละคน ดังนั้นนี่ไม่ใช่วิธีแก้ปัญหาที่ใช้งานได้หากคุณต้องการให้พฤติกรรมเปลี่ยนไปโดยขึ้นอยู่กับว่าเป็นรุ่นหรือรุ่นของ debug
Eric

5
@Eric เห็นด้วย แต่ไม่ได้ตั้งค่าไว้สำหรับการดำเนินการตามแผนทั้งหมด ดังนั้นคุณสามารถทำสิ่งหนึ่งในการสร้างและเรียกใช้และสิ่งที่แตกต่างในการเก็บถาวรซึ่งมักจะเป็นความแตกต่างในชีวิตจริงที่คุณต้องการวาด หรือคุณอาจมีหลายรูปแบบซึ่งเป็นรูปแบบทั่วไปในชีวิตจริง บวกกับที่ฉันพูดในคำตอบการสลับเปิดและปิดตัวแปรสภาพแวดล้อมในโครงการเป็นเรื่องง่าย
matt

10
ตัวแปรสภาพแวดล้อมไม่ทำงานในโหมดเก็บถาวร พวกเขาจะใช้เฉพาะเมื่อเปิดตัวแอพจาก XCode หากคุณพยายามเข้าถึงสิ่งเหล่านี้บนอุปกรณ์แอปจะหยุดทำงาน ค้นพบวิธีที่ยากลำบาก
iupchris10

2
@ iupchris10 "การเก็บถาวรไม่มีตัวแปรสภาพแวดล้อม" เป็นคำสุดท้ายของคำตอบของฉันข้างต้น ตามที่ข้าพเจ้าได้กล่าวว่าในคำตอบของฉันเป็นดี มันเป็นจุด
matt

159

การเปลี่ยนแปลงที่สำคัญของifdefการเปลี่ยนมากับ Xcode 8. ใช้ IE ของเงื่อนไขการรวบรวมการใช้งาน

อ้างถึงการก่อสร้างและการเชื่อมโยงในบันทึก Xcode 8 Release

การตั้งค่าการสร้างใหม่

การตั้งค่าใหม่: SWIFT_ACTIVE_COMPILATION_CONDITIONS

Active Compilation Conditionsis 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 เพิ่มเติมใน Xcode 8: http://www.miqu.me/blog/2016/07/31/xcode-8-new-build-settings-and-analyzer-improvements/


อย่างไรก็ตามมีการปิดใช้งานเงื่อนไขการคอมไพล์ชุดที่เวลาสร้าง? ฉันต้องปิดใช้งานเงื่อนไข DEBUG เมื่อสร้างการกำหนดค่าการดีบักสำหรับการทดสอบ
จอนนี่

1
@ Jonny วิธีเดียวที่ฉันพบคือการสร้างการกำหนดค่าการสร้างครั้งที่ 3 สำหรับโครงการ จากแท็บโครงการ> ข้อมูล> การกำหนดค่าให้กด '+' จากนั้นดีบักที่ซ้ำกัน จากนั้นคุณสามารถกำหนดเงื่อนไขการรวบรวมที่ใช้งานอยู่สำหรับการกำหนดค่านี้ อย่าลืมแก้ไข Target> Test schemes ของคุณเพื่อใช้การกำหนดค่า build ใหม่!
matthias

1
นี่ควรเป็นคำตอบที่ถูกต้อง .. มันเป็นสิ่งเดียวที่ทำงานกับฉันใน xCode 9 โดยใช้ Swift 4.x!
shokaveli

1
BTW ใน Xcode 9.3 Swift 4.1 DEBUG มีอยู่แล้วใน Active Compilation conditions และคุณไม่ต้องเพิ่มอะไรเพื่อตรวจสอบการกำหนดค่า DEBUG เพียง #if DEBUG และ #endif
เดนิส Kutlubaev

ฉันคิดว่านี่เป็นทั้งเรื่องนอกเรื่องและเป็นเรื่องที่ต้องทำ คุณไม่ต้องการที่จะปิดการใช้งานเงื่อนไขการรวบรวมที่ใช้งานอยู่ คุณต้องการการกำหนดค่าใหม่และการทดสอบที่แตกต่างซึ่งจะไม่มีแท็ก "Debug" เรียนรู้เกี่ยวกับแบบแผน
Motti Shneor

93

ในฐานะของ Swift 4.1 หากคุณต้องการเพียงแค่ตรวจสอบว่ามีการสร้างรหัสด้วยการดีบักหรือการกำหนดค่าการปล่อยคุณอาจใช้ฟังก์ชั่นในตัว:

  • _isDebugAssertConfiguration()(จริงเมื่อตั้งค่าการปรับให้เหมาะสม-Onone)
  • _isReleaseAssertConfiguration()(จริงเมื่อตั้งค่าการปรับให้เหมาะสม-O) (ไม่พร้อมใช้งานใน Swift 3+)
  • _isFastAssertConfiguration()(จริงเมื่อตั้งค่าการปรับให้เหมาะสม-Ounchecked)

เช่น

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

เปรียบเทียบกับมาโครตัวประมวลผลล่วงหน้า

  • ✓คุณไม่จำเป็นต้องกำหนด-D DEBUGธงที่กำหนดเองเพื่อใช้งาน
  • ~ มันถูกกำหนดในแง่ของการตั้งค่าการเพิ่มประสิทธิภาพไม่ใช่การกำหนดค่า Xcode build
  • ✗ไม่มีเอกสารซึ่งหมายความว่าสามารถลบฟังก์ชั่นได้ในการอัปเดตใด ๆ (แต่ควรเป็น AppStore-safe เนื่องจากเครื่องมือเพิ่มประสิทธิภาพจะเปลี่ยนเป็นค่าคงที่)

  • ✗ใช้ในถ้า / อื่นจะสร้างคำเตือน "จะไม่ถูกดำเนินการ" เสมอ


1
ฟังก์ชันในตัวเหล่านี้ถูกประเมิน ณ เวลารวบรวมหรือรันไทม์หรือไม่
ma11hew28

@MattDiPasquale การเพิ่มประสิทธิภาพเวลา if _isDebugAssertConfiguration()จะได้รับการประเมินif falseในโหมดรีลีสและif trueเป็นโหมดดีบั๊ก
kennytm

2
ฉันไม่สามารถใช้ฟังก์ชั่นเหล่านี้เพื่อยกเลิกการเลือกตัวแปรเฉพาะการดีบักในรีลีสได้
Franklin Yu

3
ฟังก์ชั่นเหล่านี้มีการบันทึกไว้ที่ไหนสักแห่ง?
Tom Harrington

7
ตั้งแต่ Swift 3.0 & XCode 8 ฟังก์ชันเหล่านี้ไม่ถูกต้อง
CodeBender

86

Xcode 8 ขึ้นไป

ใช้ที่ใช้งานเงื่อนไขการรวบรวมการตั้งค่าในการตั้งค่าการสร้าง / Swift คอมไพเลอร์ - ธงที่กำหนดเอง

  • นี่คือการตั้งค่าบิลด์ใหม่สำหรับการส่งแฟล็กการรวบรวมตามเงื่อนไขไปยังคอมไพเลอร์ Swift
  • ธงเพิ่มที่เรียบง่ายเช่นนี้ALPHA, BETAฯลฯ

จากนั้นตรวจสอบด้วยเงื่อนไขการคอมไพล์ดังนี้:

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

เคล็ดลับ: คุณยังสามารถใช้#if !ALPHAฯลฯ


77

ไม่มีตัวประมวลผลล่วงหน้าของ Swift (สำหรับสิ่งหนึ่งการแทนที่รหัสโดยพลการจะแบ่งประเภทและความปลอดภัยของหน่วยความจำ)

Swift จะมีตัวเลือกการกำหนดค่าบิลด์เวลาดังนั้นคุณจึงสามารถรวมโค้ดตามเงื่อนไขสำหรับแพลตฟอร์มบางอย่างหรือสร้างสไตล์หรือตอบสนองต่อการตั้งค่าสถานะที่คุณกำหนดด้วย-Dคอมไพล์ args แม้ว่าจะแตกต่างจาก C แต่ส่วนที่รวบรวมโดยมีเงื่อนไขของรหัสของคุณจะต้องเสร็จสมบูรณ์ มีส่วนเกี่ยวกับเรื่องนี้ในการใช้สวิฟท์กับโกโก้และวัตถุประสงค์ -C

ตัวอย่างเช่น:

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

34
"การเปลี่ยนรหัสโดยพลการจะแบ่งประเภทและความปลอดภัยของหน่วยความจำ" ตัวประมวลผลล่วงหน้าทำงานไม่ได้ก่อนที่คอมไพเลอร์จะทำงานอย่างไร ดังนั้นการตรวจสอบทั้งหมดเหล่านี้ยังคงเกิดขึ้น
Thilo

10
@Thilo ฉันคิดว่าสิ่งที่ผิดพลาดคือการสนับสนุน IDE
Aleksandr Dubinsky

1
ฉันคิดว่าสิ่งที่ @rickster กำลังจะเกิดขึ้นคือมาโคร C Preprocessor ไม่มีความเข้าใจเกี่ยวกับประเภทและการมีอยู่ของพวกเขาจะทำลายข้อกำหนดประเภทของ Swift เหตุผลที่มาโครทำงานใน C คือเนื่องจาก C อนุญาตการแปลงชนิดโดยนัยซึ่งหมายความว่าคุณสามารถทำให้INT_CONSTทุกที่ที่floatยอมรับได้ สวิฟท์จะไม่อนุญาตสิ่งนี้ นอกจากนี้หากคุณสามารถทำvar floatVal = INT_CONSTอย่างหลีกเลี่ยงไม่ได้มันจะพังทลายลงที่ใดที่หนึ่งในภายหลังเมื่อคอมไพเลอร์คาดหวังIntแต่คุณใช้มันเป็นFloat(ประเภทของการfloatValอนุมานเป็นInt) 10 บรรยากาศในภายหลังและเพียงแค่ทำความสะอาดในการลบแมโคร ...
แมลงเม่า

ฉันพยายามใช้สิ่งนี้ แต่ดูเหมือนว่าจะไม่ทำงาน แต่ก็ยังคงรวบรวมรหัส Mac บน iOS ที่สร้างขึ้น มีหน้าจอตั้งค่าอื่นที่ต้องปรับแต่งอีกหรือไม่
Maury Markowitz

1
@Thilo ถูกต้อง - โปรเซสเซอร์ล่วงหน้าไม่ทำลายความปลอดภัยของหน่วยความจำประเภทใด ๆ
tcurdt

50

สองเซ็นต์ของฉันสำหรับ Xcode 8:

ก) การตั้งค่าสถานะที่กำหนดเองโดยใช้-Dคำนำหน้าทำงานได้ดี แต่ ...

b) ใช้ง่ายกว่า:

ใน Xcode 8 มีส่วนใหม่: "เงื่อนไขการรวบรวมที่ใช้งานอยู่" ซึ่งมีสองแถวสำหรับการดีบักและปล่อย

เพียงเพิ่มคำจำกัดความของคุณโดยไม่-Dต้อง


ขอบคุณที่พูดถึงว่ามีสองแถวสำหรับการแก้ไขข้อผิดพลาดและการเปิดตัว
Yitzchak

ใครทดสอบสิ่งนี้ในรุ่น?
เกล็น

นี่คือคำตอบที่ปรับปรุงแล้วสำหรับผู้ใช้ที่รวดเร็ว -Dคือไม่มี
มณี

46

isDebug คงที่ตามเงื่อนไขการรวบรวมที่ใช้งานอยู่

อีกวิธีที่ง่ายกว่าอาจเป็นโซลูชันที่ยังคงเป็นบูลีนที่คุณสามารถส่งผ่านไปยังฟังก์ชั่นได้โดยไม่ต้อง#ifมีเงื่อนไขใด ๆ ในโค้ดเบสของคุณคือการกำหนดDEBUGให้เป็นหนึ่งในโครงการสร้างเป้าหมายของคุณActive Compilation Conditionsและรวมต่อไปนี้

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

isDebug คงที่ตามการตั้งค่าการเพิ่มประสิทธิภาพคอมไพเลอร์

แนวคิดนี้สร้างขึ้นจากคำตอบของ kennytm

ข้อได้เปรียบหลักเมื่อเปรียบเทียบกับของ kennytm คือสิ่งนี้ไม่ได้พึ่งพาวิธีการแบบส่วนตัวหรือแบบไม่มีเอกสาร

ในสวิฟต์ 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 build
  • เอกสารซึ่งหมายความว่าฟังก์ชั่นจะเป็นไปตามรูปแบบการวางจำหน่าย / การเลิก API ตามปกติ

  • ✓ใช้ในถ้า / อื่นจะไม่สร้างคำเตือน "จะไม่ถูกดำเนินการ"


25

Moignans คำตอบที่นี่ใช้ได้ดี นี่คือความสงบของข้อมูลในกรณีที่มันช่วย

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

คุณสามารถคัดค้านมาโครเช่นด้านล่าง

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

23

ในโครงการ Swift ที่สร้างด้วย Xcode เวอร์ชัน 9.4.1, Swift 4.1

#if DEBUG
#endif

ทำงานได้ตามค่าเริ่มต้นเนื่องจากใน Mac Preprocessor DEBUG = 1 ได้รับการตั้งค่าโดย Xcode แล้ว

ดังนั้นคุณสามารถใช้ #if DEBUG "out of box"

โดยวิธีการวิธีการใช้บล็อกการรวบรวมสภาพโดยทั่วไปจะเขียนไว้ในหนังสือของ Apple ภาษา Swift Programming 4.1 (ส่วนงบควบคุมคอมไพเลอร์) และวิธีการเขียนธงรวบรวมและสิ่งที่เป็นคู่ของมาโคร C ใน Swift เขียนใน อีกเล่มของ Apple ที่ใช้ Swift กับ Cocoa และ Objective C (ในส่วน Preprocessor Directives)

หวังว่าในอนาคต Apple จะเขียนเนื้อหาละเอียดและดัชนีสำหรับหนังสือของพวกเขา


17

XCODE 9 AND เหนือ

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

3
ว้าวนั่นเป็นตัวย่อที่น่าเกลียดที่สุดที่ฉันเคยเห็น: p
rmp251

7

หลังจากตั้งค่าDEBUG=1ในการตั้งค่าGCC_PREPROCESSOR_DEFINITIONSBuild ของคุณฉันชอบใช้ฟังก์ชั่นโทรออกนี้

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

และจากนั้นใส่ฟังก์ชันนี้ไว้ในบล็อกที่ฉันต้องการตัดออกใน Debug builds:

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

คือคอมไพเลอร์ตรวจสอบไวยากรณ์ของรหัสของฉันดังนั้นฉันแน่ใจว่าไวยากรณ์นั้นถูกต้องและสร้าง


3

คุณได้กดที่นี่! Swift #if ดูที่แฟล็กที่กำหนดเองไม่ใช่มาโครตัวประมวลผลล่วงหน้า โปรดอัปเดตคำตอบของคุณด้วยเนื้อหาจากลิงก์บ่อยครั้งที่ลิงก์จะแตกหลังจากผ่านไปครู่หนึ่ง
Dale

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

แหล่ง


1
นี่ไม่ใช่การรวบรวมตามเงื่อนไข ในขณะที่มีประโยชน์มันเป็นเพียงเงื่อนไขรันไทม์เก่าธรรมดา OP กำลังถามหลังจากรวบรวมเพื่อวัตถุประสงค์ในการ Metaprogramming
Shayne

3
เพียงเพิ่ม@inlinableด้านหน้าfuncและนี่จะเป็นวิธีที่หรูหราและสำนวนที่สุดสำหรับ Swift ในรุ่นต่อcode()บล็อกของคุณจะได้รับการปรับปรุงและกำจัดออกไปโดยสิ้นเชิง ใช้ฟังก์ชั่นที่คล้ายกันในกรอบ NIO ของ Apple
mojuba

1

สิ่งนี้สร้างขึ้นจากคำตอบของ 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 จะกำจัดการเรียกฟังก์ชันอย่างสมบูรณ์


กำจัดเช่นเดียวกับในการลบการโทรอย่างสมบูรณ์เมื่อไม่ได้อยู่ในการแก้ปัญหา - เนื่องจากฟังก์ชั่นที่ว่างเปล่า? นั่นจะสมบูรณ์แบบ
Johan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.