ARC 及其工作原理。

ARC and how it works exactly.

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

下面是 ViewController class,下面是一辆车 class。

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

问题的第 2 部分:此外,在您回答该问题时,我还有另一个问题,我还想知道 ARC 是否仅适用于 class 个实例和对它的引用,因为每篇文档或我查过的教程似乎只提到 class 个实例。例如,如果我设置 var number = 2 var othernumber = number , "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. "used" 术语是 confusing/misleading(或者充其量是不精确的)。使用 ARC,对象将不会被释放,直到没有剩余的强引用,简单明了。如果你 nil 所有这些强引用,或者那些强引用超出范围,那就是对象被释放的时候。

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

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

    因此,考虑

    var number = 2 
    var othernumber = number
    

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

这是个大问题。

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

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

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

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

还有一个 autorelease 消息将对象添加到 "autorelease pool"。每次访问您的代码 returns 和事件循环时,自动释放池中的所有对象都会在每次位于自动释放池中时发送一条释放消息。 (您可以向一个对象发送多个自动释放消息,但请忽略它。)自动释放对于返回临时对象很有用,如果您不对它们做任何特殊操作,这些临时对象就会消失。一个自动释放的对象在当前调用链中存在,但是当你的代码 returns 如果没有人保留它时会被释放。

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

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

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

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

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

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