如果我不在Debug构建中,我想全局忽略我的Swift代码中的所有println()
调用。我找不到任何有力的分步说明,并希望得到指导。有没有办法在全球范围内做到这一点,还是我需要用println()
语句包围每个#IF DEBUG/#ENDIF
?
最简单的方法是将自己的全局函数放在Swift的println
前面:
func println(object: Any) {
Swift.println(object)
}
当停止记录时,只需注释掉该函数的主体:
func println(object: Any) {
// Swift.println(object)
}
或者您可以使用条件使其自动化:
func println(object: Any) {
#if DEBUG
Swift.println(object)
#endif
}
编辑在Swift 2.0中println
改为print
。不幸的是,它现在有一个可变的第一个参数;这很酷,但这意味着你不能轻易覆盖它,因为Swift没有“splat”操作符,所以你不能在代码中传递一个可变参数(它只能按字面创建)。但是,如果通常情况下只打印一个值,则可以制作缩小版本:
func print(items: Any..., separator: String = " ", terminator: String = "\n") {
Swift.print(items[0], separator:separator, terminator: terminator)
}
在Swift 3中,您需要禁止第一个参数的外部标签:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
Swift.print(items[0], separator:separator, terminator: terminator)
}
Swift 4 Xcode 10.0
也许你可以用这个
func dPrint(_ message: @autoclosure () -> Any) {
#if DEBUG
print(message())
#endif
}
使用@autoclosure
的原因是,如果将函数作为消息参数传递,则仅在调试模式下调用该函数,这将导致性能损失。
与Swift.print(_ items: Any..., separator: String = default, terminator: String = default)
函数不同,我的解决方案只有一个参数,因为在大多数情况下,我们不传递多个参数,因为print函数只显示控制台中的信息,我们只需将参数转换为String:"\(param1)"+"\(param2)"
,对吧?希望你喜欢我的解决方案
您可以定义debug_println
,其内容大致为:
#if DEBUG
println()
#endif
我的解决方案是在课前在AppDelegate中使用此代码
// Disable console log in live app
#if !arch(x86_64) && !arch(i386)
public func debugPrint(items: Any..., separator: String = " ", terminator: String = "\n") {
}
public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
}
#endif
class AppDelegate: UIResponder, UIApplicationDelegate {
// App Delegate Code
}
对于我的解决方案,我说得很简单
import UIKit
class DLog: NSObject {
init(title:String, log:Any) {
#if DEBUG
print(title, log)
#endif
}
}
然后显示它只是打电话
_ = DLog(title:"any title", log:Any)
针对Swift 4.x进行了更新:
由于Swift 2.0 / 3.0和Xcode 7/8现已超出测试版,因此在发布版本中如何禁用打印功能会有一些变化。
@matt和@Nate Birkholz提到的一些重要观点仍然有效。
println()
函数已被print()
取代#if DEBUG
宏,您必须定义“Swift编译器 - 自定义标志 - 其他标志”以包含值-D DEBUG
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
}
注意:我们在此处将默认分隔符设置为空格,并将默认终止符设置为换行符。如果您愿意,可以在项目中以不同方式进行配置。
希望这可以帮助。
更新:
通常最好将此函数放在全局范围内,以便它位于Swift的print
函数前面。我发现组织这个的最好方法是在项目中添加一个实用程序文件(比如DebugOptions.Swift),你可以将这个函数放在全局范围内。
从Swift 3开始,++
运营商将被弃用。我已更新上面的代码段以反映此更改。
所有这些方法(包括我的方法)的问题在于它们不会消除评估print
参数的开销。无论你使用哪一种,这都会很昂贵:
print(myExpensiveFunction())
唯一合适的解决方案是在条件编译中包装实际的print调用(假设DEBUG
仅为调试版本定义):
#if DEBUG
print(myExpensiveFunction())
#endif
只有那样,才能防止在发布版本中调用myExpensiveFunction
。
但是,您可以使用autoclosure将评估推迟一级。因此,您可以像这样重写我的解决方案(这是Swift 3):
func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") {
#if DEBUG
Swift.print(item(), separator: separator, terminator: terminator)
#endif
}
这只是在你打印一件事情的情况下解决问题,这通常是正确的。那是因为在发布模式下没有调用item()
。 print(myExpensiveFunction())
因此不再昂贵,因为调用被封装在封闭中而没有被评估,并且在释放模式下,它根本不会被评估。
如上所述,我是一名学生,需要更清楚地定义一些内容。经过大量研究,我需要遵循的顺序是:
单击Xcode项目窗口左侧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
}
}
我今天很难让这个类被Xcode接受,所以init可能比必要的重量级更重。
现在,您需要在任何要使用新自定义函数的类中引用自定义类来代替println()
在每个适用的类中将其添加为属性:
let logFor = Log()
现在你可以用println()
替换logFor.DLog()
的任何实例。输出还包括调用该行的函数的名称。
请注意,在类函数内部我无法调用函数,除非我将该函数的副本作为该类中的类函数,并且println()
对输入也更加灵活,所以我无法在每个实例中使用它在我的代码中。
这是我使用的一个函数,它在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
}
以下是它生成的输出示例:
说明:
希望这对其他人有用!
使用Swift 2.1和Xcode 7.1.1进行测试
一旦您知道Swift编译器删除了空函数,就可以轻松地从发布版本中排除所有打印语句。
旁注:在Objective-C时代,有一个预解析器可用于在编译器启动之前删除NSLog语句,如我的回答here中所述。但是由于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
}
这是您设置编译器标志的位置:
带有所有标志的示例输出如下所示:
2016-01-13 23:48:38.026 FoodTracker[48735:4147607] -- [MAIN THREAD] ViewController(19) -> viewDidLoad() - hello
log()的代码如下所示:
override func viewDidLoad() { log("hello")
super.viewDidLoad()
// Handle the text field's user input through delegate callbacks
nameTextField.delegate = self
}
XCode 8引入了一些new build settings。
特别是一个提到Active Compilation Conditions
的方式与其他Flags设置的方式类似。
“Active Compilation Conditions”是一个新的构建设置,用于将条件编译标志传递给Swift编译器。
根据XCode 8(在8.3.2中测试),您将默认获得此信息:
所以没有任何配置你可以写下面的内容:
#if DEBUG
print("⚠️ Something weird happened")
#endif
我强烈建议您如果使用此方法,请广泛创建一个包含此日志记录逻辑的类/结构/函数。您可能希望在此之后进一步扩展这一点。
Swift 4.2
下面的代码对我来说非常有用:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
#if DEBUG
items.forEach {
Swift.print($0, separator: separator, terminator: terminator)
}
#endif
}
此函数镜像默认的Swift打印,因此您可以使用完全相同的方式,例如print("hello world")
(无需放入分隔符或终止符参数)。此外,如果你只是将items
直接传递给Swift.print()
,那么打印这样的项目会消除打印语句周围恼人的数组括号。
对于任何相对较新的Swift,你可能想知道什么是$0
。它只代表传递给forEach
块的第一个参数。 forEach
声明也可以这样写:
items.forEach { item in
Swift.print(item, separator: separator, terminator: terminator)
}
最后,如果你有兴趣,print
的Swift声明如下:
public func print(_ items: Any..., separator: String = default, terminator: String = default)
文档还说默认分隔符是单个空格(" "
),默认终结符是换行符("\n"
),所以我上面的答案反映了确切的Swift实现 - 尽管我从不打印多个东西或更改分隔符/终止符。但谁知道,你可能想要。
在确保为-D DEBUG
Debug构建设置设置OTHER_SWIFT_FLAGS
之后,更简单:
#if !DEBUG
func println(object: Any) {}
func print(object: Any){}
#endif
在Swift 2 / Xcode 7中,您不再需要/使用println
,但您可能需要添加以下行:
func print(_ items: Any..., separator separator: String = default, terminator terminator: String = default)