一篇文章看懂自动引用计数和循环引用到底是怎么回事

2023-11-19

ARC

Swift中的ARC(Automatic Reference Counting,自动引用计数)是一种内存管理机制,用于跟踪和管理应用程序中的对象引用计数,确保内存中的对象只在需要的时候存在,当对象的引用计数为0时就会被系统析构掉。

ARC的重要概念

  1. 引用计数:每个对象都有一个引用计数,表示有多少个指针引用该对象。当引用计数为0时,对象将被释放。
  2. 强引用:默认情况下,Swift中的对象之间都是强引用关系,即一个对象持有另一个对象的强引用,如果持有该引用的对象被释放了,它所持有的对象也将被释放。
  3. 弱引用:弱引用不会增加对象的引用计数。当持有该引用的对象被释放时,弱引用会被自动设置为nil。在Swift中,使用weak关键字来定义弱引用。
  4. 无主引用:与弱引用类似,无主引用也不会增加对象的引用计数。但不同之处在于,无主引用默认情况下不是可选类型,因此必须始终指向一个对象。在Swift中,使用unowned关键字来定义无主引用。
  5. 循环引用:如果两个对象互相持有对方的强引用,将会导致循环引用,即这两个对象都无法被释放。为了避免循环引用,可以使用弱引用或无主引用。

造成循环引用的情况以及解决办法

两种解决办法:

1. 使用弱引用

在以下示例中,类A持有一个强引用类B,类B持有一个弱引用类A。这可以防止循环引用,因为类B不能保持类A的引用计数。当类A释放时,类B的弱引用将自动设置为nil。若没有weak,objA对象的b变量指向objB对象,objB对象的a变量指向objA对象,且都是强引用,这时便造成了循环引用,内存泄漏。

class A {
    var b: B?
}

class B {
    weak var a: A?
}

var objA: A? = A()
var objB: B? = B()

objA?.b = objB
objB?.a = objA

objA = nil
objB = nil

2. 使用无主引用

在以下示例中,类A持有一个强引用类B,类B持有一个无主引用类A。这可以防止循环引用,因为类B不能保持类A的引用计数。当类A释放时,类B的无主引用将继续指向已释放的对象,这可能会导致运行时错误。因此,需要确保类B在访问类A时检查其无主引用是否为nil。unowned的用法和weak类似,区别在于无主引用是对非可选型变量,而弱引用是对可选型变量。

class A {
    var b: B?
}

class B {
    unowned var a: A
    
    init(a: A) {
        self.a = a
    }
}

var objA: A? = A()
var objB: B? = B(a: objA!)

objA?.b = objB
objB = nil

objA = nil

三种循环引用情况

1. 闭包捕获self

在闭包中捕获self可能会导致循环引用。当闭包中捕获self时,它会持有对self的强引用,这可能会导致循环引用。

解决办法是使用weak或unowned来定义捕获的引用。如果闭包可能在self释放之后被调用,应该使用unowned,否则应该使用weak。

class MyClass {
    var closure: (() -> Void)?
    func someMethod() {
        closure = { [weak self] in
            self?.someOtherMethod()
        }
    }
    func someOtherMethod() {
        // ...
    }
}

2. 委托和代理

委托和代理模式可能会导致循环引用。在这个例子中,我们定义了一个协议MyDelegate,MyClass是遵循这个协议的类。同时,我们定义了一个UIViewController的子类MyViewController,它是MyClass的代理对象。在MyViewController中,我们将myClass的代理设置为self,并实现了MyDelegate协议中的方法didSomething()。在MyViewController中,当代理方法被调用时,我们调用了myClass的doSomething()方法。

这个例子中的循环引用问题是,MyViewController持有对myClass的强引用,而myClass持有对MyViewController代理对象的强引用。这意味着,当MyViewController被销毁时,它并不能被释放,因为MyClass仍然持有对它的强引用。而当MyClass被销毁时,它也不能被释放,因为MyViewController仍然持有对它的强引用。这样就形成了循环引用的问题。

为了解决这个问题,我们需要使用weak来声明对代理对象的引用。

 

protocol MyDelegate: class {
    func didSomething()
}

class MyClass {
    weak var delegate: MyDelegate?

    func doSomething() {
        // ...
        delegate?.didSomething()
        // ...
    }
}

class MyViewController: UIViewController, MyDelegate {
    let myClass = MyClass()

    override func viewDidLoad() {
        super.viewDidLoad()

        myClass.delegate = self
    }

    deinit {
        print("MyViewController deinit")
    }

    func didSomething() {
        // ...
        myClass.doSomething()
        // ...
    }
}

3. 对象间相互引用

如果两个对象相互持有对方的强引用,那么就会发生循环引用。

解决办法是将其中一个引用定义为弱引用或无主引用。如果两个对象的生命周期相同,则应该使用弱引用,否则应该使用无主引用。

class MyClass {
    var otherObject: OtherClass?
    // ...
}

class OtherClass {
    weak var myObject: MyClass?
    // ...
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一篇文章看懂自动引用计数和循环引用到底是怎么回事 的相关文章

随机推荐

  • wimax与anroid的困惑

    我要加入wimax组 研究wimax 但是 为什么是anroid平台呢 wimax和android有关系吗 我们是做wimax芯片的呀 难道wimax芯片上跑anroid系统 好像不太可能 经过分析 应该是xx公司要使用我们的wimax芯片
  • PR-RL:Portrait Relighting via Deep Reinforcement Learning

    文章目录 Title PR RL Portrait Relighting via Deep Reinforcement Learning 1 Article 1 1 Abstract and Introduction 1 2 Conclus
  • thttpd源码分析

    由于最近要自己实现一个嵌入式web服务器 所以开始了对嵌入式web服务器的相关学习 为了使自己对服务器了解更加深入 便找到了开源的服务器进行了相关学习 首先学习的是 thttpd thttpd 是一个小型的 HTTP 服务器 官方网址 ht
  • Elasticsearch 7.13.2启动成功,但无法访问?

    今天在linux服务器上配置了es环境 已经成功运行 如下 原因 elasticsearch出于安全策略考虑 默认仅开启了本地访问 需要额外配置远程访问 备注 生产环境请设置密码 且不要直接开放0 0 0 0 解决 在elasticsear
  • 图像的FFT变换

    一 实验设备 计算机 matlab软件 二 实验目的 1 理解并掌握图像的FFT变换的原理 2 学习使用matlab对图像进行FFT变换 三 实验原理 图像fft变换可以将图像空间域变为频率域 进而对频率域图像进行操作 这样会使操作变得简单
  • vue_cli4遇到的问题及解决

    vue cli4遇到的问题及解决 vue cli4遇到的问题及解决 新建项目时报错 vue cli4遇到的问题及解决 新建项目时报错 新建项目代码 vue create project name 报错信息图 解决办法 检查node版本与np
  • 嵌入式系统C语言编程小心使用局部变量

    问题 今天同事在写一个STM32上的程序时 总是遇到内存溢出的错误 结果发现是因为使用了一个局部变量导致的 因为C语言的局部变量被编译器自动放到栈区的空间 全局变量需要手动申请并释放空间 嵌入式系统的栈区本来就很小 而且要放进去的变量是一个
  • 使用CSS在浏览器中绘制虚拟仪表盘(2020-12-30更新)

    效果
  • 【idea】idea无法打开,常规报错的原因和解决方法

    2020 07 29 更新 mac下因破解无法打开的解决方案 删除 Users 你的名字 Library Preferences IntelliJIdea2019 3 idea vmoptions 添加的内容即可 原因一 老版本的idea没
  • b01lers CTF web 复现

    warmup 按照提示依次 base64 加密后访问 可以访问 flag txt 也就是 Li9mbGFnLnR4dA from base64 import b64decode import flask app flask Flask na
  • Spring基础3——AOP,事务管理

    导航 黑马Java笔记 踩坑汇总 JavaSE JavaWeb SSM SpringBoot 瑞吉外卖 SpringCloud SpringCloudAlibaba 黑马旅游 谷粒商城 目录 1 AOP简介 1 1 AOP概念 作用 方式
  • 【Qt】智能指针

    https zhuanlan zhihu com p 364014571 ivk sa 1024320u 代码中出现一个bug 最终发现是由于在某个特殊情况下出现了使用垂悬指针 造成了程序崩溃 进而学习了解了Qt的智能指针机制 一 悬垂指针
  • 【力扣经典题目】链表的回文结构,赶快收藏起来

    题目描述 对于一个链表 请设计一个时间复杂度为O n 额外空间复杂度为O 1 的算法 判断其是否为回文结构 给定一个链表的头指针A 请返回一个bool值 代表其是否为回文结构 保证链表长度小于等于900 测试样例 1 gt 2 gt 2 g
  • Windows10 2004无线网卡电源管理消失 电源计划只有平衡

    问题 升级到了Windows10 2004 发现每次睡眠后QQ和微信消息都收不到了 去网卡的属性界面却找不到电源管理面板 并且电源计划中也只能创建平衡的电源计划 在网上找了很多解决办法 最多的是关于一个改注册表中的更改注册表HKEY LOC
  • 9 变量进阶

    变量进阶 变量的引用 可变和不可变类型 局部变量和全局变量 1 变量的引用 变量和数据都是保存在内存中的 在 python 中 函数 的参数传递以及返回值都是靠引用传递的 引用的概念 在 python 中 变量和数据是分开存储的 数据保存在
  • 应用层、传输层、网络层、数据链路层

    应用层 包括 应用服务 FTP FileTransfer Protocol 文件传输协议 和DNS Domain NameSystem 域名系统 发送HTTP请求 DNS域名解析系统 提供通过域名 www baidu com 查找IP地址
  • 劳务派遣员工转正制度是什么

    一 劳务派遣员工转正制度是什么 要根据不同的单位来看 因为具体的制度还是要看用工单位的相关规定 比如大部分银行每年都会有一定的转正名额 公务员和事业单位的转正机会则较少 想要成为正式员工还是要参加招聘考试 银行招聘的时候会以劳务派遣的方式招
  • DDK&WDM学习 - HelloWDM,WDM驱动加载,INF文件

    转自 http www itstudy net html 200911 30 20091130234443 htm 第十五章 驱动程序的安装 方便的 自动的和容易的安装一个设备驱动程序对于用户来说是一个重要的步骤 对于提供给用户容易的设备管
  • 解决存储vuex数据,页面刷新后vuex数据被清空了的问题

    1 vuex刷新后数据会被清除 2 可以监听用户是否手动刷新页面 刷新之前先把vuex的数据存储在localStorage里面 页面加载时读取localStorage里的状态信息给vuex赋值 赋值后再清空localStorage 打开ap
  • 一篇文章看懂自动引用计数和循环引用到底是怎么回事

    ARC Swift中的ARC Automatic Reference Counting 自动引用计数 是一种内存管理机制 用于跟踪和管理应用程序中的对象引用计数 确保内存中的对象只在需要的时候存在 当对象的引用计数为0时就会被系统析构掉 A