【问题标题】:Protocol function implementation without actually conforming to a protocol协议功能实现而不实际符合协议
【发布时间】:2017-05-02 14:37:37
【问题描述】:

我是 Swift 初学者,我有一个关于协议的问题。我遵循了一个教你链表的教程,内容如下:

节点:

class LinkedListNode<Key> {
    let key: Key
    var next: LinkedListNode?
    weak var previous: LinkedListNode?

    init (key: Key) {
        self.key = key
    }
}

还有链表:

class LinkedList<Element>: CustomStringConvertible {
    typealias Node = LinkedListNode<Element>

    private var head: Node?

    // irrelevant code removed here

    var description: String {
        var output = "["
        var node = head
        while node != nil {
            output += "\(node!.key)"
            node = node!.next
            if node != nil { output += ", " }
        }
        return output + "]"
    }
}

var description: String 实现只是让您打印链接列表中的每个元素。

到目前为止,我了解了链表的结构,我的问题实际上与链表无关。我不明白的是协议CustomStringConvertible。如果我只有var description: String 实现而不符合协议,为什么会出错?我的意思是,这个协议只是简单地说“嘿,你需要实现var description: String,因为你符合我,但是为什么我们不能在不符合协议的情况下实现var description: String?

是不是因为在后台,有一个函数或某种类型接受 CustomStringConvertible 类型并通过一些代码运行它,瞧!出现文字。

【问题讨论】:

  • 我不确定我是否完全理解你的问题——你是在问为什么类型不隐式地符合协议,如果它们恰好实现了它们的所有要求?情况并非如此是有充分理由的,因为大多数协议都有特定的语义要求。例如,Error 协议完全没有要求——但这并不意味着所有类型都是错误的。

标签: swift protocols standards-compliance


【解决方案1】:

为什么我们不能只实现var description: String而不符合协议?

比较:

class Foo {
    var description: String { return "my awesome description" }
}

let foo = Foo()

print("\(foo)") // return "stackoverflow.Foo" (myBundleName.Foo)

和

class Foo: CustomStringConvertible {
    var description: String { return "my awesome description" }
}

let foo = Foo()

print("\(foo)") // return "my awesome description"

当你使用CustomStringConvertible时,你保证这个类有变量description,然后,你可以调用它,而不知道其他的实现细节。

另一个例子:

(someObject as? CustomStringConvertible).description

我不知道 someObject 的类型,但是,如果它订阅了CustomStringConvertible,那么,我可以调用description。

【讨论】:

    【解决方案2】:

    如果您希望字符串插值使用您的description 属性,则必须符合CustomStringConvertible。

    你在 Swift 中像这样使用字符串插值:

    "Here's my linked list: \(linkedList)"
    

    编译器基本上把它变成这样:

    String(stringInterpolation:
        String(stringInterpolationSegment: "Here's my linked list: "),
        String(stringInterpolationSegment: linkedList),
        String(stringInterpolationSegment: ""))
    

    String(stringInterpolationSegment:)defined like this 有一个通用版本:

    public init<T>(stringInterpolationSegment expr: T) {
      self = String(describing: expr)
    }
    

    String(describing: ) 是defined like this:

    public init<Subject>(describing instance: Subject) {
      self.init()
      _print_unlocked(instance, &self)
    }
    

    _print_unlocked 是defined like this:

    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)
    }
    

    注意_print_unlocked只有在对象符合CustomStringConvertible时才调用对象的description方法。

    如果您的对象不符合CustomStringConvertible 或_print_unlocked 中使用的其他协议之一,则_print_unlocked 为您的对象创建一个Mirror,最终只打印对象的类型(例如@ 987654341@),仅此而已。

    【讨论】:

    • 谢谢,这很有意义
    【解决方案3】:

    CustomStringConvertible 允许您执行print(linkedListInstance),它将将描述设置器返回的任何内容打印到控制台。

    您可以在此处找到有关此协议的更多信息:https://developer.apple.com/reference/swift/customstringconvertible

    【讨论】:

      猜你喜欢
      • 2013-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多