浅谈iOS开发中的自动引用计数ARC

news/2024/7/20 20:04:30 标签: ios, ARC, swift, objective-c, 内存管理, 内存泄漏, 循环引用

1.ARC是什么

      我们知道,在C语言中,创建对象时必须手动分配和释放适量的内存。然而,在 Swift 中,当不再需要类实例时,ARC 会自动释放这些实例的内存

      Swift 使用 ARC 来跟踪和管理应用程序的内存,其主要是由Objective-C语言提供的。ARC是一种自动化的内存管理机制,它通过在编译时插入内存管理代码来自动管理对象的引用计数。ARC机制可以让开发者不用手动管理对象的引用计数,从而减少了内存泄漏和野指针等问题的发生。

      总的来说:

  1. Swift中每个对象都有一个【被引用计数】;
  2. 当对象A被引用时(实例),对象A的被引用计数会 + 1;
  3. 当对象A被放弃引用时,对象A的被引用计数会 - 1;
  4. 只有当对象A的被引用计数为 0 的时候,ARC才会释放对象A的内存。

2.强引用stong

      为了防止实例在仍然需要时被释放,属性、常量或变量在分配实例时会建立对实例的强引用。


//简单类Person
class Person{
    
    var name0 : String
    var name1 : String
    
    
    init(name0: String, name1: String) {
        self.name0 = name0
        self.name1 = name1
    }
    
    deinit {
        print("Person is deallocated")
    }
}


let person : Person? = Person(name0: "whj", name1: "whj1")

var man = person
var women = person

man = nil
women = nil

        以上设置了一个简单类Person,它有两个属性:init()deinit()deinit()方法仅在释放时才会被调用。类定义之后, Person类的实例已初始化。还有另外两个变量(man、women)指向与第一个变量(person)相同的实例。

        当我们将man和women设置为nil时,发现deinit()并没有发挥作用,说明该实例并没有被释放内存,这是因为三个变量(person、man、women)都对该实例有着很强的引用,当三个变量均引用该实例的时候, 该实例的ARC的计数为3。因此,即使将两个变量设置为 nil,引用计数仍然为 1。除非引用计数不为零,否则实例仍然存在。

循环引用(类之间强引用引起)

        循环引用可以简单的理解为A引用了B,B也引用了A,因为两者相互持有,所以ARC无法释放两者,这在应用程序中是致命的。



//简单类Person
class Person{
    
    var name0 : String
    var name1 : String
    var band : Band?
    
    init(name0: String, name1: String) {
        self.name0 = name0
        self.name1 = name1
    }
    
    deinit {
        print("Person is deallocated")
    }
}

class Band{
    var bandName : String
    var person : Person?
    
    init(bandName: String) {
        self.bandName = bandName
    }
    
    deinit {
        print("\(bandName) is deallocated.")
     }
    
    
}

var person : Person? = Person(name0: "whj", name1: "whj1")
var band : Band? = Band(bandName: "ruishi")

person?.band = band
band?.person = person

person = nil
band = nil

        在上面的示例中,有两个简单的类,称为PersonBand。每个类都有一个可选属性,该属性具有另一个类的类型。正如您在代码中看到的,这些属性在初始化后被分配给彼此的实例。目前两者的关系可以如下图描述:

        当我们设置person和band为nil ,关系如下:

        由于变量被设置为nil,变量和实例之间的强引用消失了。然而,实例仍然存在,因为“ Band”属性和“Person”属性对每个实例都有很强的引用。只要存在强引用,引用计数就不会为零。因此,即使变量为零,实例也不会被释放。这称为循环引用。这会导致ARC 无法释放其内存,造成内存泄漏

        Block 和 代理 、NSTimer均有可能造成循环引用

3.弱引用weak

        解决循环引用的方法之一就是使用弱引用weak。弱引用weak对实例没有强引用。换句话说,它不会增加实例的强引用计数。因此,如果使用weak关键字,它并不真正参与实例的生命周期管理。

        将代码改成:



//简单类Person
class Person{
    
    var name0 : String
    var name1 : String
    weak var band : Band?
    
    init(name0: String, name1: String) {
        self.name0 = name0
        self.name1 = name1
    }
    
    deinit {
        print("Person is deallocated")
    }
}

class Band{
    var bandName : String
    var person : Person?
    
    init(bandName: String) {
        self.bandName = bandName
    }
    
    deinit {
        print("\(bandName) is deallocated.")
     }
    
    
}

var person : Person? = Person(name0: "whj", name1: "whj1")
var band : Band? = Band(bandName: "ruishi")

person?.band = band
band?.person = person

person = nil
band = nil

4.无主引用Unowned

        解决循环引用的另一种方法就是使用无主引用Unowned。与弱引用相同,无主引用不会增加引用计数。那么,无主引用Unowned和弱引用weak有什么区别呢?区别在于:

weak是可选类型,可以设置为nil,而Unowned是值类型,其必须持有具体的值。

5.Autoreleasepool 自动释放池

在Swift中,自动释放池(Autorelease Pool)是一种用于管理内存释放的机制,它可以减少内存峰值并提高内存使用效率。自动释放池允许您暂时保留对象,直到您离开当前代码块,然后自动释放它们。

在Swift中,您可以使用 autoreleasepool 块来创建自动释放池。在 autoreleasepool 块中创建的对象都会被加入到自动释放池中。当程序离开该块时,自动释放池将被清空,并释放其中的所有对象。

func myFunc() {
    autoreleasepool {
        // 创建一些临时对象
        let obj1 = MyClass()
        let obj2 = MyClass()

        // 在此使用 obj1 和 obj2

    } // 自动释放池在这里被清空
}

        当代码中临时对象较多的时,或者代码执行时间长,那么使用autoreleasepool可以对项目的内存利用效率产生积极的影响

参考:

https://kentakodashima.medium.com/ios-arc-memory-management-in-ios-30aae3da92cf

iOS内存管理_ios 内存管理-CSDN博客


http://www.niftyadmin.cn/n/5465571.html

相关文章

剑指offer--数组中重复的数字

一.题目描述 在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。 算法1.排序,然后遍历,时间复杂度O(nlogn),空…

hcip实验4:gre mgre ppp综合实验

实验拓扑: 实验目的: 1.R5为ISP,只能进行IP地址配置,其所有地址均配为公有IP地址 2.R1和R5间使用PPP的PAP认证,R5为主认证方;R2与R5之间使用ppp的CHAP认证,R5为主认证方;R3与R5之间使用HDLC封装; 3.R1、R…

蓝桥杯_阅读魔法书(字符串匹配)

原题连接 #include <iostream> #include <string> #include <algorithm> #include <vector> using namespace std;string s; vector<string> vs;int n;int main() {cin>>s>>n;while(n--){string t; cin>>t;vs.push_back(t);}…

MFC:滑块控件与进度条控件的使用

这是一个基于对话框的MFC程序。程序目的是实验滑块控件(Slider Control)与进度条控件(Progress Control)的使用。 滑块常用操作包括设置滑动范围、设置位置、获取位置等&#xff1b;当滑块的位置改变后&#xff0c;系统发出WM_COMMAND消息&#xff08;通知码NM_CUSTOMDRA…

蓝桥杯刷题_day10

文章目录 最大子数组和环形子数组的最大和乘积最大子数组 最大子数组和 【题目描述】 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组是数组中的一个连续部分。 【…

在linux上设置nginx上自动启动

进入到/lib/systemd/system/目录 cd /lib/systemd/system/要设置Nginx服务在系统启动时自动启动&#xff0c;您可以使用Systemd来管理Nginx服务并配置它开机自启动。以下是在Linux系统上设置Nginx开机自启动的步骤&#xff1a; 创建自定义的Systemd单元文件&#xff1a; 首先…

mac系统使用经验

mac安装brew brew是macos下的一个包管理工具&#xff0c;类似与centos的yum&#xff0c;ubuntu的apt-get等。 自动脚本(全部国内地址)&#xff08;在Mac os终端中复制粘贴回车下面这句话) /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/H…

深入理解Python异常处理机制:助力你的自动化测试脚本

前些天&#xff0c;公司准备使用开源BI工具superset&#xff0c;但部署成功后&#xff0c;连接阿里数仓获取表时&#xff0c;一直报错&#xff0c;苦于日志不详细&#xff0c;从日志中并没有看出哪里的问题&#xff0c;然后就拉源码进行调试&#xff0c;终于找到抛出异常的位置…