#ifdef thay thế bằng ngôn ngữ Swift


735

Trong C / C ++ / Objective C, bạn có thể xác định một macro sử dụng bộ tiền xử lý của trình biên dịch. Hơn nữa, bạn có thể bao gồm / loại trừ một số phần mã bằng các bộ tiền xử lý của trình biên dịch.

#ifdef DEBUG
    // Debug-only code
#endif

Có một giải pháp tương tự trong Swift?


1
Như một ý tưởng, bạn có thể đặt nó trong tiêu đề cầu nối obj-c của bạn ..
Matej

42
Bạn thực sự nên trao một câu trả lời vì bạn có nhiều lựa chọn và câu hỏi này đã giúp bạn nhận được rất nhiều phiếu bầu.
David H

Câu trả lời:


1069

Có, bạn có thể làm điều đó.

Trong Swift, bạn vẫn có thể sử dụng các macro tiền xử lý "# if / # other / # endif" (mặc dù bị ràng buộc nhiều hơn), theo tài liệu của Apple . Đây là một ví dụ:

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

Bây giờ, bạn phải đặt biểu tượng "DEBUG" ở nơi khác. Đặt nó trong phần "Trình biên dịch Swift - Cờ tùy chỉnh", dòng "Cờ Swift khác". Bạn thêm biểu tượng DEBUG với -D DEBUGmục nhập.

Như thường lệ, bạn có thể đặt một giá trị khác khi ở chế độ Gỡ lỗi hoặc khi phát hành.

Tôi đã thử nó trong mã thực và nó hoạt động; nó dường như không được công nhận trong một sân chơi.

Bạn có thể đọc bài viết gốc của tôi ở đây .


LƯU Ý QUAN TRỌNG: -DDEBUG=1 không hoạt động. Chỉ -D DEBUGhoạt động. Có vẻ trình biên dịch đang bỏ qua một cờ với một giá trị cụ thể.


41
Đây là câu trả lời đúng, mặc dù cần lưu ý rằng bạn chỉ có thể kiểm tra sự hiện diện của cờ chứ không phải là một giá trị cụ thể.
Charles Harley

19
Lưu ý bổ sung : Ngoài việc thêm -D DEBUGnhư đã nêu ở trên, bạn cũng cần xác định DEBUG=1trong Apple LLVM 6.0 - Preprocessing-> Preprocessor Macros.
Matthew Quiros

38
Tôi không thể làm việc này cho đến khi tôi thay đổi định dạng -DDEBUGtừ câu trả lời này: stackoverflow.com/a/24112024/747369 .
Kramer

11
@MattQuiros Không cần thêm DEBUG=1vào Preprocessor Macros, nếu bạn không muốn sử dụng nó trong mã Objective-C.
derpoliuk

7
@Daniel Bạn có thể sử dụng các toán tử boolean tiêu chuẩn (ví dụ: `#if!
DEBUG`

353

Như đã nêu trong Apple Docs

Trình biên dịch Swift không bao gồm bộ tiền xử lý. Thay vào đó, nó tận dụng các thuộc tính thời gian biên dịch, xây dựng cấu hình và các tính năng ngôn ngữ để thực hiện cùng chức năng. Vì lý do này, các chỉ thị tiền xử lý không được nhập trong Swift.

Tôi đã quản lý để đạt được những gì tôi muốn bằng cách sử dụng Cấu hình bản dựng tùy chỉnh:

  1. Chuyển đến dự án của bạn / chọn mục tiêu của bạn / Cài đặt bản dựng / tìm kiếm Cờ tùy chỉnh
  2. Đối với mục tiêu bạn đã chọn, đặt cờ tùy chỉnh của bạn bằng cách sử dụng tiền tố -D (không có khoảng trắng), cho cả Gỡ lỗi và Phát hành
  3. Thực hiện các bước trên cho mọi mục tiêu bạn có

Đây là cách bạn kiểm tra mục tiêu:

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

nhập mô tả hình ảnh ở đây

Đã thử nghiệm bằng Swift 2.2


4
1.với không gian trắng cũng hoạt động, 2. bạn có nên đặt cờ cho Debug không?
c0ming

3
@ c0ming tùy thuộc vào nhu cầu của bạn, nhưng nếu bạn muốn điều gì đó chỉ xảy ra trong chế độ gỡ lỗi và không phát hành, bạn cần xóa -DDEBUG khỏi Bản phát hành.
cdf1982

1
Sau khi tôi đặt cờ tùy chỉnh -DLOCAL, trên tôi #if LOCAl #else #endif, nó rơi vào #elsephần. Tôi đã sao chép mục tiêu ban đầu AppTargetvà đổi tên thành AppTargetLocal& đặt cờ tùy chỉnh.
Perwyl Liu

3
@Andrej bạn có biết làm thế nào để XCTest nhận ra các cờ tùy chỉnh không? Tôi nhận ra nó rơi vào #if LOCAL , kết quả dự định khi tôi chạy với trình giả lập và rơi vào #else trong quá trình thử nghiệm. Tôi muốn nó cũng rơi vào #if LOCALtrong quá trình thử nghiệm.
Perwyl Liu

3
Đây phải là câu trả lời được chấp nhận. Câu trả lời được chấp nhận hiện tại là không chính xác cho Swift vì nó chỉ áp dụng cho Objective-C.
miken.mkndev

171

Trong nhiều tình huống, bạn không thực sự cần biên dịch có điều kiện ; bạn chỉ cần hành vi có điều kiện mà bạn có thể bật và tắt. Đối với điều đó, bạn có thể sử dụng một biến môi trường. Điều này có lợi thế rất lớn mà bạn không thực sự phải biên dịch lại.

Bạn có thể đặt biến môi trường và dễ dàng bật hoặc tắt nó trong trình chỉnh sửa lược đồ:

nhập mô tả hình ảnh ở đây

Bạn có thể truy xuất biến môi trường bằng NSProcessInfo:

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

Đây là một ví dụ thực tế. Ứng dụng của tôi chỉ chạy trên thiết bị, vì nó sử dụng thư viện nhạc không tồn tại trên Trình mô phỏng. Sau đó, làm cách nào để chụp ảnh màn hình trên Trình mô phỏng cho các thiết bị tôi không sở hữu? Nếu không có những ảnh chụp màn hình đó, tôi không thể gửi AppStore.

Tôi cần dữ liệu giả và một cách xử lý khác . Tôi có hai biến môi trường: một biến, khi được bật, sẽ cho ứng dụng tạo dữ liệu giả từ dữ liệu thực trong khi chạy trên thiết bị của tôi; cái khác, khi được bật, sử dụng dữ liệu giả (không phải thư viện nhạc bị thiếu) trong khi chạy trên Trình mô phỏng. Việc bật / tắt từng chế độ đặc biệt đó thật dễ dàng nhờ các hộp kiểm biến môi trường trong trình soạn thảo Scheme. Và phần thưởng là tôi không thể vô tình sử dụng chúng trong bản dựng App Store của mình, vì việc lưu trữ không có biến môi trường.


Vì một số lý do, biến môi trường của tôi trở lại là con số không trong lần khởi chạy ứng dụng thứ hai
Eugene

60
Xem ra : Biến môi trường được đặt cho tất cả các cấu hình bản dựng, chúng không thể được đặt cho từng cấu hình. Vì vậy, đây không phải là giải pháp khả thi nếu bạn cần thay đổi hành vi tùy thuộc vào việc đó là bản phát hành hay bản dựng gỡ lỗi.
Eric

5
@Eric Đồng ý, nhưng chúng không được đặt cho tất cả các hành động chương trình. Vì vậy, bạn có thể làm một điều về xây dựng và chạy và một điều khác trên kho lưu trữ, đó thường là sự khác biệt trong cuộc sống thực mà bạn muốn vẽ. Hoặc bạn có thể có nhiều sơ đồ, cũng là một mô hình chung ngoài đời thực. Thêm vào đó, như tôi đã nói trong câu trả lời của mình, việc bật và tắt các biến môi trường trong sơ đồ rất dễ dàng.
matt

10
Biến môi trường KHÔNG hoạt động trong chế độ lưu trữ. Chúng chỉ được áp dụng khi ứng dụng được khởi chạy từ XCode. Nếu bạn cố truy cập những thứ này trên thiết bị, ứng dụng sẽ bị sập. Tìm ra cách khó khăn.
iupchris10

2
@ iupchris10 "Lưu trữ không có biến môi trường" là những từ cuối cùng trong câu trả lời của tôi, ở trên. Điều đó, như tôi nói trong câu trả lời của tôi, là tốt . Đó là điểm chính .
matt

160

Một thay đổi lớn đã thay ifdefthế bằng Xcode 8. tức là sử dụng Điều kiện biên dịch hoạt động .

Tham khảo Xây dựng và Liên kết trong Xcode 8 Ghi chú phát hành .

Cài đặt bản dựng mới

Cài đặt mới: SWIFT_ACTIVE_COMPILATION_CONDITIONS

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

Trước đây, chúng tôi đã phải khai báo các cờ biên dịch có điều kiện của bạn trong OTHER_SWIFT_FLAGS, hãy nhớ thêm trước -Diên vào cài đặt. Ví dụ: để biên dịch có điều kiện với giá trị MYFLAG:

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

Giá trị để thêm vào cài đặt -DMYFLAG

Bây giờ chúng ta chỉ cần chuyển giá trị MYFLAG sang cài đặt mới. Thời gian để di chuyển tất cả những giá trị biên dịch có điều kiện!

Vui lòng tham khảo liên kết bên dưới để biết thêm tính năng Cài đặt Swift Build trong Xcode 8: http://www.miqu.me/blog/2016/07/31/xcode-8-new-build-sinstall-and-analyzer-improvements/


Có cách nào để vô hiệu hóa một Điều kiện biên dịch hoạt động được thiết lập khi xây dựng không? Tôi cần phải vô hiệu hóa điều kiện DEBUG khi xây dựng cấu hình gỡ lỗi để kiểm tra.
Jonny

1
@Jonny Cách duy nhất tôi tìm thấy là tạo cấu hình bản dựng thứ 3 cho dự án. Từ tab Dự án> Thông tin> Cấu hình, nhấn '+', sau đó sao chép Gỡ lỗi. Sau đó, bạn có thể tùy chỉnh Điều kiện biên dịch hoạt động cho cấu hình này. Đừng quên chỉnh sửa Target> Các lược đồ thử nghiệm để sử dụng cấu hình bản dựng mới!
matthias

1
Đây phải là câu trả lời chính xác..có điều duy nhất phù hợp với tôi trên xCode 9 khi sử dụng Swift 4.x!
shokaveli

1
BTW, Trong Xcode 9.3 Swift 4.1 DEBUG đã có trong Điều kiện biên dịch hoạt động và bạn không phải thêm bất cứ điều gì để kiểm tra cấu hình DEBUG. Chỉ cần #if DEBUG và #endif.
Denis Kutlubaev

Tôi nghĩ rằng đây là cả ngoài chủ đề, và một điều xấu để làm. bạn không muốn tắt Điều kiện biên dịch hoạt động. bạn cần một cấu hình mới và khác để thử nghiệm - sẽ không có thẻ "Gỡ lỗi" trên đó. Tìm hiểu về các đề án.
Motti Shneor

93

Kể từ Swift 4.1, nếu tất cả những gì bạn cần chỉ là kiểm tra xem mã được xây dựng với cấu hình gỡ lỗi hay giải phóng, bạn có thể sử dụng các hàm tích hợp:

  • _isDebugAssertConfiguration()(đúng khi tối ưu hóa được đặt thành -Onone)
  • _isReleaseAssertConfiguration() (đúng khi tối ưu hóa được đặt thành -O) (không khả dụng trên Swift 3+)
  • _isFastAssertConfiguration()(đúng khi tối ưu hóa được đặt thành -Ounchecked)

ví dụ

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

So với các macro tiền xử lý,

  • ✓ Bạn không cần xác định một -D DEBUGcờ tùy chỉnh để sử dụng nó
  • ~ Nó thực sự được định nghĩa theo các cài đặt tối ưu hóa, không phải cấu hình xây dựng Xcode
  • Không có giấy tờ, có nghĩa là chức năng có thể bị xóa trong bất kỳ bản cập nhật nào (nhưng nó phải là AppStore an toàn vì trình tối ưu hóa sẽ biến những thứ này thành hằng số)

  • Sử dụng trong if / other sẽ luôn tạo cảnh báo "Sẽ không bao giờ được thực thi".


1
Là các hàm dựng sẵn này được đánh giá tại thời gian biên dịch hoặc thời gian chạy?
ma11hew28

@MattDiPasquale Thời gian tối ưu hóa. if _isDebugAssertConfiguration()sẽ được đánh giá if falseở chế độ phát hành và if truelà chế độ gỡ lỗi.
kennytm

2
Mặc dù vậy, tôi không thể sử dụng các hàm này để từ chối một số biến chỉ gỡ lỗi trong bản phát hành.
Franklin Yu

3
Những chức năng này được ghi nhận ở đâu đó?
Tom Harrington

7
Kể từ Swift 3.0 & XCode 8, các chức năng này không hợp lệ.
CodeBender

87

Xcode 8 trở lên

Sử dụng cài đặt Điều kiện biên dịch hoạt động trong Cài đặt bản dựng / Trình biên dịch Swift - Cờ tùy chỉnh .

  • Đây là cài đặt bản dựng mới để chuyển các cờ biên dịch có điều kiện sang trình biên dịch Swift.
  • Đơn giản add cờ như thế này: ALPHA, BETA, vv

Sau đó kiểm tra nó với các điều kiện biên dịch như thế này:

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

Mẹo: Bạn cũng có thể sử dụng, #if !ALPHAv.v.


77

Không có tiền xử lý Swift. (Đối với một điều, thay thế mã tùy ý phá vỡ loại an toàn và bộ nhớ.)

Mặc dù vậy, Swift bao gồm các tùy chọn cấu hình thời gian xây dựng, do đó, bạn có thể bao gồm mã một cách có điều kiện cho các nền tảng nhất định hoặc kiểu xây dựng hoặc để đáp ứng với các cờ bạn xác định với -Dtrình biên dịch. Tuy nhiên, không giống với C, một phần được biên dịch có điều kiện trong mã của bạn phải được hoàn thiện về mặt cú pháp. Có một phần về vấn đề này trong việc sử dụng Swift với ca cao và Objective-C .

Ví dụ:

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

34
"Đối với một điều, thay thế mã tùy ý phá vỡ loại an toàn và bộ nhớ." Không phải bộ xử lý trước thực hiện công việc của nó trước khi trình biên dịch thực hiện (do đó là tên)? Vì vậy, tất cả các kiểm tra vẫn có thể diễn ra.
Thilo

10
@Thilo Tôi nghĩ những gì nó phá vỡ là hỗ trợ IDE
Aleksandr Dubinsky

1
Tôi nghĩ những gì @rickster đang nhận được là các macro C Pre Processor không hiểu về loại và sự hiện diện của chúng sẽ phá vỡ các yêu cầu loại của Swift. Lý do macro hoạt động trong C là vì C cho phép chuyển đổi kiểu ngầm định, có nghĩa là bạn có thể đặt INT_CONSTbất cứ nơi nào a floatsẽ được chấp nhận. Swift sẽ không cho phép điều này. Ngoài ra, nếu bạn có thể làm var floatVal = INT_CONSTchắc chắn nó sẽ bị hỏng ở đâu đó sau đó khi trình biên dịch mong đợi Intnhưng bạn sử dụng nó như một Float(loại floatValsẽ được suy ra là Int). 10 phôi sau và nó chỉ sạch hơn để loại bỏ macro ...
Ephemera

Tôi đang cố gắng sử dụng nhưng nó dường như không hoạt động, nó vẫn đang biên dịch mã Mac trên các bản dựng iOS. Có một màn hình thiết lập khác ở đâu đó phải được điều chỉnh?
Maury Markowitz

1
@Thilo bạn đúng - bộ xử lý trước không phá vỡ bất kỳ loại hoặc bộ nhớ an toàn.
tcurdt

50

Hai xu của tôi cho Xcode 8:

a) Cờ tùy chỉnh sử dụng -Dtiền tố hoạt động tốt, nhưng ...

b) Sử dụng đơn giản hơn:

Trong Xcode 8 có một phần mới: "Điều kiện biên dịch hoạt động", đã có hai hàng, để gỡ lỗi và phát hành.

Đơn giản chỉ cần thêm định nghĩa của bạn KHÔNG CÓ -D.


Cảm ơn vì đã đề cập rằng có HAI ROWS CHO DEBUG VÀ ĐÁNG TIN CẬY
Yitzchak

Bất cứ ai đã thử nghiệm điều này trong phát hành?
Glenn

Đây là câu trả lời cập nhật cho người dùng nhanh chóng. tức là không có -D.
Mani

46

isDebug Constant dựa trên các điều kiện biên dịch hoạt động

Một giải pháp khác, có lẽ đơn giản hơn, vẫn dẫn đến một boolean mà bạn có thể chuyển vào các hàm mà không cần #ifđiều kiện trong toàn bộ cơ sở mã của bạn là xác định DEBUGlà một trong những mục tiêu xây dựng dự án của bạn Active Compilation Conditionsvà bao gồm các mục sau (tôi xác định nó là hằng số toàn cầu):

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

isDebug Constant dựa trên cài đặt tối ưu hóa trình biên dịch

Khái niệm này dựa trên câu trả lời của kennytm

Ưu điểm chính khi so sánh với kennytm, là điều này không dựa trên các phương pháp riêng tư hoặc không có giấy tờ.

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

So với các macro tiền xử lý và câu trả lời của kennytm ,

  • ✓ Bạn không cần xác định một -D DEBUGcờ tùy chỉnh để sử dụng nó
  • ~ Nó thực sự được định nghĩa theo các cài đặt tối ưu hóa, không phải cấu hình xây dựng Xcode
  • Tài liệu , có nghĩa là chức năng sẽ tuân theo các mẫu phát hành / khấu hao API thông thường.

  • ✓ Sử dụng trong if / other sẽ không tạo ra cảnh báo "Sẽ không bao giờ được thực thi".


25

Moignans trả lời ở đây hoạt động tốt. Đây là một thông tin khác trong trường hợp nó giúp,

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

Bạn có thể phủ nhận các macro như bên dưới,

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

23

Trong các dự án Swift được tạo bằng Xcode Phiên bản 9.4.1, Swift 4.1

#if DEBUG
#endif

hoạt động theo mặc định vì trong Macros tiền xử lý DEBUG = 1 đã được Xcode đặt.

Vì vậy, bạn có thể sử dụng #if DEBUG "ngoài hộp".

Nhân tiện, cách sử dụng các khối biên dịch điều kiện nói chung được viết trong cuốn sách của Apple Ngôn ngữ lập trình Swift 4.1 (phần Báo cáo kiểm soát trình biên dịch) và cách viết các cờ biên dịch và đối tác của các macro C trong Swift được viết trong một cuốn sách khác của Apple Sử dụng Swift với Ca cao và Mục tiêu C (trong phần Chỉ thị tiền xử lý)

Hy vọng trong tương lai Apple sẽ viết các nội dung chi tiết hơn và các chỉ mục cho sách của họ.


17

XCODE 9 VÀ TRÊN

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

3
wow đó là chữ viết tắt xấu nhất tôi từng thấy: p
rmp251

7

Sau khi cài đặt DEBUG=1trong GCC_PREPROCESSOR_DEFINITIONSCài đặt bản dựng, tôi thích sử dụng chức năng để thực hiện cuộc gọi này:

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

Và sau đó chỉ đưa vào hàm này bất kỳ khối nào tôi muốn bỏ qua trong các bản dựng Debug:

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

Lợi thế khi so sánh với:

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

Có phải trình biên dịch kiểm tra cú pháp mã của tôi không, vì vậy tôi chắc chắn rằng cú pháp của nó là chính xác và được xây dựng.



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

Nguồn


1
Đây không phải là biên dịch có điều kiện. Mặc dù hữu ích, nó chỉ là một điều kiện thời gian chạy cũ đơn giản. OP đang yêu cầu sau khi hoàn thành mục đích siêu lập trình
Shayne

3
Chỉ cần thêm @inlinablevào trước funcvà đây sẽ là cách thanh lịch và thành ngữ nhất cho Swift. Trong bản phát hành, bản dựng của bạn code()sẽ được tối ưu hóa và loại bỏ hoàn toàn. Một chức năng tương tự được sử dụng trong khung NIO của riêng Apple.
mojuba

1

Điều này dựa trên câu trả lời của Jon Willis dựa trên sự khẳng định, chỉ được thực hiện trong các phần tổng hợp của Debug:

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

Trường hợp sử dụng của tôi là để ghi nhật ký in báo cáo. Dưới đây là điểm chuẩn cho phiên bản Phát hành trên 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)" )

in:

Log: 0.0

Có vẻ như Swift 4 loại bỏ hoàn toàn chức năng gọi.


Loại bỏ, như trong việc loại bỏ toàn bộ cuộc gọi khi không gỡ lỗi - do chức năng bị trống? Điều đó sẽ hoàn hảo.
Johan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.