【问题标题】:Confused about the reference type assignment对引用类型分配感到困惑
【发布时间】:2021-12-27 05:43:02
【问题描述】:

以下是链表实现的一部分,我对语义感到困惑:

class Node<T> {
    var value: T
    var next: Node?
    weak var previous: Node?
    
    init(value: T) {
        self.value = value
    }
}

struct LinkedList<T> {
    var head: Node<T>?
    var tail: Node<T>?
    
    init() { }
    
    // what this method actually does is unimportant, but it creates a linked list from a sequence
    func createChain<S>(of sequence: S) -> (head: Node<T>, tail: Node<T>)? where S: Sequence, S.Element == T {
        var iterator = sequence.makeIterator()
        guard let firstValue = iterator.next() else {
            return nil
        }
        var head: Node<T>
        var tail: Node<T>
        
        var currentNode = Node(value: firstValue)
        head = currentNode
        
        while let nextValue = iterator.next() {
            let newNode = Node(value: nextValue)
            currentNode.next = newNode
            newNode.previous = currentNode
            currentNode = newNode // ?
        }
        
        tail = currentNode
        return (head: head, tail: tail)
    }
}

假设我要使用上面的代码创建一个链表:

let linkedList = LinkedList<Int>()
let sequence = [0, 1, 2]
let chain = linkedList.createChain(of: sequence)

while 循环的第一个循环中,快照如下:

while let nextValue = iterator.next() {
    let newNode = Node(value: nextValue) // newNode: previous = nil, next = nil, value = 1
    currentNode.next = newNode // currentNode: previous = nil, next = newNode, value = 0
    newNode.previous = currentNode // newNode: previous = currentNode, next = nil, value = 1
    currentNode = newNode // ?
}

当最后一行执行时,我表面上明白新节点必须不断替换当前节点才能循环遍历序列的所有元素,但我不确定当currentNode 设置@987654326 时会发生什么@。 newNode 覆盖 currentNode 是没有意义的,因为 currentNodenext 刚刚被分配了 newNodenewNodenext 为零。

如果newNode没有覆盖currentNodenewNode的内容如何不影响currentNode = newNode中的currentNode

【问题讨论】:

  • 只是将currentNode标记为当前列表的tail,然后不断追加到tail。你可以认为变量名是一个指针,实际对象的内存在某处。在第一轮循环之后,newNode 指针消失了,但它的内存仍然可以通过currentNode 访问。
  • currentNode.next = newNode 中的 currentNode.next 如何在 currentNode = newNode 之后发生时仍保持原样?
  • 因为Node是类,引用类型,所以当你分配新值时,它指向另一个地址。你应该多研究一下引用类型和值类型、指针和对象。

标签: ios swift data-structures linked-list


【解决方案1】:

类是引用类型。这意味着currentNode 持有对该Node 实例的引用。当您将新实例分配给 currentNode 时,您正在更改引用的对象。只要其他一些变量或属性持有对前一个实例的引用,它就会保留在内存中。

这就是为什么将第一个被称为currentNodeNode 分配给head 很重要的原因;如果head 没有持有引用,那么当新的Node 分配给currentNode 时,Node 将被释放。对后续 Nodes 的引用由 previousnext 属性保存,也将它们保存在内存中。

为了说明,我将使用由 *0、*1 等表示的虚构内存地址来展示序列:

while 循环之前:

  • currentNode = Node*0(0,nil,nil)
  • head = Node*0(0,nil,nil)

请注意,currentNodehead 指的是相同的内存地址。

第一次通过while 循环:

  • currentNode = Node*0(0,nil,nil)
  • newNode = Node*1(1,nil,nil)
  • currentNode = Node*0(0,Node*1,nil)
  • newNode = Node*1(0,nil,Node*0)
  • currentNode = Node*1(nil,Node*0)

请注意,currentNode 现在引用 Node*1head 仍然指的是Node*0,就像Node*1previous 值一样

下次通过while循环:

  • currentNode = Node*1(1,nil,Node*0)
  • newNode = Node*2(2,nil,nil)
  • currentNode = Node*1(0,Node*2,nil)
  • newNode = Node*2(0,nil,Node*1)
  • currentNode = Node*2(nil,Node*1)

等等

最后,createChain 的实现有点奇怪;它返回一个 (head,tail) 的元组,并在一个空的 LinkedList 上运行。

您可以将其实现为返回 LinkedList 的静态函数 -

struct LinkedList<T> {
    var head: Node<T>
    var tail: Node<T>
    
    // what this method actually does is unimportant, but it creates a linked list from a sequence
    static func createChain<S>(of sequence: S) -> LinkedList<S.Element>? where S: Sequence, S.Element == T {
        var iterator = sequence.makeIterator()
        guard let firstValue = iterator.next() else {
            return nil
        }
        var head: Node<S.Element>
        var tail: Node<S.Element>
        
        var currentNode = Node(value: firstValue)
        head = currentNode
        
        while let nextValue = iterator.next() {
            let newNode = Node(value: nextValue)
            currentNode.next = newNode
            newNode.previous = currentNode
            currentNode = newNode // ?
        }
        
        tail = currentNode
        return LinkedList(head: head, tail: tail)
    }
}

然后你可以创建你的链表

let sequence = [0, 1, 2]

let list = LinkedList.createChain(of: sequence)

【讨论】:

  • 在第一次循环结束时,currentNode.next = newNode 也修改了head 的内存,意味着它会有head = Node*0(0,Node*1,nil)。但是,当currentNode = newNode 发生时,为什么head 没有被修改?也就是说,为什么头部的记忆不是head = Node*1(nil,Node*0)
  • 这样想,如果我说currentScore = 1; oldScore = currentScore; currentScore = 2你不会想到oldScore会突然变成2。同样的事情; head 的值为“指向节点*0”。 currentNode = newNode 表示 currentNode 现在的值为“Pointint to Node*1”。 heads 值保持不变
  • 我明白了。那么在currentNode.next = newNode 的情况下说head 更改是否正确,因为它不是修改内存地址,而只是修改内容?另一方面,currentNode = newNode 改变了整个内存地址,因此head 不受影响。
  • head 不会改变。它指向的东西发生了变化。一旦你为currentNode 分配了一个新值,那么它和head 将指向不同的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-22
  • 2012-04-08
  • 1970-01-01
  • 1970-01-01
  • 2017-11-12
相关资源
最近更新 更多