Swift: print() vs println() vs NSLog()

564

在IT技术中,printNSLogprintln有什么区别?我应该在何时使用它们?

例如,在Python中如果我想打印一个字典,我只需使用print myDict,但现在我有另外两个选项。我应该如何选择并使用它们?


1
可能是Swift中println和print的区别的重复问题。 - Connor
2
NSLog和打印NSDictionary没有给我任何有用的信息,这是怎么回事? - User
从iOS 10.0开始,建议使用os_log。请参见我的答案下面 - HuaTham
除了查看os_log的Swift文档之外,也可以尝试查看Objective-C页面的完整文档,它更为详尽 - mfaani
1
在iOS 14(和macOS 11等)中,为了更高效地记录日志,请使用Logger代替os_log - Rob
6个回答

941

几个区别:

  1. printprintln

    print 函数在调试应用程序时会将消息打印到 Xcode 控制台中。

    println 是其变体,在 Swift 2 中已被删除,不再使用。如果您看到旧代码正在使用 println,现在可以安全地将其替换为 print

    在 Swift 1.x 中,print 在打印字符串末尾没有添加换行符,而 println 则添加了。但是现在,print 始终在字符串末尾添加换行符。如果不想这样做,可以提供一个 terminator 参数,值为 ""

  2. NSLog

    • NSLog 将时间戳和标识符添加到输出中,而 print 不会。

    • NSLog 语句出现在设备的控制台和调试器的控制台中,而 print 只出现在调试器控制台中。

    • iOS 10-13/macOS 10.12-10.x 中的 NSLog 使用类似于 printf 的格式字符串,例如:

 NSLog("%0.4f", CGFloat.pi)

将产生:

2017-06-09 11:57:55.642328-0700 MyApp[28937:1751492] 3.1416

  • iOS 14 / macOS 11的NSLog支持字符串插值。(然而,在iOS 14和macOS 11中,我们通常会优先使用Logger而不是NSLog。请参见下一点)

  • 现在,虽然NSLog仍然可以工作,但我们通常会使用“统一日志记录”(见下文)而不是NSLog

  • 从iOS 14 / macOS 11开始,我们有了Logger界面来访问“统一日志记录”系统。有关Logger的介绍,请参见WWDC2020 在Swift中探索日志记录

    • 要使用Logger,必须导入os

    import os
    
  • NSLog 一样,统一日志记录将输出消息到 Xcode 调试控制台和设备控制台。

  • 创建一个 Logger 并记录一条消息:

  • let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "network")
    logger.log("url = \(url)")
    

    通过外部控制台应用程序观察应用程序时,您可以基于子系统(subsystem)类别(category)进行筛选。这非常有用,以便将调试消息与(a)由其他子系统代表您的应用程序生成的那些消息,或者(b)来自其他类别或类型的消息区分开来。

    您可以指定不同类型的日志记录消息,例如.info.debug.error.fault.critical.notice.trace等:

    logger.error("web service did not respond \(error.localizedDescription)")
    
    如果使用外部控制台应用程序,则可以选择仅查看某些类别的消息(例如,如果在Console“操作”菜单上选择“包含调试消息”,则仅显示调试消息)。这些设置还决定许多微妙的问题详细信息,例如是否将事物记录到磁盘。有关更多详细信息,请参见WWDC视频。
    默认情况下,在日志中会隐藏非数字数据。在您记录URL的示例中,如果从设备本身调用应用程序并且您正在从macOS控制台观看,则在macOS控制台中将看到以下内容: url = 如果您确信此消息不包括用户机密数据,并且想要在macOS控制台中查看字符串,则需要执行以下操作:
    logger.log("url = \(url, privacy: .public)")
    
  • iOS 14/macOS 11之前,iOS 10/macOS 10.12推出了用于“统一日志记录”的os_log。有关统一日志记录的介绍,请参见WWDC 2016视频Unified Logging and Activity Tracing

    • 导入 os.log

  • import os.log
    
    你应该定义 subsystemcategory:
    let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "network")
    
    使用 os_log 时,应使用 printf 样式的模式而不是字符串插值:
    os_log("url = %@", log: log, url.absoluteString)
    
    您可以指定不同类型的日志消息,包括.info.debug.error.fault(或.default):
    os_log("web service did not respond", type: .error)
    
    使用os_log时,不能使用字符串插值。例如,使用printLogger您可以这样做:
    logger.log("url = \(url)")
    

    但是使用 os_log,你需要这样做:

    os_log("url = %@", url.absoluteString)
    
    os_log强制实施相同的数据隐私,但您可以在printf格式化程序中指定公共可见性(例如%{public}@而不是%@)。例如,如果您想从外部设备查看它,您必须执行以下操作:
    os_log("url = %{public}@", url.absoluteString)
    
    您还可以使用“兴趣点”日志,如果您想查看从仪器中观察到的活动范围:
    let pointsOfInterest = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: .pointsOfInterest)
    

    并以以下方式开始一个范围:

    os_signpost(.begin, log: pointsOfInterest, name: "Network request")
    

    最后以以下方式结束:

    os_signpost(.end, log: pointsOfInterest, name: "Network request")
    

    欲知详情,请参见https://dev59.com/3mMk5IYBdhLWcg3wvARo#39416673

    总之,print在Xcode中用于简单日志记录已足够,但统一日志记录(无论是使用Logger还是os_log)实现了相同的功能并提供了更强大的能力。

    当调试iOS应用程序需要在Xcode之外测试时,统一日志记录的优势尤为突出。例如,在测试后台iOS应用进程(如后台获取)时,连接到Xcode调试器会改变应用生命周期。因此,您经常需要在物理设备上测试,从设备本身运行应用程序,而不是从Xcode的调试器启动应用程序。统一日志记录允许您仍然从macOS控制台应用程序监视iOS设备日志声明。


    44
    很好的总结!补充一些内容:你可以将NSString传递给println,但不能传递给NSLog;你可以为NSLog添加参数,但不能为println添加参数;Swift风格的字符串插值有时会导致NSLog崩溃,但不会影响println。 - Bao Lei
    2
    有关Swift编译器优化和print()使用的有趣注释 https://medium.com/ios-os-x-development/swift-log-devil-or-why-println-is-dangerous-46390453353d - Carl
    @Rob 如果我使用print,它会出现在调试器控制台中吗?还是我们应该使用debugPrint? - user4790024
    1
    如果您使用 print,它会显示在 Xcode 的调试区域中,就像 debugPrint 一样。唯一的区别是 print 最终会调用对象的 description 方法,而 debugPrint 则调用 debugDescription,后者可能比 description 更冗长。 - Rob
    @Rob,你的回答似乎解决了Swift中的登录问题。我正在尝试将其与我在升级到Xcode 9后在Objective C中遇到的NSLog问题联系起来。https://dev59.com/7qXja4cB1Zd3GeqPRG9B - Greg
    显示剩余10条评论

    85

    如果您正在使用Swift 2,现在只能使用print()将某些内容写入输出。

    Apple已将println()print()函数合并为一个函数。

    更新至iOS 9

    默认情况下,此函数通过添加换行符来终止所打印的行。

    print("Hello Swift")
    

    终止符

    要在输出值后不换行打印,请将空字符串作为终止符传递。

    print("Hello Swift", terminator: "")
    

    分隔符

    现在您可以使用分隔符将多个项目连接起来。

    print("Hello", "Swift", 2, separator:" ")
    

    两者皆可

    或者你可以以这种方式进行组合使用

    print("Hello", "Swift", 2, separator:" ", terminator:".")
    

    5
    "appendNewline" 的默认值为 "true"。 - Adam
    1
    在iOS(9.0)中,您需要使用terminator:"",例如print("...",terminator:"") - Khotu Nam
    你第一句话的说法是不正确的。NSLog()仍然可以在最新的Swift 2.x中工作。 - Sebastian

    64
    此外,Swift 2 还有 debugPrint() (以及 CustomDebugStringConvertible 协议)!
    不要忘记 debugPrint() ,它的作用和 print() 相似,但更适合 调试
    例如:
    • 字符串
      • print("Hello World!") 变成 Hello World
      • debugPrint("Hello World!") 变成 "Hello World" (有引号!)
    • 区间
      • print(1..<6) 变成 1..<6
      • debugPrint(1..<6) 变成 Range(1..<6)
    任何类都可以通过 CustomDebugStringConvertible 协议自定义其调试字符串表示形式。

    2
    DebugPrintable 协议已被重命名为 CustomDebugStringConvertible 协议 - Franklin Yu
    那么,Swift 中的 description 相当于 debugDescription,就像 Python 中的 str 相当于 repr 一样? - BallpointBen
    是的,我认为是这样的。 - Valentin Shergin

    44
    为了补充Rob的答案,自iOS 10.0以来,苹果公司推出了一个全新的“统一日志记录”系统,取代了现有的日志记录系统(包括ASL和Syslog、NSLog等),并且在性能上超越了现有的日志记录方法,这要归功于它的新技术,包括日志数据压缩和延迟数据收集。
    Apple
    统一日志记录系统提供了一个单一、高效、性能优异的API,用于捕获系统各个级别的消息。这个统一的系统将日志数据在内存中和磁盘上的数据存储中进行集中处理。
    苹果公司强烈建议使用os_log来记录各种消息,包括信息、调试、错误消息,因为相比之前的日志记录系统,它的性能得到了大幅度改进,并且其集中化数据收集使得开发人员可以方便地查看日志和活动。实际上,新系统可能是如此低的占用,以至于它不会导致“观察效应”,即如果您插入一个日志命令来干扰bug的定时发生,则bug会消失。

    Performance of Activity Tracing, now part of the new Unified Logging system

    您可以在这里详细了解此内容。

    总而言之:为方便个人调试,请使用print()(但在部署到用户设备时不会记录消息)。然后,尽可能多地使用统一日志记录(os_log)处理其他所有事项。


    15

    iOS记录器

    [控制台日志]

    1. NSLog - 添加元信息(如时间戳和标识符),并允许输出1023个字符。同时将消息打印到控制台。这是最慢的方法。不安全,因为其他应用程序可以访问日志文件。

      //@import Foundation //Objective-C
      @import Foundation
      NSLog("SomeString")
      
    2. print - 将所有字符串打印到Xcode。比前一种方法性能更好。

      //@import Foundation //Objective-C
      import Foundation
      print("SomeString")
      
    3. println(仅适用于Swift v1)并在字符串末尾添加\n

    4. os_log(从iOS v10开始)- 打印32768个字符,同时打印到控制台。比前一种方法性能更好。

      //@import os.log //Objective-C
      @import os.log
      os_log("SomeIntro: %@", log: .default, type: .info, "someString")
      
    5. Logger(从iOS v14开始)- 打印32768个字符,同时打印到控制台。比前一种方法性能更好。

      //@import os //Objective-C
      import os
      let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "someCategory")
      
      logger.log("some message \(Date)")
      //some message <private>
      //it is about privacy level
      
      logger.log("some message \(Date, privacy: .public)")
      //some message 2023-11-11 16:09:03 +0000
      

    6

    还有一种名为dump()的方法也可用于日志记录:

    func dump<T>(T, name: String?, indent: Int, maxDepth: Int, maxItems: Int)
    

    Dumps an object’s contents using its mirror to standard output.

    来自Swift标准库函数


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接