Xcode Playgrounds中的Swift ARC实验

并介绍自动释放池

ARC代表自动引用计数,Swift将其用于动态内存管理。 我正在遍历《 Swift编程语言(Swift 4) 》中 “自动引用计数”一章,试图在遇到某些矛盾时涵盖所有角度。 至少那是我的第一个想法。

“自动引用计数”一章描述了强大的引用循环,展示了几种可能导致潜在引用的情况,最后,它针对给定情况提出了使用语言构造来避免它们的方法。

在本文中,“ 对象 ”一词用于指代类实例 ,因为“引用计数仅适用于类实例。 结构和枚举是值类型,而不是引用类型,并且不通过引用存储和传递。”

方案1:

对象通过属性相互引用,这两个属性都可以为“ nil`

它建议使用弱引用,以便在删除强引用时,将使用weak关键字声明的属性设置为nil

基类:

 类人{ 
命名:字符串
init(name:String){
打印(“ \(名称)-开始初始化”)
self.name =名称
}
var apartment:公寓?
deinit {print(“ \(name)正在被初始化”)}
}
 公寓类{ 
let单位:字符串
init(单位:字符串){
print(“ \(unit)-开始初始化”)
self.unit =单位
}
弱var租户:人?
deinit {print(“公寓\(单元)正在被初始化”)}
}

创建变量和强引用:

  //注意可选的类型声明! 
var john:人吗?
var unit4A:公寓?
  john = Person(姓名:“ John Appleseed”) 
unit4A =公寓(单位:“ 4A”)

查找john的对象标识符以供以后比较:

 让johnID = ObjectIdentifier(john!)。hashValue 
//> 105553116545632

打破由john创建的强引用将导致在Apartment对象中将tenant属性设置为nil

 约翰=零 

到目前为止,所有内容ObjectIdentifier从本书中复制粘贴( ObjectIdentifier部分除外)。 在这一点上,我想检查tenant是否真的是nil但我得到了这个¹:

  print(“ \(unit4A!.tenant!.name)”) 
//>约翰·Appleseed
  ObjectIdentifier(unit4A!.tenant!)。hashValue == johnID 
//>是

自动释放池

参加了Stackoverflow并输入了我的问题(“ 弱引用对象属性在强引用对应对象被释放后,是否应该设置为nil? ”),所以有很多主题相同。

弱属性未设置为nil(重复)的答案对我最有帮助:

读取弱变量可能导致指向的对象被保留并自动释放。 然后,对象将至少与当前自动释放池保持活动状态。

大多数问题(包括上面的问题)已作为重复项被关闭,并且引用为什么在强引用消失后我的弱引用却不被清除? 答案甚至还有更多技术性的答案,示例在Objective-C中提供,但他们都提到了动词“ autorelease ”和名词“ autorelease pool ”。

可以在Apple的《 高级内存管理编程指南》中找到有关后者的精妙解释:

自动释放池块提供了一种机制,您可以通过该机制放弃对象的所有权,但可以避免将其立即释放的可能性(例如,从方法返回对象时)。 通常,您不需要创建自己的自动释放池块,但是在某些情况下,您必须这样做或者这样做是有益的。

这是一个Objective-C指南,但是“ 将Swift与Cocoa和Objective-C结合使用(Swift 4)”一书展示了如何在Swift中使用自动释放池块(即使很简短,并且解释本身也从上述指南中完全删除了) 。

以上文章的其余部分(包括代码示例) 摘自 Wei Wang的@AUTORELEASEPOOL文章,该文章解释了自动发布是什么:

Swift在内存管理中使用了自动引用计数(ARC)。 尽管以ARC方式禁止手动调用keep, releaseautorelease ,但实际上它们实际上是由编译器自动添加的,并在那里被调用。 retainrelease非常简单。 它们将分别增加和减少参考计数。 在这个级别上, autorelease非常特殊。 接收器将被插入到预先创建的自动释放池中。 当池收到drain方法时,池中所有对象的引用计数将减少它们出现在池中的时间。

在iOS应用中,主要功能在自动发布池中运行。 该池的drain操作将在每个主运行循环结束时进行。 这种延迟释放在开发中是必需的,有时我们希望变量的生存期比其范围长(例如被调用函数),因此我们可以在以后继续使用它。

即使已过时,也值得阅读本文的其余部分。

添加官方(即Apple Developer) autorelease文档以供参考:

在当前自动释放池块的末尾减少接收者的保留计数。

在方案1中应用autorelease

  / *省略基类的声明。  * / 
  var john:人吗? 
var unit4A:公寓?
  john = Person(姓名:“ John Appleseed”) 
unit4A =公寓(单位:“ 4A”)

约翰!。公寓= unit4A
租户=约翰
 约翰=零 
//>约翰·Appleseed正在取消初始化
  print(“ \(unit4A!.tenant)”) 
//>无

删除对Apartment对象的强引用

最后,我从一开始就打算做:将unit4A设置为nil ,看看会发生什么:

  //约翰=零 
unit4A =无
  //注意,`unit4A`并未被初始化,因为 
//`john`仍然对此有很强的参考!
约翰!。公寓!。单位
//>“ 4A”