简体   繁体   English

为什么 Swift ARC 不首先中断属性引用以防止保留循环?

[英]Why doesn't Swift ARC break property references first to prevent retain cycles?

I was just interested to know how ARC works when a block of code goes out of scope.我只是想知道当代码块超出范围时 ARC 是如何工作的。 I assume all the reference variables are set to nil/destroyed with properties excluded and all objects are destroyed that have a 0 reference count.我假设所有引用变量都设置为 nil/destroyed 并排除了属性,并且所有引用计数为 0 的对象都被销毁。 This scenario:这个场景:

A = nil
B = nil

Or perhaps all variables and property references are set to nil going from top to bottom which would keep any retain cycles because A and B are nil before the properties are accessed.或者可能所有的变量和属性引用从上到下都设置为 nil,这将保留任何保留循环,因为在访问属性之前 A 和 B 是 nil。

A = nil
B = nil
A?.macbook = nil
B?.person = nil

So I was just wondering why ARC doesn't cycle through the properties first (go backwards through the code) to remove property references first, this would break any retain cycles.所以我只是想知道为什么 ARC 不首先循环遍历属性(向后遍历代码)以首先删除属性引用,这会破坏任何保留循环。

    A?.macbook = nil
    B?.person = nil
    A = nil
    B = nil

I think I understand the basics of retain cycles, I'm just curious about the detail of what happens with ARC during an automated destruction phase eg finishing the execution of a function.我想我了解保留周期的基础知识,我只是对 ARC 在自动销毁阶段(例如完成函数的执行)期间发生的事情的细节感到好奇。 Perhaps properties references aren't accessed/destroyed because they are part of the object?也许属性引用没有被访问/销毁,因为它们是对象的一部分? maybe it is too computationally complex to check every property?也许检查每个属性在计算上太复杂了?

       var name: String
       init(name: String) {
           self.name = name
           print("The Person \(name) is born")
       }
       var macbook: Macbook?
       deinit {
           print("The Person \(name) has died")
       }
   }
   
   class Macbook {
       var model: String
       init(model: String) {
           self.model = model
           print("The \(model) Macbook is born")
       }
       var person: Person?
       deinit {
           print("The \(model) Macbook has expired")
       }
   }
   
   func runTasks() {
       var A: Person? = Person(name: "Reuben")
       var B: Macbook? = Macbook(model: "Pro 2020")
       
       //Setup Retain Cycle
       A?.macbook = B
       B?.person = A
       
       //A?.macbook = nil
       B?.person = nil //**Why doesn't ARC make this nil first to avoid retain cycles?**
       A = nil
       B = nil
   }
   
   runTasks()

You ask:你问:

I'm just curious about the detail of what happens with ARC during an automated destruction phase eg finishing the execution of a function.我只是想知道在自动销毁阶段 ARC 会发生什么的细节,例如完成函数的执行。

When you have a local variable that is referencing an instance of an object, it establishes a strong reference to that object.当您有一个引用对象实例的局部变量时,它会建立对该对象的强引用。 When the local variable falls out of scope, it releases its strong reference.当局部变量超出范围时,它会释放其强引用。 And if that was the last strong reference to the object, that object will be deallocated.如果这是对该对象的最后一个强引用,则该对象将被释放。

func runTasks() {
    var person = Person(name: "Reuben")
    
    // At this point, the `Person` object has one strong reference, as does the `MacBook`

    doSomething(with: person)

    // The `person` variable falls out of scope, the sole strong reference to the `Person` 
    // instance will be relieved, and the `Person` object will therefore be deallocated.
}

Perhaps properties references aren't accessed/destroyed because they are part of the object?也许属性引用没有被访问/销毁,因为它们是对象的一部分?

When an object is deallocated, any of its properties that had their own strong references to something else are automatically released, too.当一个对象被释放时,它的任何具有对其他事物的强引用的属性也会自动释放。

func runTasks() {
    var person = Person(name: "Reuben")
    var computer = Macbook(model: "Pro 2020")
    
    // At this point, the `Person` object has one strong reference, as does the `MacBook`

    person.macbook = computer

    // Now the `Macbook` instance has two strong references, the local variable and the `Person`

    doSomething(with: person)

    // When `person` and `computer` variables the local variables fall out of scope
    // at the end of this function, their respective strong references to the `Person` 
    // and the `Macbook` objects will be released. But since there are no more strong
    // references to `Person`, it will be deallocated. But when it is deallocated,
    // its strong reference to `Macbook` will be released automatically, too. So now,
    // the two strong references to that `Macbook` object are now released, too (both
    // the `computer` local variable and the `macbook` property of `Person`), so it will
    // deallocated, too.
}

Now, as you have identified, a strong reference cycle (previously called a “retain cycle”) is when two or more objects are keeping strong references to each other (thus, unless you manually nil one or more of those references, they will end up with lingering strong references to each other and therefore neither will be deallocated until the cycle is broken).现在,正如您所确定的,强引用循环(以前称为“保留循环”)是当两个或多个对象保持对彼此的强引用时(因此,除非您手动将一个或多个这些引用置nil ,否则它们将结束相互之间存在挥之不去的强引用,因此在循环中断之前两者都不会被释放)。

It might seem appealing for the memory management system to identify and break these strong reference cycles for us at runtime, but that is computationally impractical.内存管理系统在运行时为我们识别和打破这些强引用循环似乎很有吸引力,但这在计算上是不切实际的。 It would have to build a memory graph, identifying which objects are referencing each other, and somehow figure out which reference to break.它必须构建一个内存图,识别哪些对象正在相互引用,并以某种方式找出要中断的引用。 There are even scenarios where we might have some short-lived cycle that we absolutely would not want the OS to resolve for us (eg you create a dispatch queue, dispatch some blocks of work, and we rely on the queue to not be released until the dispatched blocks are finished; URLSession is another example).甚至在某些情况下,我们可能有一些我们绝对不希望操作系统为我们解决的短暂周期(例如,您创建了一个调度队列,调度了一些工作块,我们依赖队列直到分派的块已完成; URLSession是另一个示例)。

Fortunately, while this sort of constant memory graph analysis is not feasible at runtime, Xcode does offer a debugging tool to help identify these scenarios, namely, the “debug memory graph” feature .幸运的是,虽然这种常量内存图分析在运行时不可行,但 Xcode 确实提供了一个调试工具来帮助识别这些场景,即“调试内存图” 功能 For more information, see How to debug memory leaks when Leaks instrument does not show them?有关更多信息,请参阅如何在 Leaks 工具未显示内存泄漏时调试内存泄漏? or How can identify strong reference cycles in Swift?或者如何在 Swift 中识别强引用循环? or iOS app with ARC, find who is owner of an object .带有 ARC 的 iOS 应用程序,查找对象的所有者

Fortunately, while we have wonderful diagnostic tools to find these strong reference cycles, preventing them in the first place is very easy, by breaking the cycles with weak or unowned references.幸运的是,虽然我们有很好的诊断工具来找到这些强引用循环,但通过使用weak引用或unowned引用打破循环,首先防止它们很容易。 The result is a highly performant, very simple memory management infrastructure, where cycles are easily avoided with weak / unowned , but we have some excellent debugging tools for identifying issues, when needed.结果是一个高性能、非常简单的内存管理基础设施,其中使用weak / unowned很容易避免循环,但是我们有一些出色的调试工具可以在需要时识别问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM