删除 println() 发布版本 iOS Swift

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26913799/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 03:37:18  来源:igfitidea点击:

Remove println() for release version iOS Swift

iosxcodeswiftxcode6

提问by Nate Birkholz

I would like to globally ignore all println()calls in my Swift code if I am not in a Debug build. I can't find any robust step by step instructions for this and would appreciate guidance. is there a way to do this globally, or do I need to surround every println()with #IF DEBUG/#ENDIFstatements?

println()如果我不在调试版本中,我想全局忽略我的 Swift 代码中的所有调用。我找不到任何可靠的分步说明,希望得到指导。有没有办法在全球范围内做到这一点,还是我需要println()#IF DEBUG/#ENDIF语句包围每一个?

采纳答案by Nate Birkholz

As noted, i am a student and need things defined a little more clearly to follow along. After lots of research, the sequence I needed to follow is:

如前所述,我是一名学生,需要更清楚地定义一些事情才能跟上。经过大量研究,我需要遵循的顺序是:

Click on the project name at the top of the File Navigator at the left of the Xcode project window. This is line that has the name of the project, how many build targets there are, and the iOS SDK version.

单击 Xcode 项目窗口左侧 File Navigator 顶部的项目名称。这是包含项目名称、构建目标数量以及 iOS SDK 版本的行。

Choose the Build Settingstab and scroll down to the "Swift Compiler - Custom Flags" section near the bottom. Click the Down Arrow next to Other Flagsto expand the section.

选择Build Settings选项卡并向下滚动到靠近底部的“ Swift Compiler - Custom Flags”部分。单击其他标志旁边的向下箭头以展开该部分。

Click on the Debugline to select it. Place your mouse cursor over the right side of the line and double-click. A list view will appear. Click the +button at the lower left of the list view to add a value. A text field will become active.

单击调试行以选择它。将鼠标光标放在线的右侧并双击。将出现一个列表视图。单击列表视图左下方的+按钮以添加值。文本字段将变为活动状态。

In the text field, enter the text -D DEBUGand press Returnto commit the line.

在文本字段中,输入文本-D DEBUG并按Return提交该行。

Add a new Swift file to your project. You are going to want to make a custom class for the file, so enter text along the lines of the following:

将一个新的 Swift 文件添加到您的项目中。您将要为该文件创建一个自定义类,因此请按照以下内容输入文本:

class Log {

  var intFor : Int

  init() {
    intFor = 42
   }

  func DLog(message: String, function: String = __FUNCTION__) {
    #if DEBUG
      println("\(function): \(message)")
    #endif
  }
}

I was having trouble getting the class to be accepted by Xcode today, so the init may be a bit more heavyweight than necessary.

我今天很难让 Xcode 接受这个类,所以 init 可能比必要的更重量级。

Now you will need to reference your custom class in any class in which you intend to use the new custom function in place of println()Add this as a property in every applicable class:

现在,您需要在您打算使用新自定义函数代替将其println()添加为每个适用类中的属性的任何类中引用您的自定义类:

   let logFor = Log()

Now you can replace any instances of println()with logFor.DLog(). The output also includes the name of the function in which the line was called.

现在,你可以替换的任何实例println()logFor.DLog()。输出还包括调用该行的函数的名称。

Note that inside class functions I couldn't call the function unless I made a copy of the function as a class function in that class, and println()is also a bit more flexible with the input, so I couldn't use this in every instance in my code.

请注意,在类函数内部,除非我将该函数的副本作为该类中的类函数,否则我无法调用该函数,并且println()输入也更加灵活,因此我无法在每个实例中使用它我的代码。

回答by matt

The simplest way is to put your own global function in front of Swift's println:

最简单的方法是把你自己的全局函数放在 Swift 的前面println

func println(object: Any) {
    Swift.println(object)
}

When it's time to stop logging, just comment out the body of that function:

当需要停止记录时,只需注释掉该函数的主体:

func println(object: Any) {
    // Swift.println(object)
}

Or you can make it automatic by using a conditional:

或者您可以使用条件使其自动:

func println(object: Any) {
    #if DEBUG
        Swift.println(object)
    #endif
}

EDITIn Swift 2.0 printlnis changed to print. Unfortunately it now has a variadic first parameter; this is cool, but it means you can't easily override it because Swift has no "splat" operator so you can't pass a variadic in code (it can only be created literally). But you can make a reduced version that works if, as will usually be the case, you are printing just one value:

编辑在 Swift 2.0println中更改为print. 不幸的是,它现在有一个可变参数第一个参数;这很酷,但这意味着您无法轻松覆盖它,因为 Swift 没有“splat”运算符,因此您无法在代码中传递可变参数(它只能按字面意思创建)。但是,如果通常情况下您只打印一个值,则可以制作一个有效的简化版本:

func print(items: Any..., separator: String = " ", terminator: String = "\n") {
    Swift.print(items[0], separator:separator, terminator: terminator)
}

In Swift 3, you need to suppress the external label of the first parameter:

在 Swift 3 中,您需要取消第一个参数的外部标签:

func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
    Swift.print(items[0], separator:separator, terminator: terminator)
}

回答by Glavid

Updated for Swift 4.x:

为 Swift 4.x 更新:

With Swift 2.0/3.0 and Xcode 7/8 now out of beta, there have been some changes to how you disable the print function in release builds.

随着 Swift 2.0/3.0 和 Xcode 7/8 现已退出测试阶段,您在发布版本中禁用打印功能的方式发生了一些变化。

There are some important points mentioned by @matt and @Nate Birkholz above that are still valid.

上面@matt 和@Nate Birkholz 提到的一些重要观点仍然有效。

  1. The println()function has been replaced by print()

  2. To use the #if DEBUGmacro then you have to define the "Swift Compiler - Custom Flags -Other Flags" to contain the value -D DEBUG

  3. I would recommend overriding the Swift.print()function in the global scope so that you can use the print()function as normal in your code, but it will remove output for non-debug builds. Here is a function signature that you can add at the global scope to do this in Swift 2.0/3.0:

    func print(items: Any..., separator: String = " ", terminator: String = "\n") {
    
        #if DEBUG
    
        var idx = items.startIndex
        let endIdx = items.endIndex
    
        repeat {
            Swift.print(items[idx], separator: separator, terminator: idx == (endIdx - 1) ? terminator : separator)
            idx += 1
        }
        while idx < endIdx
    
        #endif
    }
    
  1. println()功能已被替换为print()

  2. 要使用 #if DEBUG宏,则必须定义“Swift Compiler - Custom Flags -Other Flags”以包含值-D DEBUG

  3. 我建议Swift.print()在全局范围内覆盖该函数,以便您可以print()在代码中正常使用该函数,但它会删除非调试版本的输出。这是一个函数签名,您可以在全局范围内添加它以在 Swift 2.0/3.0 中执行此操作:

    func print(items: Any..., separator: String = " ", terminator: String = "\n") {
    
        #if DEBUG
    
        var idx = items.startIndex
        let endIdx = items.endIndex
    
        repeat {
            Swift.print(items[idx], separator: separator, terminator: idx == (endIdx - 1) ? terminator : separator)
            idx += 1
        }
        while idx < endIdx
    
        #endif
    }
    

Note: We have set the default separator to be a space here, and the default terminator to be a newline. You can configure this differently in your project if you would like.

注意:我们在这里设置了默认分隔符为空格,默认终止符为换行符。如果您愿意,您可以在项目中对其进行不同的配置。

Hope this helps.

希望这可以帮助。

Update:

更新:

It is usually preferable to put this function at the global scope, so that it sits in front of Swift's printfunction. I find that the best way to organize this is to add a utility file to your project (like DebugOptions.Swift) where you can place this function at the global scope.

通常最好将此函数放在全局范围内,以便它位于 Swiftprint函数的前面。我发现组织它的最好方法是将一个实用程序文件添加到您的项目(如 DebugOptions.Swift)中,您可以在其中将此函数放置在全局范围内。

As of Swift 3 the ++operator will be deprecated. I have updated the snippet above to reflect this change.

从 Swift 3 开始,该++运算符将被弃用。我已经更新了上面的代码片段以反映这一变化。

回答by matt

The problem with all these approaches, including mine, is that they do not remove the overhead of evaluating the printarguments. No matter which of them you use, this is going to be expensive:

所有这些方法(包括我的)的问题在于它们没有消除评估print参数的开销。无论您使用它们中的哪一个,这都将是昂贵的:

print(myExpensiveFunction())

The only decent solution is to wrap the actual print call in conditional compilation (let's assume that DEBUGis defined only for debug builds):

唯一合适的解决方案是在条件编译中包装实际的打印调用(假设它DEBUG仅针对调试版本定义):

#if DEBUG
print(myExpensiveFunction())
#endif

That, and only that, prevents myExpensiveFunctionfrom being called in a release build.

只有这样,才能防止myExpensiveFunction在发布版本中被调用。

However, you can push back evaluation one level by using autoclosure. Thus, you could rewrite my solution (this is Swift 3) like this:

但是,您可以使用autoclosure将评估推迟一级。因此,您可以像这样重写我的解决方案(这是 Swift 3):

func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") {
    #if DEBUG
    Swift.print(item(), separator: separator, terminator: terminator)
    #endif
}

This solves the problem just in the case where you are printing just one thing, which is usually true. That's because item()is not called in release mode. print(myExpensiveFunction())thus ceases to be expensive, because the call is wrapped in a closure without being evaluated, and in release mode, it won't be evaluated at all.

这仅在您只打印一件事的情况下解决了问题,这通常是正确的。那是因为item()没有在发布模式下调用。print(myExpensiveFunction())因此不再是昂贵的,因为调用被包装在一个闭包中而没有被评估,并且在发布模式下,它根本不会被评估。

回答by Gene Loparco

Here is a function that I use, which works perfectly in Swift 3:

这是我使用的一个函数,它在 Swift 3 中完美运行:

func gLog<T>( _ object: @autoclosure() -> T, _ file: String = #file, _ function: String = #function, _ line: Int = #line)
    {
    #if DEBUG
        let value = object()
        let stringRepresentation: String

        if let value = value as? CustomDebugStringConvertible
            {
            stringRepresentation = value.debugDescription
            }
        else if let value = value as? CustomStringConvertible
            {
            stringRepresentation = value.description
            }
        else
            {
            fatalError("gLog only works for values that conform to CustomDebugStringConvertible or CustomStringConvertible")
            }

        let fileURL = NSURL(string: file)?.lastPathComponent ?? "Unknown file"
        let queue = Thread.isMainThread ? "UI" : "BG"
    let gFormatter = DateFormatter()
    gFormatter.dateFormat = "HH:mm:ss:SSS"
        let timestamp = gFormatter.string(from: Date())

        print("? \(timestamp) {\(queue)} \(fileURL) > \(function)[\(line)]: " + stringRepresentation + "\n")
    #endif
    }

Here is an example of the output it generates:

这是它生成的输出示例:

screenshot of output

输出截图

Explanation:

解释:

  • the green checkmark is used to enable you to quickly see your print (gLog) messages in the console, where they can sometimes get lost in a sea of other messages

  • the time/date stamp

  • the thread it is being run on -- in my case it is either the MainThread (which I call UI), or not the MainThread (which I call BG, for background thread)

  • the name of the file that the gLog message resides in

  • the function within the file that the gLog message resides in

  • the line number of the gLog message

  • the actual gLog message you would like to print out

  • 绿色复选标记用于使您能够在控制台中快速查看您的打印 (gLog) 消息,有时它们可​​能会在其他消息的海洋中迷失

  • 时间/日期戳

  • 它正在运行的线程——在我的情况下,它要么是 MainThread(我称之为 UI),要么不是 MainThread(我称之为 BG,用于后台线程)

  • gLog 消息所在的文件名

  • gLog 消息所在的文件中的函数

  • gLog 消息的行号

  • 您想要打印的实际 gLog 消息

Hope this is useful to someone else!

希望这对其他人有用!

回答by Trev14

Swift 4.2

斯威夫特 4.2

The code below is working perfectly for me:

下面的代码非常适合我:

func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
    #if DEBUG
    items.forEach {
        Swift.print(
items.forEach { item in
    Swift.print(item, separator: separator, terminator: terminator)        
}
, separator: separator, terminator: terminator) } #endif }

This function mirrors the default Swift print so you can use it the exact same way, like print("hello world")(no need to put in the separator or terminator parameters). Also, printing each item like this gets rid of annoying array brackets around the print statements that show up if you just pass itemsstraight into Swift.print().

此函数反映了默认的 Swift 打印,因此您可以以完全相同的方式使用它,例如print("hello world")(无需放入分隔符或终止符参数)。此外,像这样打印每个项目可以消除打印语句周围烦人的数组括号,如果您items直接传入Swift.print().

For anyone relatively new to Swift you may wonder what the heck $0is. It just represents the first argument passed into the forEachblock. The forEachstatement could also be written like this:

对于 Swift 相对较新的人来说,您可能想知道这到底$0是什么。它只代表传递给forEach块的第一个参数。该forEach声明也可以写成这样:

public func print(_ items: Any..., separator: String = default, terminator: String = default)

Lastly if you're interested, the Swift declaration of printlooks like this:

最后,如果您有兴趣,Swift 声明print如下所示:

// Gobal log() function
//
// note that empty functions are removed by the Swift compiler -> use #if $endif to enclose all the code inside the log()
// these log() statements therefore do not need to be removed in the release build !
//
// to enable logging
//
// Project -> Build Settings -> Swift Compiler - Custom flags -> Other Swift flags -> Debug
// add one of these 3 possible combinations :
//
//      -D kLOG_ENABLE
//      -D kLOG_ENABLE -D kLOG_DETAILS
//      -D kLOG_ENABLE -D kLOG_DETAILS -D kLOG_THREADS
//
// you can just call log() anywhere in the code, or add a message like log("hello")
//
func log(message: String = "", filePath: String = #file, line: Int = #line, function: String = #function) {
            #if kLOG_ENABLE

            #if kLOG_DETAILS

            var threadName = ""
            #if kLOG_THREADS
                threadName = NSThread.currentThread().isMainThread ? "MAIN THREAD" : (NSThread.currentThread().name ?? "UNKNOWN THREAD")
                threadName = "[" + threadName + "] "
            #endif

            let fileName = NSURL(fileURLWithPath: filePath).URLByDeletingPathExtension?.lastPathComponent ?? "???"

            var msg = ""
            if message != "" {
                msg = " - \(message)"
            }

            NSLog("-- " + threadName + fileName + "(\(line))" + " -> " + function + msg)
        #else
            NSLog(message)
        #endif
    #endif
}

The docs also say that the default separator is a single space (" ") and the default terminator is a newline ("\n") so my answer above mirrors the exact Swift implementation - although I never print more than one thing or change separator/terminators. But who knows, you may want to.

文档还说默认分隔符是一个空格 ( " ") 并且默认终止符是一个换行符 ( "\n") 所以我上面的回答反映了确切的 Swift 实现——尽管我从不打印多于一件事或更改分隔符/终止符。但谁知道呢,你可能想要。

回答by Ronny Webers

Tested with Swift 2.1& Xcode 7.1.1

使用Swift 2.1Xcode 7.1.1测试

There's an easy way to exclude all print statements from release versions, once you know that empty functions are removed by the Swift compiler.

一旦您知道Swift 编译器删除了空函数,就有一种简单的方法可以从发布版本中排除所有打印语句。

Side note : In the era of Objective-C, there was a pre-parser which could be used to remove NSLog statements before the compiler kicked in, like described in my answer here. But since Swift no longer has a pre-parser this approach is no longer valid.

旁注:在 Objective-C 时代,有一个预解析器可用于在编译器启动之前删除 NSLog 语句,如我在此处的回答中所述。但是由于 Swift 不再有预解析器,这种方法不再有效。

Here's what I use today as an advanced and easily configurable log function, without ever having to worry about removing it in release builds. Also by setting different compiler flags, you can tweak the information that is logged as needed.

这是我今天使用的高级且易于配置的日志功能,无需担心在发布版本中删除它。此外,通过设置不同的编译器标志,您可以根据需要调整记录的信息。

You can tweak the function as needed, any suggestion to improve it is welcome!

您可以根据需要调整功能,欢迎提出任何改进建议!

   2016-01-13 23:48:38.026 FoodTracker[48735:4147607] -- [MAIN THREAD] ViewController(19) -> viewDidLoad() - hello

Here's where you set the compiler flags :

这是您设置编译器标志的地方:

enter image description here

在此处输入图片说明

An example output with all flags on looks like this :

带有所有标志的示例输出如下所示:

    override func viewDidLoad() { log("hello")
    super.viewDidLoad()

   // Handle the text field's user input through delegate callbacks
   nameTextField.delegate = self
}

The code with the log() looks like this :

带有 log() 的代码如下所示:

#if !DEBUG
    func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { }
#endif

回答by Rivera

Even simpler, after making sure -D DEBUGis set for the OTHER_SWIFT_FLAGSDebug build settings:

更简单的是,在确保-D DEBUGOTHER_SWIFT_FLAGSDebug 构建设置设置后:

#if DEBUG
    print("?? Something weird happened")
#endif

回答by Javier Cadiz

XCode 8 introduced a few new build settings.
In particular one referred to Active Compilation Conditionsdoes in a similar way what Other Flagssettings did.

XCode 8 引入了一些新的构建设置
特别是提到的Active Compilation Conditions一个与其他标志设置所做的类似。

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

“活动编译条件”是一个新的构建设置,用于将条件编译标志传递给 Swift 编译器。

As per XCode 8 (tested in 8.3.2) you will get this by default:

根据 XCode 8(在 8.3.2 中测试),默认情况下您将获得:

enter image description here

在此处输入图片说明

So without any config you can write the following:

因此,无需任何配置,您就可以编写以下内容:

#if !DEBUG
 public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
}
#endif

I strongly recommend you that if you use this approach extensively create a class/struct/function that wraps this logging logic. You may want to extend this further down the road.

我强烈建议您,如果您广泛使用这种方法,请创建一个包装此日志记录逻辑的类/结构/函数。您可能希望将其进一步扩展。

回答by DuckDucking

Varun Naharia has the better solution so far. I would combine his answer with Rivera's ...

到目前为止,Varun Naharia 有更好的解决方案。我会将他的回答与里维拉的......

  1. create a -D DEBUGflag on the compiler directives, build settings.
  2. then add this code:

    #if !DEBUG
     public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
    }
    #endif
    
  1. -D DEBUG在编译器指令上创建一个标志,构建设置。
  2. 然后添加此代码:

    ##代码##

This code will convert every printinto nothing for release.

此代码会将所有print内容都转换为无内容以进行发布。