如何使用自动引用计数 (ARC)?

How to work with Automatic Reference Counting (ARC)?

在 Swift 中,我们主要使用 类 的许多 引用 ,例如,

  1. UITableView
  2. UIStepper
  3. UILabel
  4. NSTimer
  5. UISlider 等

一个例子:

var slider : UISlider!

我的问题是我们是否必须通过前缀将所有这些创建为引用,这样 ARC 就不会对它有很强的控制力,所以 ARC 可以在需要的时候删除它,或者只是创建一个 strong reference 和在 viewDidUnload 委托中达到 nil ??

示例:

slider = nil

事实上我不知道如何手动使用ARC或根本不需要手动处理ARC?我不知道这个记忆处理

如果您遇到此问题并找到解决方案,请分享...

提前致谢....

如果您谈论的是由视图本身初始化的视图控制器属性(例如 UILabel 或您在 IB 中定义的 UIView 的几乎所有子类),Xcode 将自动将它们分配为 weak,因为视图(而不是视图控制器)已经创建了强引用。

大多数时候您不需要手动将 weak 添加到您的属性,除非您在代码中的其他地方定义了牢固的关系(这是您有委托时的典型情况)。

ARC(Swift 中的 Automatic Reference Counting)official documentation 中解释得很好。

您可以在下面找到对 ARC 的 4 个重要方面的非常简单的回顾。

1.基础知识

ARC 将 Class 的每个实例关联到一个 retainCount 整数。 该值表示对该特定实例的强引用数。 当此数字变为 0 时,实例使用的内存将被释放。

class Something {}
var aVariable = Something()
// now the reference counting for this particular instance of Something is 1

现在我们的 Something 实例保存在内存中,因为它的 retainCount 值为 1

var anotherVariable = aVariable 

现在我们有 2 个对 Something 实例的强引用。好的!它的 retainCount 现在是 2 并且实例仍然保存在内存中!

aVariable = nil

retainCount刚变成1。没问题,实例还在内存中。

anotherVariable = nil

我们实例的retainCount终于变成了0。这意味着该实例已被释放,无法再访问。

2。当你完成变量时,你应该将它们设置为 nil 吗?

不。 事实上,当变量超出范围时,ARC 会自动减少引用实例的 retainCount

在下面的示例中,在最后一个 } 之前,Something 实例的 retainCount 减少并达到值 0(并被释放)。

func doSomethingUseful() {
    let something = Something()
    // ... do very important stuff here
}

所以在常见的情况下,您不需要将变量设置为 nil 以强制 ARC 释放引用的实例。

另一个例子:

class Son {}

class Father {
    var sons: [Son]
    init (sons: [Son]) {
        self.sons = sons
    }
}

func lifeGoesOn() {
    let son = Son()
    // here the referenceCout of the instance of Son is 1
    let father = Father(sons: [son])
    // here the referenceCount of the instance of Son is 2...

    // now variable son goes out of scope so the reatinCount of the instance of Son becomes 1
    // also father goes out of scope, so the variable father.sons goes out of scope as well, so the `retainCount` of the instance of Son becomes 0
    // and so both the instances of Father and Son gets freed
}

你可以看到这就像多米诺骨牌效应。当一个实例被释放时,它对其他实例的所有引用都会被删除。因此,引用实例的 retainCounts 会减少。如果变成 0 他们就会被释放。等等...

3。保留周期

好的,如果我有 2 个 class 会怎样?

class Son {
    let father:Father
    init(father:Father) {
        self.father = father
    }
}

class Father {
    var sons: [Son]
    init (sons: [Son]) {
        self.sons = sons
    }
}

现在让我们创建一个从 father 到它的 son 的引用,反之亦然从 son 到它的 father

func lifeGoesOn() {
    var father = Father(sons:[])
    // retainCount of the instance of Father is 1
    var son = Son(father: father)
    // retainCount of the instance of Father is 2
    // retainCount of the instance of Son is 1
    father.sons.append(son)
    // retainCount of the instance of Father is 2
    // retainCount of the instance of Son is 2

    // Now we have a problem
}

在函数的最后,父变量超出范围,因此 Father 实例的 retainCount 变为 1。 类似地,变量 son 超出范围, Son 实例的 retainCount 变为 1.

这里的问题是 Son 的实例引用了 Father 的实例(将这个实例保存在活动内存中)。 Father 的实例引用了 Son 的实例。这两个实例不应再存在于内存中。程序员无法访问它们,因为引用它们的所有变量都消失了。

这是个问题。

4.弱引用

构建代码时,应注意强保留循环。让我们看看如何重构我们的代码来解决这个问题。

class Son {
    weak var father:Father?
    init(father:Father) {
        self.father = father
    }
}

现在从 Son 到它的 Father 的引用是 weak。这意味着当 ARC 计算对实例的(强)引用的数量时,它不计算在内。这解决了上一段中出现的问题。

我希望现在主题更清楚了。有几种情况我没有涵盖。同样,官方文档非常好且详尽。

更新(以更好地回答下面的评论)

如果你有一个自定义 UIViewController(我们称它为 MyCustomViewController)并且这个 class 对一个对象有很强的 属性 让我们看看会发生什么。

class MyCustomViewController : UIViewController {
    var something = Something()
}

class Something {
    init() { // called when memory is allocated
        debugPrintln("The instance of Something has been created")
    }
    deinit { // called when memory is freed
        debugPrintln("The instance of Something has been freed")
    }
}

当您呈现 MyCustomViewController 时,会创建一个 MyCustomViewController 的实例。然后也创建了一个 Something 的实例。

现在 MyCustomViewController 的实例被 UINavigationController 引用,retaintCount = 1 也是如此。 类似地,Something 的实例被 MyCustomViewController 的实例引用,因此它具有 retainCount = 1.

因此 UINavigationController 的实例保持活动状态 MyCustomViewController 的实例。并且 MyCustomViewController 的实例保持活动 Something 的实例。

UINavigationController -(strong)-> MyCustomViewController -(strong)-> Something

接下来您决定关闭 MyCustomViewController,因此 iOS 将其动画化以离开屏幕。当它不再可见时,它会删除从 UINavigationController 实例到实例 MyCustomViewController 的引用。

UINavigationController -(REMOVED)- MyCustomViewController -(strong)-> Something

这意味着 MyCustomViewController 实例的 retainCount 变为 0 因为:现在没有人在引用它!

因此 MyCustomViewController 的实例将从内存中删除。在此过程中,它的属性为空。

UINavigationController -(REMOVED)- [free memory] -(REMOVED)- Something

现在Something实例的retainCount变成0了。 所以它也会从内存中删除。

UINavigationController -(REMOVED)- [free memory] -(REMOVED)-> [free memory]

最后,我覆盖了 Somethinginitdeinit 方法,因此您可以跟踪相关实例的分配和释放。查看日志(或使用断点)可以验证我在这里所说的。

希望对您有所帮助。

ARC 基本上可以正常工作。您通常不必担心在使用 Cocoa class 时声明 weak,例如您列出的那些。

只有当您自己开发了具有双向引用的不同 classes 时才需要考虑它。 (公寓 class 和人 class 在 Apple's example)。