ARC 以及它是如何工作的。

Posted

技术标签:

【中文标题】ARC 以及它是如何工作的。【英文标题】:ARC and how it works exactly. 【发布时间】:2016-08-25 23:50:38 【问题描述】:

我刚刚学习了有关 ARC 的教程,并获得了此代码。

下面的 ViewController 类和下面的 Vehicle 类。

我从中得到的是,ARC 本质上是跟踪一个实例化的类并为它分配一块内存。当创建实例的“强”引用时,arc 会增加对实例的引用数量的增量。一旦所有这些都设置为 nil,ARC 就会从内存中释放实例。讲师还说了一些类似的话,一旦所有引用都没有被使用,它就会从内存中释放。我不太明白它们没有被“使用”的部分,所以我决定添加一个按钮,它显示另一个没有代码的空白视图控制器。我想如果我导航到下一个视图控制器,deinit 将作为视图控制器 1 中的引用被调用,现在没有被使用,因此从内存中释放。情况并非如此,并且 deinit 没有被调用。因此,我想知道,除非您将它们设置为 nil,否则引用会一直保留在内存中吗?

问题的第 2 部分:此外,当您回答这个问题时,我还有另一个问题,我还想知道 ARC 是否仅适用于类实例和对它的引用,因为我查阅了每篇文档或教程似乎只提到类实例。例如,如果我设置 var number = 2 var othernumber = number ,“数字”是否也存储在内存中,并且只有在所有对它的引用都为零时才被释放。如果也是这种情况,那么同样的问题也适用,将所有引用设置为 nil 是从内存中释放的唯一方法吗?很抱歉这个冗长的问题,但我对记忆概念很陌生。

import UIKit

class ViewController: UIViewController 


var ref1: Vehicle?
var reference2: Vehicle?
var ref3: Vehicle?
var timer: NSTimer!
var count = 0
override func viewDidLoad() 
    super.viewDidLoad()

    ref1 = Vehicle(kind: "Car")
    reference2 = ref1
    ref3 = ref1

    timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(tick), userInfo: nil, repeats: true)





func tick() 
count++

    if count >= 3 
        ref3 = nil
        reference2 = nil



    

    if count == 5 
    ref1 = nil


    



   class Vehicle 

let type: String


init(kind: String)
self.type = kind
print("\(type) is being initialized")
//when the class is instantiated, we get an initialization message. When class is deallocated, we get a deinit message. As in, all strong references are gone, we can deinitialize.



deinit 
//class vehicle not in memory anymore as all strong references to it have been destroyed. This will be tested with segue as well. 
    print("\(type) is being deinitialized")


【问题讨论】:

【参考方案1】:

    “使用”的术语令人困惑/误导(或者,充其量是不精确的)。使用 ARC,直到没有剩余的强引用,对象才会被释放,简单明了。如果您 nil 所有这些强引用,或者这些强引用超出范围,则对象将被释放。

    顺便说一下,请注意scheduledTimerWithTimeInterval 建立了自己对其目标的强引用。您必须invalidate 计时器才能解决该强引用。

    ARC 仅适用于引用类型(即class 实例)。它根本不适用于值类型(例如数字类型或struct 类型)。

    因此,考虑

    var number = 2 
    var othernumber = number
    

    othernumber 不引用number。它制作副本。它是一个新对象,其值恰好与number 相同。有关区分 Swift 值类型和引用类型的讨论,请参阅 WWDC 2015 Building Better Apps with Value Types。 (顺便说一句,复杂值类型的幕后内存管理实际上比简单值类型更复杂,但在本次对话中并不真正相关。但如果您是有兴趣。)

【讨论】:

感谢 Rob 澄清这一点,尽管在什么情况下强引用会“超出范围” 同样,我猜值类型,它们也被分配到内存中并被释放。程序员是否必须自己解除分配才能保留内存空间? 这提醒我,当我提出一个新的视图控制器时,它不包含任何内容,从理论上讲,引用不应该超出范围吗?在前一个视图控制器中的那些 让我们continue this discussion in chat.【参考方案2】:

这是一个大问题。

要了解 ARC,您确实需要了解手动引用计数。

引用计数是一种跟踪哪些对象仍在使用中以及哪些对象可以被释放的方法。

在手动引用计数中,对象具有保留计数。

您向对象发送保留消息以增加其保留计数,并释放以减少其保留计数。如果向对象发送释放消息导致其保留计数降至 0,则该对象被释放/释放。

还有一个autorelease 消息将对象添加到“自动释放池”。每次您的代码返回并访问事件循环时,自动释放池中的所有对象每次在自动释放池中时都会收到一条释放消息。 (您可以向一个对象发送多个自动释放消息,但忽略它。)如果您不对它们做任何特殊处理,自动释放对于返回会消失的临时对象很有用。一个自动释放的对象在当前调用链中一直存在,但如果没有人保留它,则在您的代码返回时被释放。

对象被创建并返回给所有者,引用计数为 1,所有者负责在对象完成后向对象发送释放消息。

在手动引用计数中,您必须在代码中的正确位置放置保留、释放和自动释放调用,以表达您的内存管理意图。弄错会导致内存泄漏或崩溃。

ARC 使用上述所有机制,但编译器会分析您的代码并为您插入保留、释放和自动释放调用。它还将多余的保留/释放调用去除到所需的最低限度。

在 ARC 中,您只需将变量声明为强变量或弱变量,编译器会完成其余的工作。仍然有一些陷阱,但在大多数情况下,您不必担心内存管理。

将对象存储在强变量中会导致编译器生成保留调用。将强变量清零会导致编译器向对象发送释放消息。

与垃圾收集不同,系统不必停止并执行耗时的内存清理过程。一旦不再有任何强引用对象就会被释放。

【讨论】:

以上是关于ARC 以及它是如何工作的。的主要内容,如果未能解决你的问题,请参考以下文章

Hashgraph,它是啥以及它是如何工作的?

Laravel 应用程序密钥 - 它是啥以及它是如何工作的?

如何在 Fragment 中调用 OnActivityResult 以及它是如何工作的?

Threadpool 如何重用线程以及它是如何工作的

setSoTimeout 的功能是啥以及它是如何工作的?

什么是 [cmdletbinding()] 以及它是如何工作的?