Swift 4.0:自动引用计数(ARC)-第2部分

欢迎回到本教程的自动引用计数第二部分! 在第一部分中 ,您了解了ARC的一些基本概念,例如weakUnowned 以及如何使用weakunowned负责解决参考周期问题。

在最后一部分中,您将了解最后一种情况,在这两种情况下,两个properties都应具有值,并且初始化完成后,两个属性都不应该nil 。 此外,您还将学习如何克服Closures强参考周期 。 让我们直接深入。

假设我们有一个CountryPresident类。 这些类中的每一个都将另一个类的实例存储为变量。 这意味着每个国家都应该有一个总统 ,每个总统都应该与一个国家联系在一起。

为了满足此要求而不会导致内存泄漏,您需要声明一个属性(在我们的示例中为Country类中的countryPresident )作为隐式展开的可选Property 。 这可以通过将感叹号放在其类型注释的末尾( President ! )来完成。 而另一方面,您需要将其声明为无主财产(在我们的country中为President阶级)。

  国家 类别 { 
countryName: 字符串
var countryPresident: 主席
  初始化 (countryName: String ,PresidentName: String ){ 
自我 .countryName = countryName
self .countryPresident = 总统 (presidentName:PresidentName,国家/地区: self
打印 (“国家正在初始化”)
}
  deinit { 
打印 (“国家/地区正在取消初始化”)
}
}
  班长 { 
PresidentName: 字符串
无主 出租国: 国家

初始化 (presidentName: String ,国家/ 地区Country ){
自我 .presidentName = PresidentName
自我。国家=国家
打印 (“正在初始化总统”)
}
  deinit { 
打印 (“总统正在取消初始化”)
}
}

由于countryPresident具有默认的nil值,因此,一旦Country实例在其初始化程序中设置了countryName属性,便认为该新Country实例已完全初始化。 这意味着,一旦设置了countryName属性,Country初始化程序就可以开始引用并传递隐式的self属性。 因此,当Country初始化器设置自己的countryPresident属性时,Country初始化器可以将self作为总统初始化器的参数之一传递。 这样做不会创建任何强大的参考( 图像1-1 )。

 var country: Country ? = Country (countryName: "India", presidentName: "Ram Nath Kovind") 
  //打印“ 总统正在初始化” 
//打印“ 国家正在初始化”

现在,当国家/地区设置为nil ,总统也将自动被取消( 图像1-2 )。

 国家=零 
  //打印“总统正在取消初始化” 
//打印“国家/地区正在取消初始化”

尝试删除unowned 来自总统阶层的country财产。 观察将country设为nil造成的泄漏  

封盖的强大参考周期

如果将闭包分配给类实例的属性,并且该闭包的主体捕获该实例,则也会发生强引用循环。 之所以会发生这种捕获,是因为闭包的主体访问了实例的属性,例如self.someProperty ,或者因为闭包调用了实例上的方法,例如self.someMethod() 。 在任何一种情况下,这些访问都将导致闭包“捕获” self ,从而创建一个强大的参考周期。

之所以会发生这种强烈的引用循环,是因为像类一样,闭包是引用类型。

让我们看看如何引起这种强烈的参考周期。 假设您具有一个具有firstNamelastName属性的Person类。 您还拥有一个lazy闭包属性,可以通过组合firstNamelastName返回全名。 让我们将其命名为fullName ()->String类型的 要了解有关封闭的更多信息,请阅读 苹果文档

Person类提供了一个初始化器,该初始化器带有2个参数( firstNamelastName )。 该类还定义了一个反初始化器,该反初始化器打印一条消息以显示Person实例何时被释放。

    { 
var firstName: String
var lastName: 字符串
  懒惰 var fullName: ()-> String = { 
return (“ \( self .firstName!)\( self .lastName!)”)
}
  初始化 (firstName: String ,lastName: String ){ 
自我 .firstName = firstName
自我 .lastName = lastName
打印 (“人员班级正在初始化”)
}
  deinit { 
打印 (“正在取消初始化人员类别”)
}
}

上面的person变量定义为可选的Person ,因此可以将其设置为nil以演示强引用循环的存在。

Person实例的fullName属性对其关闭有很强的引用。 另外,由于闭包在其主体内引用self (作为引用self.firstNameself.lastName ),因此闭包捕获了self 。 这意味着它还强烈引用了Person实例。 结果,在两者之间创建了强大的参考周期( 图像2-1 )。

即使闭包多次引用self ,Swift仍确保仅捕获对Person实例的一个强引用。

  var person: 人员 ?  = 人员名字: “ Rahul”, 姓氏: “ Singh”) 
//打印“正在初始化人员班”
  打印 (人! 。fullName ()) 
//打印“ Rahul Singh”

将person变量设置为nil时,不会记录任何消息。 这表明Person实例及其关闭都没有被释放。 这是因为在变量和闭包之间创建了“ 强引用循环”图2–2 )。

 人=零 

如何解决类与闭包之间的强引用?

为了解决这个问题,Swift有一个优雅的方法称为Closure Capture List 。 您可以将捕获列表定义为闭包定义的一部分。 捕获列表定义在捕获封闭体内的一个或多个引用类型时要使用的规则。 解决两个类实例之间的强引用循环的方式,即,您声明每个捕获的引用为weak引用或不带所有权的引用,同样,您可以根据代码不同部分之间的关​​系选择这些引用。

捕获列表中的每个项目都是对weakunowned关键字的配对,这些关键字与对类实例的引用(例如self )或用某个值初始化的变量(例如delegate = self.delegate! )配对。 这些配对写在一对方括号内,并用逗号分隔。

除了在fullName闭包内添加捕获列表图2–3)之外, Person Class的实现与以前的实现相同。 捕获列表[unowned self] ,这意味着“将自身捕获为无主引用而不是强引用”。

    { 
var firstName: String
var lastName: 字符串

lazy var fullName:()-> String = { [[unown self] in
return (“ \( self .firstName!)\( self .lastName!)”)
}
  初始化 (firstName: String ,lastName: String ){ 
自我 .firstName = firstName
自我 .lastName = lastName
打印 (“人员班级正在初始化”)
}
  deinit { 
打印 (“正在取消初始化人员类别”)
}
}
  var person: 人员 ?  = 人员名字: “ Rahul”, 姓氏: “ Singh”) 
//打印“正在初始化人员班”
  打印 (人! 。fullName ()) 
//打印“ Rahul Singh”

与之前相反,这次将person变量设置为nil将取消分配Person实例并打印所需的消息( 图像3-4 )。

 人=零 
//打印“ Person类正在被初始化

资源: Apple Docs ,当然还有 Internet😄

可以在这里找到Playground文件。 随便玩吧。 现在,请输入我们是否可以使用weak而不是unowned 在人员阶级关闭中? 如果是,如何实现?

希望您喜欢我的“ 自动引用计数”教程。

如果您喜欢这篇文章,请写下您的评论。 我想增加我的读者群。 您可以通过分享此博客文章来帮助我吗?

领英Twitter脸书