自动引用计数(ARC)(速记3)

内存管理是开发应用程序时的关键因素。 如果程序使用大量内存,它可能会使您的设备停滞不前,从而使应用运行缓慢甚至崩溃。 幸运的是,作为一个快速的开发人员,您可以依靠自动引用计数(ARC)来使您的应用程序的内存使用降至最低。 这并不意味着您可以忘记应用程序中的内存,但是它确实可以为您处理大多数事情。

重要的是要注意,ARC仅在使用类时才起作用。 结构和枚举是值类型,因此与通过引用存储和传递的类相比,它们在内存中的存储方式有所不同。

他们以ARC方式工作

创建类的实例时,ARC会处理从内存中分配和删除实例的情况。 ARC知道通过跟踪对该实例的所有其他活动引用,可以从内存中删除该类的实例。 如果没有对该实例的活动引用,则引用计数为零,并且该实例从内存中删除。

例如-让我们创建一个具有名称的dog类,并在初始化和取消初始化时将其打印到控制台。 这样,我们可以跟踪ARC将实例添加到内存中的时间以及何时将其从内存中删除。

 狗类{ 
命名:字符串
  init(name:String){ 
self.name =名称
print(“ \(名称)被初始化”)
}
  deinit { 
print(“ \(名称)我们正在被初始化”)
}
}

为了演示的目的,我们将使用可选值,以便我们可以将它们设置为nil并查看是否调用了deinit。 让我们创建一个可选的Dog,然后在游乐场文件中将其值设置为nil。

在这种情况下,当我们使用名称“ Guts”创建Dog实例时,该实例对其有一个引用-变量guts。 当该变量设置为nil时,引用计数将变为零,并且名称为“ Guts”的Dog将被取消初始化。

让我们看一个示例,其中对同一实例有多个引用。

在此示例中,“ guts2”现在还引用了名为“ Guts”的Dog的实例。 请记住,“ guts2”未指向变量“ guts”,而是指向了guts指向的内存中相同的位置—这是名为Guts的Dog的实例。

现在,有两个变量引用内存中的同一实例,这意味着引用计数=2。Dog名称“ Guts”的实例未取消初始化,因为我们仅将其中一个引用设置为nil。 这意味着引用计数仍为1,我们还需要将“ guts2”设置为nil才能从内存中删除实例。

强弱

当我们谈论某个属性强弱时,它与ARC特别相关。 默认情况下,创建属性时,它将创建对指向其所指向的任何类的实例的强引用。 使用ARC,引用计数仅计算强引用。 因此,如果我们将一个属性标记为弱属性,它将不会增加引用计数,或者如果该实例是对该实例的唯一活动引用,则不会将该实例保留在内存中。

在此示例中,实例的引用计数仅为1,因为guts2现在是弱引用。 因此,我们只需要设置guts = nil即可使ARC取消初始化名为“ Guts”的Dog的实例。

从内存中删除了名为“ guts”的Dog实例后,guts2将不再能够引用它,而成为nil值。

为什么要使用弱…? 参考周期。

使用ARC时,可能会创建称为强参考周期的东西。 当两个对象是彼此有效的引用,使引用计数不可能等于零时,就会发生这种情况。

当存在强参考周期时,ARC无法识别该周期中捕获的实例需要从内存中释放。 这将导致应用程序内存泄漏。

为了给您展示一个示例,让我们创建一个新的Owner类并将Owner添加到Dog类。

 类所有者{ 
命名:字符串
var dog:狗? =无
  init(name:String){ 
self.name =名称
  print(“ \(名称)正在初始化”) 
}
  deinit { 
print(“ \(名称)正在被初始化”)
}
}
 狗类{ 
命名:字符串
var owner:所有者? =无
  init(name:String){ 
self.name =名称
print(“ \(名称)被初始化”)
}
  deinit { 
print(“ \(名称)我们正在被初始化”)
}
}

让我们进入操场,看看当我们给所有者实例“狗”和“狗所有者”时会发生什么。

即使胆量和道格都设置为nil,也不会从内存中释放它们。 让我们看看是否可以对每个引用进行计数,以了解原因。

Dog Guts和Owner Doug的第一个参考是变量“ guts”和“ doug”,所以每个都是。

当我们设置guts.owner = doug时,所有者Doug现在有了第二个强引用。 设置doug.dog = guts的情况也一样,将Dog Guts增加到两个强引用。

请记住,这些变量都指向内存中的同一位置,而不是彼此指向。 这是考虑类的引用语义时的关键。

现在,这两个实例都具有2个强引用,仅删除了我们创建的用于访问这些实例的变量,这只会将引用计数减少为1,并将它们保留在内存中。 为了增加侮辱性伤害,我们无法再在应用中访问这些实例。 这两个实例最终漂浮在内存中,直到应用停止。

为了防止这种情况发生,我们可以将Dog或Owner类中的属性之一设置为弱,以打破强引用周期。 最好的选择寿命较短的变量是弱变量。 在这种情况下,一个人的狗很可能不会长寿,因此我们可以将其设为弱变量。

 类所有者{ 
命名:字符串
弱var狗:狗? =无
  init(name:String){ 
self.name =名称
  print(“ \(名称)正在初始化”) 
}
  deinit { 
print(“ \(名称)正在被初始化”)
}
}

现在,当我们在操场上运行相同的代码时,我们可以看到实例已从内存中正确删除。

Dog Guts仍然有两个强有力的参考,但所有者Doug只有一个。 因此,当变量doug设置为nil时,将从内存中删除所有者Doug。 一旦变量guts设置为nil,Dog Gut的两个强引用都不存在,并且ARC从内存中将其删除。

也可以在闭包中创建强大的参考循环,但它会稍微复杂一些,我将在我的下一篇博客文章中介绍闭包,以及有关闭包的更多信息。

谢谢阅读!!