删除发行版iOS Swift的println()

Nat*_*olz 77 xcode ios swift xcode6

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

mat*_*att 97

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

func println(object: Any) {
    Swift.println(object)
}
Run Code Online (Sandbox Code Playgroud)

什么时候停止记录,只需注释掉该函数的主体:

func println(object: Any) {
    // Swift.println(object)
}
Run Code Online (Sandbox Code Playgroud)

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

func println(object: Any) {
    #if DEBUG
        Swift.println(object)
    #endif
}
Run Code Online (Sandbox Code Playgroud)

编辑在Swift 2.0 println中更改为print.不幸的是,它现在有一个可变的第一个参数; 这很酷,但这意味着你不能轻易覆盖它,因为Swift没有"splat"操作符,所以你不能在代码中传递一个可变参数(它只能按字面创建).但是,如果通常情况下只打印一个值,则可以制作缩小版本:

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

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

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

  • @NateBirkholz不,那是胡说八道.(他说,经过测试确认!) (12认同)
  • 好的解决方案,谢谢.我被告知在iOS(但不是OS X)中,```println()```不会在发布模式下执行. (3认同)
  • 如果您将这些日志语句插入代码的高性能部分,这里需要注意一点:Swift仍然会花时间进行字符串插值和渲染参数传递给函数,即使它们不会被使用.我看到真正有条件地删除语句的唯一方法是在标志上预测它们.例如,if(log){print(..)}在每个使用它们的位置. (2认同)

Gla*_*vid 46

针对Swift 4.x进行了更新:

由于Swift 2.0/3.0和Xcode 7/8现已超出测试版,因此在发布版本中禁用打印功能的方式发生了一些变化.

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

  1. println()功能已被替换print()

  2. 要使用 #if DEBUG 宏,您必须定义"Swift编译器 - 自定义标志 - 其他标志"以包含该值-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
    }
    
    Run Code Online (Sandbox Code Playgroud)

注意:我们在此处将默认分隔符设置为空格,并将默认终止符设置为换行符.如果您愿意,可以在项目中以不同方式进行配置.

希望这可以帮助.

更新:

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

从Swift 3开始,++运营商将被弃用.我已更新上面的代码段以反映此更改.

  • 所以我查找了print的引用(在didFinishLaunching中使用...),它指向了原始的打印函数Swift.把它和@ JonathanZhan的注释放在一起,我调整了函数看起来像这样,并且它工作正常:`public func print(_ items:Any ...,separator:String ="",terminator:String ="\n") {` (7认同)

mat*_*att 32

所有这些方法(包括我的方法)的问题在于它们不会消除评估print参数的开销.无论你使用哪一种,这都会很昂贵:

print(myExpensiveFunction())
Run Code Online (Sandbox Code Playgroud)

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

#if DEBUG
print(myExpensiveFunction())
#endif
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  • 我建议还添加`@available(*,不可用,消息:“使用单参数 print(item:Any)代替”) func print(_ items:Any ...,分隔符:String =“”,终止符:String = "\n") { }` 以避免错误地使用多参数打印 (3认同)

Nat*_*olz 16

如上所述,我是一名学生,需要更清楚地定义一些内容.经过大量研究,我需要遵循的顺序是:

单击Xco​​de项目窗口左侧File Navigator顶部的项目名称.这是具有项目名称,有多少构建目标以及iOS SDK版本的行.

选择Build Settings选项卡,然后向下滚动到底部附近的" Swift Compiler - Custom Flags "部分.单击Other Flags旁边的向下箭头以展开该部分.

单击Debug行以选择它.将鼠标光标放在该行的右侧,然后双击.将出现一个列表视图.单击列表视图左下角的+按钮以添加值.文本字段将变为活动状态.

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

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

class Log {

  var intFor : Int

  init() {
    intFor = 42
   }

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

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

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

   let logFor = Log()
Run Code Online (Sandbox Code Playgroud)

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

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

  • 无需为调试日志创建自定义类.使用全局函数更容易,更实用. (7认同)

Gen*_*rco 10

这是我使用的一个函数,它在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
    }
Run Code Online (Sandbox Code Playgroud)

以下是它生成的输出示例:

输出截图

说明:

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

  • 时间/日期戳

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

  • gLog消息所在的文件的名称

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

  • gLog消息的行号

  • 您要打印的实际gLog消息

希望这对其他人有用!


Tre*_*v14 9

斯威夫特4.2

下面的代码对我来说是完美的工作:

func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
    #if DEBUG
    items.forEach {
        Swift.print($0, separator: separator, terminator: terminator)        
    }
    #endif
}
Run Code Online (Sandbox Code Playgroud)

此函数镜像默认的Swift打印,因此您可以使用完全相同的方式进行使用,例如print("hello world")(无需放置分隔符或终止符参数)。另外,像这样打印每个项目都摆脱了打印语句周围烦人的数组括号,如果您items直接进入,则会出现这些语句Swift.print()

对于任何不熟悉Swift的人,您可能会想知道到底$0是什么。它只是代表传递到forEach块中的第一个参数。该forEach语句也可以这样写:

items.forEach { item in
    Swift.print(item, separator: separator, terminator: terminator)        
}
Run Code Online (Sandbox Code Playgroud)

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

public func print(_ items: Any..., separator: String = default, terminator: String = default)
Run Code Online (Sandbox Code Playgroud)

文档还说默认分隔符是单个空格(" "),默认终止符是换行符("\n"),所以我在上面的回答反映了Swift的确切实现-尽管我从不打印任何内容或更改分隔符/终止符。但是谁知道呢,您可能想要。

  • 在哪里声明这个函数,这是一些扩展还是类似的东西?我只是不想在每个文件中声明它) (2认同)

Ron*_*ers 8

使用Swift 2.1Xcode 7.1.1进行测试

一旦您知道Swift编译器删除了空函数,就可以轻松地从发布版本中排除所有打印语句.

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

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

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

// 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
}
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述

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

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

log()的代码如下所示:

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

   // Handle the text field's user input through delegate callbacks
   nameTextField.delegate = self
}
Run Code Online (Sandbox Code Playgroud)


Jav*_*diz 6

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

"Active Compilation Conditions"是一个新的构建设置,用于将条件编译标志传递给Swift编译器.

根据XCode 8(在8.3.2中测试),您将默认获得此信息:

在此输入图像描述

所以没有任何配置你可以写下面的内容:

#if DEBUG
    print("?? Something weird happened")
#endif
Run Code Online (Sandbox Code Playgroud)

我强烈建议您如果使用此方法,请广泛创建一个包含此日志记录逻辑的类/结构/函数.您可能希望在此之后进一步扩展这一点.


Riv*_*era 5

在确定-D DEBUG已设置OTHER_SWIFT_FLAGS调试构建设置后,甚至更简单:

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


Spa*_*Dog 5

到目前为止,瓦伦·纳哈里亚 (Varun Naharia) 拥有更好的解决方案。我会将他的答案与里维拉的答案结合起来......

  1. -D DEBUG在编译器指令、构建设置上创建一个标志。
  2. 然后添加这段代码:

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

该代码将把所有内容都转换print为无内容以供发布。


归档时间:

查看次数:

35416 次

最近记录:

6 年 前