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

3
在Swift中,我们主要使用许多类的引用,如:
  1. UITableView
  2. UIStepper
  3. UILabel
  4. NSTimer
  5. UISlider等等。
例如:

var slider : UISlider!

我的问题是是否需要将这些全部创建为弱引用,通过在前面加上"weak"来标记,这样ARC就不会对其有强引用,因此ARC可以根据需要删除它;或者只需创建一个强引用,在"viewDidUnload"委托中将其设为"nil" ?
例如:

slider = nil

我实际上不知道如何手动使用ARC,或者根本不需要手动处理ARC?我对这个内存处理没有任何头绪。
如果您曾经遇到过这个问题并找到了解决方案,请分享一下...
感谢您的帮助。

如果一个对象不再有强引用指向它,ARC会将其释放。你无需将滑块设置为nil,因为当视图控制器和视图被释放时,这将自动发生。请参见https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html。 - Aaron Brager
强烈推荐这篇关于ARC的文章。虽然它是用Obj-C编写的,而不是Swift,但由于Swift仍然使用Cocoa并在Obj-C运行时上运行,因此它完全相关。该文章深入探讨了编译器如何分析您的代码并将内存管理插入其中。它是一篇非常好的阅读材料。 - justinpawela
3个回答

3
在Swift中,ARC(自动引用计数)的概念在官方文档中已经很清楚地解释了。下面是4个重要方面的简要总结:

1. 基础知识

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

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

现在,由于其retainCount值为1,我们的Something实例被保留在内存中。
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
}

你可以把这个看作多米诺骨牌效应。当一个实例被释放时,它对其他实例的所有引用都会被删除。因此,被引用实例的保留计数将减少。如果变为0,它们就会被释放。依此类推...
3. 保留循环
好的,如果我有两个如下类会发生什么?
class Son {
    let father:Father
    init(father:Father) {
        self.father = father
    }
}

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

现在让我们创建一个从父亲到儿子的引用,以及从儿子到父亲的相反引用。请保留fatherson标签,不要进行解释。
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 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实例。
现在,UINavigationController引用MyCustomViewController实例,因此retainCount=1。 类似地,Something实例由MyCustomViewController实例引用,因此它的retainCount=1
所以,UINavigationController实例“保持”MyCustomViewController实例存在。而MyCustomViewController实例又“保持”Something实例存在。
接下来,您决定解除MyCustomViewController。因此,iOS动画使其离开屏幕。当它不再可见时,从UINavigationController实例到MyCustomViewController实例的引用将被删除。
这意味着MyCustomViewController实例的retainCount变为0,因为:现在没有人引用它!
因此,MyCustomViewController实例将从内存中删除。在此过程中,它的属性被设置为null。
现在,Something实例的retainCount已变为0。因此,它也将从内存中删除。
最后,我重写了Somethinginitdeinit方法,以便您可以跟踪相关实例的分配和释放。查看日志(或使用断点),您可以验证我在这里所说的。
希望这有所帮助。

感谢您宝贵的答案...但在您的第二点中,您说当函数中的引用自动变为“nil”时,我们不需要明确给出“nil”。我的问题是,在类内(例如:UIviewcontroller类)创建的引用也会在视图控制器消失后死亡(变为“nil”)吗?我想问的是,类内(而不是函数内)引用的作用域什么时候会死亡?是否在我们关闭该视图控制器后就会死亡? - Legolas
1
简短的回答是是的。当一个UIViewController被解散(然后被释放)时,仅从它引用的实例将被释放。我将添加一个新段落来更好地详细说明这一点。 - Luca Angeletti
那么仅仅调用“dismissViewControllerAnimated”就不会dealloc UIViewController吗?正如您在上面的语句中提到的-(然后被解除分配)...如果是这种情况,那么释放UIviewcontroller的方法是什么?是否有任何方法? - Legolas
1
调用 dismissViewControllerAnimated 就足够了。在调用后,UIViewController 将被释放。如果没有被释放,那么就意味着还有另一个强引用指向了这个 UIViewController - Luca Angeletti
请查看我在答案中添加的新段落,以获取更多详细信息。 - Luca Angeletti

0

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

大多数情况下,你不需要手动添加weak到你的属性中,除非你在代码的其他地方定义了一个强关系(当你有代理时,这是典型的情况)。


0

ARC大多数情况下都能正常工作。当使用Cocoa类(如您列出的那些类)时,通常无需担心声明弱引用。

只有在您开发的不同类之间存在双向引用时才需要考虑它。(例如Apple示例中的公寓类和人员类(链接1))。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接