Algorithm 删除外部头部参照时,ARC如何处理圆形链表?
我终于开始学习链表之类的东西了。我知道在垃圾收集环境中,当循环链表头被取消引用时,整个链表最终会被GC’d 我对Objective-C和Swift有很多经验,它们都使用自动参考计数而不是GC。据我所知,这将创建一个强引用循环(),因为head节点将持有对back的强引用,这也将持有对head的强引用。我不小心恰好这样做,遇到了强保留周期 在我看来,这造成了一个主要问题,因为它使循环链表完全不可释放,因为在ARC下,至少在Objective C和Swift中,您无法手动释放对象Algorithm 删除外部头部参照时,ARC如何处理圆形链表?,algorithm,automatic-ref-counting,theory,Algorithm,Automatic Ref Counting,Theory,我终于开始学习链表之类的东西了。我知道在垃圾收集环境中,当循环链表头被取消引用时,整个链表最终会被GC’d 我对Objective-C和Swift有很多经验,它们都使用自动参考计数而不是GC。据我所知,这将创建一个强引用循环(),因为head节点将持有对back的强引用,这也将持有对head的强引用。我不小心恰好这样做,遇到了强保留周期 在我看来,这造成了一个主要问题,因为它使循环链表完全不可释放,因为在ARC下,至少在Objective C和Swift中,您无法手动释放对象 解决方案是在每个节
解决方案是在每个节点上为“前部”和“返回”引用保留一个额外的弱引用属性,因为它们不能使用普通<代码> NeX/Obj>引用(因为这些将需要很强,否则中间列表中的节点将被解除分配)?或者我应该在删除对头部的最终引用之前,通过中断列表中的所有引用来伪手动解除分配列表?所有这些看起来都不那么美好,更多的是黑客而不是解决方案
实际上,如果有一个循环链表,其中最后一个节点强烈引用第一个节点,那么第一个节点将始终至少有一个引用,因此该列表将始终保留在内存中 虽然这表明,有时可以忽略这些循环,但在这种情况下,这是不可接受的,因为作为数据结构设计者,我们不控制链表的大小——它可以而且应该能够根据定义无限增长 我的解决方案是让节点知道它们的容器(即弱引用列表本身)。这样,我们可以通过使next
成为节点的计算属性来删除循环引用。当设置了实际的下一个节点时,我们将其返回为next
。当它是nil
时,我们返回container
的startNode
,因此我们得到了API,它以循环链表的形式出现,但在引擎盖下它只是一个普通的链表
下面是一些代码示例:
class CircularLinkedList<T> {
// MARK: - Internal structures
class Node {
let value: T
private var _next: Node?
weak fileprivate var container: CircularLinkedList<T>!
var next: Node! {
get {
if let next = _next {
return next
} else {
return container.startNode
}
}
set {
_next = newValue
}
}
init(value: T) {
self.value = value
}
}
// MARK: - Properties
var startNode: Node
var endNode: Node
// MARK: - Constructors
init(initialValue: T) {
startNode = Node(value: initialValue)
endNode = startNode
startNode.container = self
}
// MARK: - API
func append(newValue: T) {
let newNode = Node(value: newValue)
newNode.container = self
endNode.next = newNode
endNode = newNode
}
}
类循环链接列表{
//马克:内部结构
类节点{
let值:T
私有变量\u下一步:节点?
弱文件私有变量容器:循环链接列表!
下一步:节点{
得到{
如果让下一步=_下一步{
下一个返回
}否则{
return container.startNode
}
}
设置{
_下一步=新值
}
}
初始值(值:T){
自我价值=价值
}
}
//标记:-属性
var startNode:节点
变量endNode:Node
//马克:-建设者
初始值(初始值:T){
startNode=节点(值:initialValue)
endNode=startNode
startNode.container=self
}
//标记:-API
func追加(新值:T){
让newNode=Node(值:newValue)
newNode.container=self
endNode.next=newNode
endNode=newNode
}
}
有人可能会说,让节点
了解其容器列表并不是一个好主意。但是,使它成为fileprivate
我们将它隐藏起来,不让外界知道,在数据结构内部,我们知道应该正确使用它。这似乎是一个比在列表生命周期中手动打破引用周期更好的解决方案