我已经在Swift
编程了几个月了。最近,我更多地关注Swift
作为一种语言如何运作的概念。
因此,最近在阅读apple documentation on Automatic Reference Counting(ARC)时,我遇到了以下几行:
这一个在上面:
在大多数情况下,这意味着内存管理在Swift中“正常工作”,您不需要自己考虑内存管理。当不再需要这些实例时,ARC会自动释放类实例使用的内存。
在下一段中,以下内容:
为了实现这一点,无论何时将类实例分配给属性,常量或变量,该属性,常量或变量都会对实例进行强引用。该引用被称为“强”引用,因为它保持对该实例的坚定保留,并且只要该强引用仍然存在就不允许它被释放。
我对这种情况的动态有点困惑。我在使用故事板时注意到,你将引用设置为弱,因此类看起来像这样,我也称之为案例1:
案例1
class SomeClass : UIViewController {
@IBOutlet weak var nameLabel : UILabel!
override func viewDidLoad() {
nameLabel.text = "something."
}
}
这里,标签与ViewController有一对一的弱引用,一旦Controller被更改,引用就会被破坏(内存dealloc),因为它很弱。因此,没有与记忆有关的问题。
如果上述陈述错误或松散,请原谅我。如果有人确认我的假设或反击它,我会很高兴。
我的问题是关于第二种情况,但我不使用故事板和类如下所示:
案例2
class SomeClass : UIViewController {
var nameLabel : UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
view.addSubView(nameLabel)
// view.addConstraints...
}
}
对于上面的情况,我的假设是ViewController与标签有一对一的强引用,ViewController中的视图也有标签的强引用..如果类被更改/标签被删除subview ..然后我认为内存不会被释放。或者至少视图控制器将保持对标签的强引用(根据文档。)
我通过从视图的子视图中删除标签并打印出标签来确认这一点(它给了我一个UILabel的实例,其框架位于0原点和0大小。)因此一个实例不是零。
我唯一可以从中收集的是,尽管标签已从UIView中删除,但它仍然保持与控制器的强引用,因此在内存中保持永久状态。我是对的吗?
如果是这种情况。我应该如何防止我的代码出现此类内存问题?更大的问题是,如果我像这样声明我的变量,那么在将它添加为控制器中主视图的子视图时,我会得到一个零。
weak var nameLabel : UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
如果在第二种情况下声明变量会导致永久强引用,那么我应该如何声明这些变量而不会出现内存问题?
总而言之,我的问题是:
如果没有使用故事板出口,并且变量被视图控制器强烈引用,这些引用是否会导致内存问题?
如果是这样,我必须遵循code declaration practice
?
如果不是这样,请提供深思熟虑的论据以及有效的解释来解决这个问题。
再次,如果我在任何地方不正确,请原谅我。
提前谢谢。
答案 0 :(得分:11)
我唯一可以从中收集的是,尽管标签已从UIView中删除,但它仍然保持与控制器的强引用,因此在内存中保持永久状态。我是对的吗?
没有。这里没有大问题。
标签没有对视图控制器的强引用 - 如果是, 将是保留周期,并且会导致标签和视图控制器泄漏。出于这个原因,视图永远不会保持对其视图控制器的强引用。
然而,在这里,反过来说:视图控制器对标签有强烈的引用。没关系。确切地说,标签在从超级视图中删除后仍然存在。但那可能并不坏。在许多情况下,它很好!例如,假设您打算稍后将标签放回界面;你将需要保留它。
如果您确定以后不需要保留标签,那么只需使用Optional包装UILabel作为您的实例属性即可。这样,您可以在完成标签实例属性后将nil
分配给标签实例属性,标签将不再存在。
但无论如何,没有泄漏在这里,你应该停止担心。当视图控制器不存在时,标签也将不存在。这个标签的寿命比以往任何时候都要长,但对于大规模的事情来说,这个标签很小而且不重要。
答案 1 :(得分:1)
在您需要时创建label
,然后致电addsubView
对其进行强引用,并对您的成员var进行弱引用,如下所示:
class ViewController: UIViewController {
weak var label : UILabel?
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
view.addSubview(label)
self.label = label
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
print(label)
//click first Optional(<UILabel: 0x7fb562c3f260; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fb562c11c70>>)
//click second nil
label?.removeFromSuperview()
}
}
在viewcontroller
版本发布时,标签将会发布,view.subview
也会发布。
我写了一个简单的演示,让ViewControllerTest
成为rootviewcontroller
class Test{
weak var label:UILabel?
static let instance = Test()
}
class ViewControllerTest: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let item = UIBarButtonItem(title: "Test", style: .Plain, target: self, action: #selector(self.test))
self.navigationItem.rightBarButtonItem = item
}
func test(){
print(Test.instance.label)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let vc = ViewController()
self.navigationController?.pushViewController(vc, animated: true)
print(vc.nameLabel)
let test = Test.instance
test.label = vc.nameLabel
}
}
class ViewController: UIViewController {
var nameLabel : UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.whiteColor()
view.addSubview(nameLabel)
let item = UIBarButtonItem(title: "Test", style: .Plain, target: self, action: #selector(self.test))
self.navigationItem.rightBarButtonItem = item
}
func test(){
print(Test.instance.label)
}
}
答案 2 :(得分:1)
我不认为强烈引用变量来查看控制器会导致任何内存问题。
通常在取消分配视图控制器之前会取消分配视图。例如,在你的代码中,当取消分配视图时,ARC减少指向namelabel的计数器,因此它从2传递到1.然后,当取消分配视图控制器时,它再次减少计数器,从1到0。 0个引用指向namelabel的删除。
答案 3 :(得分:1)
弱参考是没有强力保留的参考 它引用的实例,因此不会阻止ARC处置 引用的实例。此行为会阻止引用 成为强大的参考周期的一部分。你表示弱势 通过在属性或变量之前放置weak关键字来引用 声明
&GT;必须将弱引用声明为变量,以指示它们的变量 值可以在运行时更改。弱引用不能声明为a 恒定。
因为弱引用不能保持对实例的强烈保持 它指的是,该实例可以在释放时释放 弱引用仍指它。因此, ARC 当实例表示它时,自动设置一个弱引用为nil 是指解除分配。因为弱引用需要允许nil为 他们的价值,他们总是有一个可选的类型。你可以检查一下 弱参考中存在一个值,就像任何其他参数一样 可选值,你永远不会得到一个引用 无效的实例不再存在
来源:Apple docs
弱引用只是一个指向对象的指针,该对象不保护对象不被ARC释放。虽然强引用会将对象的保留计数增加1,但弱引用则不会。此外,弱引用在成功解除分配时将指针归零。这可以确保在访问弱引用时,它将是有效对象,或者为nil。
希望可以帮助您更好地理解弱引用,无论是与故事板项目相关还是以编程方式创建。
答案 4 :(得分:1)
我总是这样向学生解释。
通过强大的参考,您可以看到一个值,并且您有一个围绕它的套索。你对价值是否仍然存在有发言权。
弱引用,你可以看到它,但没有套索。你对价值是否存在没有发言权。
答案 5 :(得分:1)
为您的情况避免发生内存泄漏一秒钟。你可以跟Matt一起回答。
为了更好地理解,请在构建阶段中使用MRC标记创建自定义UILabel类 - &gt; Complie sources。
在自定义类中,覆盖retain和release方法。在它们上面加点断点。
在视图控制器中使用该自定义UILabel类,并将ARC标志设置为ON。使用哑光答案或使用UILabel的可选声明。
import UIKit
class ViewController: UIViewController {
var label:UILabel? = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "something"
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.label!)
//namelabel goes out of scope when method exists.
//self.view has 1+ ref of self.label
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.label?.removeFromSuperview()//-1 ref of self.label
self.label = nil
print(self.label)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
您将清楚地了解ARC如何工作以及为什么UILabel的弱引用会在添加到UIView时导致崩溃。