Swift 源码解读 - Print.swift

作者: YxxxHao | 来源:发表于2017-11-21 21:23 被阅读221次

    从 Swift 开源以来,一直说要看下源码的,结果现在都拖到 Swift 4.x 了,今天抽时间编译了 Swift 的源码,如何编译这里就不介绍了,可以参考 如何阅读 Swift 标准库中的源码

    这里给自己定了个小目标,晚上抽空阅读下 Swift 源码,作为夜读系列,来扩展下自己的知识面。

    先看下 print() 的源码:

    @inline(never)
    public func print(
      _ items: Any...,
      separator: String = " ",
      terminator: String = "\n"
    ) {
      if let hook = _playgroundPrintHook {
        var output = _TeeStream(left: "", right: _Stdout())
        _print(
          items, separator: separator, terminator: terminator, to: &output)
        hook(output.left)
      }
      else {
        var output = _Stdout()
        _print(
          items, separator: separator, terminator: terminator, to: &output)
      }
    }
    

    @inline(never) 说明了这个函数 never 编译成 inline 的形式,@inline(__always) 则刚好相反。

    没有阅读源码前,一直都是靠习惯写着代码,并没有去看过 print() 的 api,一开始还真不知道 separator 和 terminator 两个参数,这两个参数也不用解析了,分割符和终止符。有没有想起曾经的 println() 了呢。

    if let hook = _playgroundPrintHook {}
    

    这句应该是判断是否 playground,然后再设置相应的 output,这里可以看出 playground 中通过 TeeStream 来 output,而其它则是 Stdout. 这个方法里面,实质调用的还是 _print() 方法:

    @_versioned
    @inline(never)
    internal func _print<Target : TextOutputStream>(
      _ items: [Any],
      separator: String = " ",
      terminator: String = "\n",
      to output: inout Target
    ) {
      var prefix = ""
      output._lock()
      defer { output._unlock() }
      for item in items {
        output.write(prefix)
        _print_unlocked(item, &output)
        prefix = separator
      }
      output.write(terminator)
    }
    

    上面代码可以看出,_print 是通过 _print_unlocked 输出内容的,我们再跳转到 -print_unlock 中查看:

    internal func _print_unlocked<T, TargetStream : TextOutputStream>(
      _ value: T, _ target: inout TargetStream
    ) {
      // Optional has no representation suitable for display; therefore,
      // values of optional type should be printed as a debug
      // string. Check for Optional first, before checking protocol
      // conformance below, because an Optional value is convertible to a
      // protocol if its wrapped type conforms to that protocol.
      if _isOptional(type(of: value)) {
        let debugPrintable = value as! CustomDebugStringConvertible
        debugPrintable.debugDescription.write(to: &target)
        return
      }
      if case let streamableObject as TextOutputStreamable = value {
        streamableObject.write(to: &target)
        return
      }
    
      if case let printableObject as CustomStringConvertible = value {
        printableObject.description.write(to: &target)
        return
      }
    
      if case let debugPrintableObject as CustomDebugStringConvertible = value {
        debugPrintableObject.debugDescription.write(to: &target)
        return
      }
    
      let mirror = Mirror(reflecting: value)
      _adHocPrint_unlocked(value, mirror, &target, isDebugPrint: false)
    }
    

    通过:

    rintableObject.description.write(to: &target)
    

    可以看出来,如果输入的对象如果实现 TextOutputStreamable,则打印出来的是它的值,如果它实现的是 CustomStringConvertible 或者 CustomDebugStringConvertible 时,print 实际打印出来的 description 内容。

    CustomStringConvertible 和 CustomDebugStringConvertible 我们都是比较熟悉的,但是 TextOutputStreamable 这个并不多见,这时候就需要我们去查下文档了:

    F776601F-EF83-4A07-89D8-7C7B61DEE27A.png

    如果是字符类型的,直接打印的就是它们的内容,但如果是整形、enum 这一些类型的怎么处理呢,看该方法最后两句代码:

    let mirror = Mirror(reflecting: value)
     _adHocPrint_unlocked(value, mirror, &target, isDebugPrint: false)
    

    这里做了一个映射的处理,根据不同的类型,按照不同的格式打印,这里就不继续说下去了,直接看下 _adHocPrint_unlocked 方法的源码就可以理解了。

    相关文章

      网友评论

        本文标题:Swift 源码解读 - Print.swift

        本文链接:https://www.haomeiwen.com/subject/fxlgvxtx.html