Swift中的强周期/保持周期
在进入“ 强/保留周期”问题之前,让我们非常简要地了解如何使用ARC管理对象内存。
ARC是XCode为我们提供的用于自动内存管理的编译时功能。 它代表自动参考计数。 简单来说,只有强引用计数变为“ 零 ”时,ARC才会释放对象
根据ARC内存管理,对象可以是带变量的strong, weak or unowned
引用类型之一,将进一步讨论它们,目前,如果对象在let语句之前没有任何关键字,则默认情况下它将具有很强的参考意义。
let name = String() // name variable has a strong reference to instance of String
让我们用代码来理解,
上面的代码片段声明了一个名为Employee
的类,其name, empid & title
Empid name, empid & title
为ivars。在第19
行,我们创建了Employee
类的实例jack
,并传递了所有employee的详细信息,因此jack
变量具有对object的强引用。
根据ARC,具有至少一个强引用的内存管理对象将不会被释放/释放。
在第20
行,我们取消了对对象jack
的引用,该对象又释放或释放了它所引用的对象,这就是在释放对象之前如何调用Employee
类的deinit()
函数,正如我们看到的Employee: Jack removed.
日志Employee: Jack removed.
我们先将参考添加到变量jack
然后再将其取消。
"Where did the deinit log “ Employee: Jack removed ” go ?"
我们让jack = nil
对吗? 为什么没有记录? 既然是ARC内存管理,即使我们将其设置为nil,也不会取消分配jack
对象,因为第20
行的变量jackReference
具有对jack
所指向实例的强引用,因此对象的强引用计数为jack
是指向也不是零,因此不会被释放或释放,如果我们使jackReference = nil
那么我们将看到deinit Employee: Jack removed.
的日志Employee: Jack removed.
那是验证简短的ARC简介。
现在遇到实际问题, 强/保持周期
众所周知,每次默认声明一个类属性时,除非指定某些内容,否则它都会强烈指向它所指向的对象,那么何时会出现一个强循环?
案例中ObjectA与ObjectB有很强的关联性,而ObjectB反过来又与ObjectA有很强的关联性
让我们像往常一样用代码来理解
让我们利用Employee
类本身,让我们再声明一类MacBook
这就是类的外观, Employee
具有4个ivars或具有强引用name, empid, title & macBook
的属性,因此前3个属性的类型为String, macBook
的类型为MacBook
,在代码段的Employee
类下定义,这与MacBook
具有2个属性serialNumber & assignee
方式相同,因为我们可以看到属性assignee
的类型为Employee.
现在让我们看看强周期是如何发生的
什么!! 强周期在哪里? 我创建了实例employee
第33行,在第34行创建了mac
的实例,分配了employee?.macBook = mac
然后我将employee
和mac
都设置为零,正如我在日志中看到的那样,这两个都已删除,因此强周期是?
实际上,还没有macBook
强大的循环,在employee
我们将属性macBook
指向一个实例mac
,该实例与mac,
有很强的引用关系mac,
但是正如我们在MacBook
类中看到的那样,我们有一个名为assinee
属性,尚未设置为employee
,让我们在删除employee and mac.
之前添加mac.assinee = employee
employee and mac.
为什么现在不打印日志? 我们既使employee and mac
nil,所以呢? 所以在这里我们犯下了在employee
和mac
之间建立强大的参考周期的罪行
-
employee
对象对指向mac
属性macBook
有很强的引用 -
mac
对象具有属性assinee
并具有较强的参考指向employee
因此,它们都变成了一个保留周期,这意味着employee
将被mac
保留,而mac
将被employee
保留,因此他们都不会被释放或释放。
我们该如何解决呢? 救世主来了 参考
弱引用是指不会对其引用的实例保持强大控制的引用,因此不会阻止ARC取消分配所引用的实例。 此行为可防止参考成为强大参考周期的一部分。 您可以通过在属性或变量声明之前放置weak
关键字来指示弱引用。
这就是苹果文档所说的弱引用,让我们尝试用弱关键字打破上述保留周期。
让employee
对macBook
仍然有很强的参考macBook
我们只需在var之前添加关键字strong macBook
使MacBook
的assinee
属性变弱,如下所示。
你有注意到吗? 是的,以前有一个关键字weak
, var assinee:Employee
,现在您可以看到已在控制台中确认已重新分配了employee
和macbook
对象/实例,并记录了Employee : Jack removed, & Mackbook : JHSF8S32 removed
。
因此,我们确实使用weak
引用类型打破了保留周期。
那么unowned
呢? 什么时候使用?
一个unowned
引用不会对其引用的实例保持强大的控制力。
那句话似乎重复了吗? 难道不是我们读的同一个定义吗? 是的 ! 像弱引用一样,当变量或属性之前使用unowned
关键字声明时,它不持有指向它所指向的对象的强引用。
因此,您要说的是,我们也可以通过不使用任何引用来打破上述Employee
和MacBook
保留周期吗? 是!!
那为什么我们需要unowned
关键字呢? 应该有一些条件,我们应该使用unowned
而不是weak
,反之亦然,让我们看看苹果医生告诉了我什么。
“ 与弱引用一样,无主引用也不能对其引用的实例保持强大的控制力。 但是,当另一个实例具有相同的生存期或更长的生存期时,将使用无主引用。 ”
让我们稍微调整一下以上陈述,以供我们理解
“弱 引用用于在其生命周期中某个时刻可能变为零的地方。 如果 在自对象存在之前,该引用在任何时候都不可能变为零的情况, 则使用 未 拥有的 引用 ”
似乎仍然很困惑,如果可以的话,让我们通过代码示例进一步解决该问题。
您可以通过将unowned
关键字放在属性或变量声明之前来指示无主引用。
让我们使用我们以前的Employee
类,再说一个类,即ICard
,每位员工将拥有一个iCard,而每个iCard都指向一个员工
现在是我们的员工类,它具有3个属性, name, title & iCard
, iCard
是ICard
类类型,其定义如下所示
如我们所料, employee
之间存在保留周期,并且icard
Employee类的iCard
变量强烈引用了ICard,而ICard的employee
变量同样强烈引用了Employee。
我们知道打破保留周期的技巧可以放手并在ICard类的employee变量之前添加weak
关键字
哦! 似乎我的编译器对此不满意,它说'weak' variable should have optional type 'Employee?'
哦,这是个好建议,但是我不能通过添加?将ICard类中的employee变量设为optional
变量。 ,因为ICard类仅在具有有效雇员的情况下才有效,因此,每个ICard实例都应具有与之关联的有效雇员,因此,将其设置为可选不是解决方案。
好的,我们使用unowned
key,正如我们之前阅读的,这也用于打破保留周期。
好的,我们让我们使用unowned
key,正如我们之前阅读的,这也用于打破保留周期。
哇,我的编译器很高兴,这次没有对我大喊大叫,看到第71行我创建了Employee jack
的实例,第73行我创建了以jack
作为参数传递的ICard实例,第74行我分配了jack.iCard = iCard
,并且我将这两个对象都修剪了,没有任何意外或保留周期,正如我们从日志中看到的那样,Employee和ICard已被删除
什么时候使用无所有权? 还是弱?
回到我们前面的示例Employee
和MacBook
,为什么我们不使用assinee
作为未拥有code : Employe and MacBook Definition
,在code : Employe and MacBook Definition
因为在这种用例中,当jack
拿新笔记本电脑时,受让人有可能变成零。 ,则MacBook的受让人变量将一直为nil,直到它指向它所指派的新雇员为止。
因此,这不是唯一的保留/强循环问题,它在类实例之间不会发生,有可能在使用闭包时创建一个保留周期。如果不确定什么是闭包,我建议先阅读闭包再继续。
让我们创建PhotosViewController
负责显示照片的PhotosViewController
,再创建一个负责将照片发送到我们的PhotosViewController
那就是我们的PhotosViewModel
在顶部,我们声明了一个封闭类型DidGetPhotos
,它接受一个参数photos
,该参数是数组。如前所述,该类负责通过didGetPhotos
封闭将照片PhotosViewController
到didGetPhotos
。
现在让我们看看我们的PhotosViewController
让我们逐行阅读PhotsViewController
在第102行中,我们创建声明并初始化类型为PhotosViewModel
的photosViewModel
对象,从而使负责获取照片的对象
在第104行中,我们定义了一个loadPhotos()
函数,在其中我们将didGetPhotos
闭包分配给了photosViewModel
,因此这是我们从ViewModel中获取所有照片的回调函数的地方。 在闭包内部,我们调用函数updatePhotosInView()
来显示photosViewModel
给定的照片
这有保留周期吗? 让我们像往常一样检查它,让我们创建一个PhotosViewContoller
实例,然后将其取消,确认它记录在deinit()内,释放或释放。
我们的deinit()日志在哪里? 我们创建了一个名为loadPhotos()的实例,并将photosViewController设置为nil,理想情况下,它应该已经释放了吗? 在找到答案之前,让我们先阅读以下有关闭包的事实
在快速访问闭包内部的自身时,使闭包对自身有很强的参考作用。
这是否意味着如果在闭包内部使用闭包,它将保留自身? 是
让我们想象一下
哦,是的!,似乎有保留周期, photoViewController
强烈引用了photosViewModel
, photosViewModel
强烈引用了didGetPhotos()
闭包,我们在第106行的loadPhotos()
函数中访问了闭包内部的self。
让我们用之前学到的技巧(弱,无主)来打破它,这样就可以继续使didGetPhotos属性变弱吗?,不,您不能,编译器会对我大喊大叫,我们可以在闭包内部做些什么?
是的,一点没错!! ,我们必须打破封闭的周期,因为我们知道封闭捕获了引用权限,因此我们需要告诉PhotoViewController
内部的didGetPhotos
封闭以捕获弱引用自身。
现在让我们检查代码的外观吗?
哦,是的,您似乎去了photoViewContoller
,因为您可以看到日志Deinited the PhotosViewController,
我们该怎么做?
观察行号105,我们通过在闭包参数之前提供[weak self]
捕获自我作为弱引用,如您所见,我们将其作为数组传递,我的意思是我们将那些捕捉到的值传递给数组[],这意味着我们可以捕捉更多封闭中的一个参考。
这就是我关于保持/强力循环的全部内容,如果有任何疑问,请随时发表评论,希望下次会出现一些新问题和新技术。