Swift 弱引用和无主引用之间有什么区别
了解iOS的内存管理对iOS和macOS的开发至关重要。弱引用和非自有引用的概念是很难理解的,特别是对于初学者来说。ARC(自动引用计数)可能已经为我们解决了很多问题。Swift仍然需要你在很多时候管理引用,当你不处理价值类型的时候。
ARC或自动引用计数
自动引用计数(ARC)是用来跟踪和管理应用程序的内存使用的。在大多数情况下,这意味着内存管理在Swift中 “只是工作”,你不需要自己考虑内存管理。当类实例不再需要时,ARC会自动释放这些实例使用的内存。
注意,在Swift中,默认情况下,引用被算作强。
import UIKit
class ProductListController: UIViewController {
// Create a strong reference to a ProductListDataSource() instance. When the ProductListController instance is deallocated, it will decrease the reference count of the ProductListDataSource instance by 1.
let dataSource = ProductListDataSource()
override func viewDidLoad() {
super.viewDidLoad()
}
}
在上面的例子中,ProductListController类有一个ProductListDataSource()类型的强引用。
当一个对象的强引用被分配到该对象时,该对象的引用计数会增加1。当一个对象的强引用被删除时,该对象的引用计数将减少1。
拥有强引用是什么意思?
让我们先来了解一下什么是强引用。它本质上是一个默认的引用(指针和所有),但它本身很特别,因为它通过增加ARC的保留计数来保护被引用的对象不被删除。从本质上讲,只要有一个对象的强引用,它就不会被取消分配。这一点在以后我解释保留周期之类的东西时要记住。
Swift代码库几乎只使用了强引用。所有的属性声明默认都是强引用。一般来说,当对象的层次关系是线性的,使用强引用是允许的。当它们的层次关系从父到子时,依靠强引用通常是明智的。
UIView.animate(withDuration: 0.5) {
self.view.alpha = 0.5
}
由于animateWithDuration是UIView上的一个静态方法,这里的闭包是父类,自我是子类。
弱引用
弱引用是解决Swift中保留循环问题的方法之一。请注意,弱引用不会增加或减少一个对象的引用计数。即使ARC的引用计数大于1,它们也可以被拆解。
基本上,我们在Swift中使用弱引用关键字来标记一个引用为弱引用。另外,弱引用不能用let关键字来声明,因为它在某些时候会被传递给nil。这是因为当弱引用指向对象时,该对象可能会被取消分配。在你认为可能产生保留循环的代码中应该有一个弱引用。
如何使用弱引用来解决保留循环问题
我们将看到一个关于如何修复保留循环的例子。我们将创建一个数据源模型类,使用网络请求获取初始数据。
import UIKit
class ProductListDataSource {
func loadInitialData(_ completion: ((_ isSuccess: Bool) -> ())?) {
// assume perform networking request to fetch initial data
// returning completion handler after finishing the task
completion?(true)
}
}
class ProductListController: UIViewController {
// Created a strong reference to a ProductListDataSource() instance. When the ProductListController instance is deallocated, it will decrease the reference count of the ProductListDataSource instance by 1.
let dataSource = ProductListDataSource()
override func viewDidLoad() {
super.viewDidLoad()
/*
1. Calling the loadInitialData function no longer creates a retain cycle.
2. Self (ProductListController) has a strong reference to the data source.
3. Completion handler has a weak reference to self (ProductListController).
*/
dataSource.loadInitialData { [weak self] isSuccess in
guard let self = self else { return }
// perform tasks that need to be done after successfully fetching data
}
}
}
在上面的例子中,你可以看到我们使用了self的弱引用(ProductListController()),因为一旦从服务器获取数据,可能需要执行某些操作。
Swift中的无主引用
无主引用与弱引用非常相似,但有一点不同。无主引用会给你一个保证,当变量被访问时,它不会为零。
与弱引用类似,无主引用不会增加或减少一个对象的引用计数。我们可以说这是另一种解决保留循环的方法。当引用所指向的实例为nil时,访问一个无主引用将导致一个致命的程序错误。
两者之间有什么区别?
弱引用是一种不会阻止被引用对象被Swift运行时去分配的引用。
另一方面,无主引用是一种不对被引用对象保持强势控制的引用。然而,与弱引用不同,无主引用被认为总是有一个值。
总结一下两者之间的区别 --
弱引用和无主引用之间的区别
弱引用 | 无主引用 |
---|---|
它可以被声明为可选的。 | 它不能被声明为可选的。 |
它可以在任何时候变成nil。 | 它在任何时候都不能成为nil。 |
它防止对象被取消分配。 | 它不会阻止对象被取消分配。 |
如果访问去分配的引用对象,发生运行时错误的机会较少。 | 如果访问去分配的引用对象,发生运行时错误的可能性很大。 |
使用哪种引用是合适的?
使用弱引用
当两个对象之间存在一种关系,其中一个对象不应该对另一个对象有很强的控制力,你应该利用弱引用来阻止保留循环。当两个对象相互强烈引用时,就会产生一个保留循环,禁止Swift运行时对任何一个对象进行解配。
例如,在两个对象之间建立父子关系时,你通常会使用弱引用来作为孩子对父对象的引用,以避免父对象被孩子永久地维护。
使用未拥有的引用
当两个对象之间存在一种关系,其中一个对象对另一个对象没有强大的控制力,但你确信被引用的对象将在引用对象的生命周期内一直存在,你应该使用一个非自有引用。
例如,如果视图控制器不断地展示同一个视图,你可以为视图控制器对视图的引用使用非自有引用,因为只要视图控制器存在,该视图就会一直存在。
结论
总之,这两种引用在各自的情况下都发挥着重要作用。但大多数时候,我们在应用中使用弱引用。这是因为弱引用在内存管理的情况下更好。由于弱引用可以被声明为可选的,并且可以处理nil,所以我们建议使用它们。