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然后我将employeemac都设置为零,正如我在日志中看到的那样,这两个都已删除,因此强周期是?

实际上,还没有macBook强大的循环,在employee我们将属性macBook指向一个实例mac ,该实例与mac,有很强的引用关系mac,但是正如我们在MacBook类中看到的那样,我们有一个名为assinee属性,尚未设置为employee ,让我们在删除employee and mac.之前添加mac.assinee = employee employee and mac.

为什么现在不打印日志? 我们既使employee and mac nil,所以呢? 所以在这里我们犯下了在employeemac之间建立强大的参考周期的罪行

  • employee对象对指向mac属性macBook有很强的引用
  • mac对象具有属性assinee并具有较强的参考指向employee

因此,它们都变成了一个保留周期,这意味着employee将被mac保留,而mac将被employee保留,因此他们都不会被释放或释放。

我们该如何解决呢? 救世主来了 参考

弱引用是指不会对其引用的实例保持强大控制的引用,因此不会阻止ARC取消分配所引用的实例。 此行为可防止参考成为强大参考周期的一部分。 您可以通过在属性或变量声明之前放置weak关键字来指示弱引用。

这就是苹果文档所说的弱引用,让我们尝试用弱关键字打破上述保留周期。

employeemacBook仍然有很强的参考macBook我们只需在var之前添加关键字strong macBook使MacBookassinee属性变弱,如下所示。

你有注意到吗? 是的,以前有一个关键字weakvar assinee:Employee ,现在您可以看到已在控制台中确认已重新分配了employeemacbook对象/实例,并记录了Employee : Jack removed, & Mackbook : JHSF8S32 removed

因此,我们确实使用weak引用类型打破了保留周期。

那么unowned呢? 什么时候使用?

一个unowned 引用不会对其引用的实例保持强大的控制力。

那句话似乎重复了吗? 难道不是我们读的同一个定义吗? 是的 ! 像弱引用一样,当变量或属性之前使用unowned关键字声明时,它不持有指向它所指向的对象的强引用。

因此,您要说的是,我们也可以通过不使用任何引用来打破上述EmployeeMacBook 保留周期吗? 是!!

那为什么我们需要unowned关键字呢? 应该有一些条件,我们应该使用unowned而不是weak ,反之亦然,让我们看看苹果医生告诉了我什么。

与弱引用一样,无主引用也不能对其引用的实例保持强大的控制力。 但是,当另一个实例具有相同的生存期或更长的生存期时,将使用无主引用。

让我们稍微调整一下以上陈述,以供我们理解

“弱 引用用于在其生命周期中某个时刻可能变为零的地方。 如果 在自对象存在之前,该引用在任何时候都不可能变为零的情况, 则使用 拥有的 引用

似乎仍然很困惑,如果可以的话,让我们通过代码示例进一步解决该问题。

您可以通过将unowned关键字放在属性或变量声明之前来指示无主引用。

让我们使用我们以前的Employee类,再说一个类,即ICard ,每位员工将拥有一个iCard,而每个iCard都指向一个员工

现在是我们的员工类,它具有3个属性, name, title & iCardiCardICard类类型,其定义如下所示

如我们所料, 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已被删除

什么时候使用无所有权? 还是弱?

回到我们前面的示例EmployeeMacBook ,为什么我们不使用assinee作为未拥有code : Employe and MacBook Definition ,在code : Employe and MacBook Definition因为在这种用例中,当jack拿新笔记本电脑时,受让人有可能变成零。 ,则MacBook的受让人变量将一直为nil,直到它指向它所指派的新雇员为止。


因此,这不是唯一的保留/强循环问题,它在类实例之间不会发生,有可能在使用闭包时创建一个保留周期。如果不确定什么是闭包,我建议先阅读闭包再继续。

让我们创建PhotosViewController负责显示照片的PhotosViewController ,再创建一个负责将照片发送到我们的PhotosViewController

那就是我们的PhotosViewModel在顶部,我们声明了一个封闭类型DidGetPhotos ,它接受一个参数photos ,该参数是数组。如前所述,该类负责通过didGetPhotos封闭将照片PhotosViewControllerdidGetPhotos

现在让我们看看我们的PhotosViewController

让我们逐行阅读PhotsViewController

在第102行中,我们创建声明并初始化类型为PhotosViewModelphotosViewModel对象,从而使负责获取照片的对象

在第104行中,我们定义了一个loadPhotos()函数,在其中我们将didGetPhotos闭包分配给了photosViewModel ,因此这是我们从ViewModel中获取所有照片的回调函数的地方。 在闭包内部,我们调用函数updatePhotosInView()来显示photosViewModel给定的照片

这有保留周期吗? 让我们像往常一样检查它,让我们创建一个PhotosViewContoller实例,然后将其取消,确认它记录在deinit()内,释放或释放。

我们的deinit()日志在哪里? 我们创建了一个名为loadPhotos()的实例,并将photosViewController设置为nil,理想情况下,它应该已经释放了吗? 在找到答案之前,让我们先阅读以下有关闭包的事实

在快速访问闭包内部的自身时,使闭包对自身有很强的参考作用。

这是否意味着如果在闭包内部使用闭包,它将保留自身?

让我们想象一下

哦,是的!,似乎有保留周期, photoViewController强烈引用了photosViewModelphotosViewModel强烈引用了didGetPhotos()闭包,我们在第106行的loadPhotos()函数中访问了闭包内部的self。

让我们用之前学到的技巧(弱,无主)来打破它,这样就可以继续使didGetPhotos属性变弱吗?,不,您不能,编译器会对我大喊大叫,我们可以在闭包内部做些什么?

是的,一点没错!! ,我们必须打破封闭的周期,因为我们知道封闭捕获了引用权限,因此我们需要告诉PhotoViewController内部的didGetPhotos封闭以捕获弱引用自身。

现在让我们检查代码的外观吗?

哦,是的,您似乎去了photoViewContoller ,因为您可以看到日志Deinited the PhotosViewController,我们该怎么做?

观察行号105,我们通过在闭包参数之前提供[weak self]捕获自我作为弱引用,如您所见,我们将其作为数组传递,我的意思是我们将那些捕捉到的值传递给数组[],这意味着我们可以捕捉更多封闭中的一个参考。

这就是我关于保持/强力循环的全部内容,如果有任何疑问,请随时发表评论,希望下次会出现一些新问题和新技术。