如何使用自动引用计数 (ARC)?
How to work with Automatic Reference Counting (ARC)?
在 Swift 中,我们主要使用 类 的许多 引用 ,例如,
- UITableView
- UIStepper
- UILabel
- NSTimer
- 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]
最后,我覆盖了 Something
的 init
和 deinit
方法,因此您可以跟踪相关实例的分配和释放。查看日志(或使用断点)可以验证我在这里所说的。
希望对您有所帮助。
ARC 基本上可以正常工作。您通常不必担心在使用 Cocoa class 时声明 weak,例如您列出的那些。
只有当您自己开发了具有双向引用的不同 classes 时才需要考虑它。 (公寓 class 和人 class 在 Apple's example)。
在 Swift 中,我们主要使用 类 的许多 引用 ,例如,
- UITableView
- UIStepper
- UILabel
- NSTimer
- 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]
最后,我覆盖了 Something
的 init
和 deinit
方法,因此您可以跟踪相关实例的分配和释放。查看日志(或使用断点)可以验证我在这里所说的。
希望对您有所帮助。
ARC 基本上可以正常工作。您通常不必担心在使用 Cocoa class 时声明 weak,例如您列出的那些。
只有当您自己开发了具有双向引用的不同 classes 时才需要考虑它。 (公寓 class 和人 class 在 Apple's example)。