【iOS】引用计数与autorelease

文章目录

  • 前言
  • 一、什么是自动引用计数
  • 二、内存管理/引用计数
  • 三、内存管理的思考方式
  • 四、release与autorelease
  • 五、赋值的引用计数


前言

最近在学习iOS内存管理之引用计数,特此撰写博客记录学习过程


一、什么是自动引用计数

自动引用计数(ARC)是指内存管理中对引用计数采取自动计数的方式

在Objective-C中采用Automatic Reference Counting
(ARC)机制,让编译器来进行内存管理。在新一代Apple
LLVM编译器中设置ARC为有效状态,就无需再次键入retain或者release代码,这在降低程序崩溃、内存泄漏等风险的同时,很大程度上减少了开发程序的工作量。编译器完全清楚目标对象,并能立刻释放那些不再被使用的对象。如此来,应用程序将具有可预测性,且能流畅运行,速度也将大幅提升。

这些优点无疑极具吸引力,但关于ARC技术,最重要的还是下面这一点:

“在LLVM编译器中设置ARC为有效状态,就无需再次键入retain或者是release代码。”

二、内存管理/引用计数

iOS中的引用计数是一种内存管理机制,用于追踪和管理内存的使用情况。每个对象都有一个引用计数器,用于记录有多少个指针指向该对象。

当对象被创建时,其引用计数器会被设置为1。当一个指针指向该对象时,引用计数器会增加1。如果有一个指针不再指向该对象,那么引用计数器会减少1。当引用计数器为0时,系统会自动释放对象的内存空间。

我们以办公室开灯关灯为例距离说明我们OC的引用计数:
此时的对象相当于我们办公室的照明设备
在这里插入图片描述
我们以一张图来理解我们引用计数的内存管理:
在这里插入图片描述

三、内存管理的思考方式

我们以开灯关灯为例子简单地了解了我们内存管理的机制,那么在OC中我们该如何进行我们的内存管理呢?

  • 自己生成的对象,自己持有;
  • 非自己生成的对象,自己也能持有;
  • 不再需要自己持有的对象时释放;
  • 非自己持有的对象无法释放。

对象操作与OC方法的对应

对象操作Objective-C方法
生成并持有对象alloc/new/copy/mutableCopy等
持有对象retain
释放对象release
废弃对象dealloc

接下来我们详细了解一下各个思考方式:

  1. 自己生成的对象,自己持有

使用alloc,new,copy,mutablecopy名称开头的方法意味着自己生成的对象只能自己持有
我们这里的自己对应着对象的使用环境,也可以说是编程人员自身

id obj = [[NSObject alloc] init];
id obj = [NSObject new];

例如上述两种方法是完全一致的,我们使用alloc方法创建对象意味着自己生成的对象自己能够持有。

另外使用对这些名称使用驼峰命名法我们也可以自己生成并持有对象,例如:

  • allocMyObject
  • newThatObject
  • copyThis
  • mutableCopyYourObject
  1. 非自己生成的对象,自己也能持有

我们通过alloc,new,copy,mutablecopy以外
的方法取得的对象,因为非自己生成并持有,所以自己不是该对象的持有者

但是我们可以通过retain方法成为对象的持有者

//获取非自己生成并持有的对象
id obj = [NSMutableArray array];
//获取到了对象,但是自己并不持有//自己持有对象
[obj retain];

从上述的举例可以看出,通过retain方法,非自己持有生成的对象跟用alloc/new/copy/mutableCopy方法生成并持有的对象一样,成了自己所持有的。

我们在上文说到我们通过alloc/new/copy/mutableCopy以外方法取得的对象存在,但自己不持有对象,是如何实现的呢?

解释:
这是因为它们在被创建时并没有任何对象对它们进行retain同时在对象被创建之后对对象使用了autorelease方法
我们以以下代码为例阐述我们非自己生成并持有的对象生成的过程

- (id)object {id obj = [[NSObject alloc] init];[obj autorelease];return obj;
}

通过查看NSMutableArray的这些类方法的实现,你会发现它们内部都调用了 autorelease 方法。

通过alloc等之外方法创建的对象在创建时会被自动加入到当前的autorelease pool中。Autorelease pool是一个特殊的对象,它持有一组对象,并会在合适的时候释放这些对象。这就是为什么我们通过alloc,new,copy,mutablecopy以外
的方法取得的对象非自己生成并持有。

  1. 不再需要自己持有的对象时释放

自己持有的对象,一旦不再需要,持有者有义务释放该对象,释放使用release方法。
例如:

//自己生成并持有对象
id obj = [[NSObject alloc] init];//释放对象
[obj release];

这样一来我们的对象的引用计数为0,对象已经被释放,我们的obj无法再次访问我们的对象,如果访问会导致程序崩溃

同样,自己生成而非自己所持有的对象,若使用retain方法持有,同样也可以使用release释放。

  1. 无法释放非自己持有的对象
//自己生成并持有对象
id obj = [[NSObject alloc] init];//自己持有对象[obj release];//对象已释放 [obj release];
/*
释放之后再次释放已非自己持有的对象!
应用程序崩溃!崩溃情况:
再度废弃已经废弃了的对象时崩溃
访问已经废弃的对象时崩溃
*/

在第一次release之后我们的对象已经被释放,如果再次释放会导致程序崩溃

四、release与autorelease

release是一个立即释放对象的方法。当你调用release时,对象的引用计数会立即减少,如果引用计数达到0,对象会被立即释放。因此,release需要在不再需要对象时手动调用,以避免内存泄漏。

autorelease是一种延迟释放对象的机制。当一个对象被autorelease时,它的引用计数不会立即减少,而是在当前autorelease pool被销毁时才会减少。
这意味着,即使你不再使用一个对象,它也不会立即被释放,而是等待当前Runloop结束时才会被释放。因此,autorelease可以在需要延迟释放对象时使用,以避免频繁释放和创建对象带来的性能开销。

这里需要注意的是我们只有在MRC中才可以使用releaseautorelease对我们的对象进行内存管理。
ARC环境中我们使用release方法会报错,因为我们的编译器自动帮我们进行了内存管理

同时在《Objective-C高级编程-iOS与OS+X多线程和内存管理》中说道:我们使用autorelease方法时,可以使取得的对象存在,但自己不持有对象。这样的功能可以使对象超出指定生存范围时能够自动并正确地释放,如下图所示:
在这里插入图片描述

对于release方法我们这里不再讲述,我们主要讲一下autorelease方法

autorelease方法听起来很像ARC,但其实它更像c语言中的局部变量,当我们的局部变量超出其作用域时,我们的局部变量会被自动废弃
在这里插入图片描述

autorelease像c语言中对待局部变量一样来对待对象,当对象超出作用域,对象实例的release方法会被自动调用

与C语言中的局部变量不同的是,我们可以设定我们变量的作用域

在这里插入图片描述
在这里插入图片描述
调用autorelease的对象,在废弃NSAutoreleasePool对象时,都将会调用release方法,用源码表示如下:

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];id obj = [[NSObject alloc] init];[obj autorelease];[pool drain];NSLog(@"%lu", [obj retainCount]);

这段代码执行下来,我们的obj会在pool drain之后自动销毁,之后我们就无法再次访问obj对象

当然在使用autorelease方法时还会有另外一种情况:

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];id obj = [NSMutableArray array];[obj autorelease];[pool drain];NSLog(@"%lu", [obj retainCount]);

如果我们将alloc方法替换为array方法时,我们的这段程序会报错,这是因为我们的array方法内部已经自动帮我们生成了一个NSAutoreleasePool对象,当我们再次手动创建一个NSAutoreleasePool对象时并进行[pool drain];操作时,手动创建的NSAutoreleasePool对象与自动创建NSAutoreleasePool的对象都会将obj加入释放池的数组中,因此对我们手动创建的pool对象进行销毁操作时也意味着我们array方法内部的自动释放池被销毁,obj的引用计数为0,被自动销毁

五、赋值的引用计数

我们代码中常用的赋值方式有两种,一种是直接赋值,一直是通过setter方法进行赋值

我们给出代码例子:

a.room = r;//setter
id obj1 = obj;//直接

a是一个对象,并且r是对另一个对象的引用,那么这段代码实际上是在将a对象的room属性设置为r引用的对象。

在这种情况下,如果a对象的r属性原来持有一个不同的对象,那么那个对象的引用计数会减1,而新的对象的引用计数会加1。这是因为当你将一个对象的属性设置为另一个对象时,你实际上是在增加对新对象的引用,同时减少对原对象的引用。

如果room属性原来没有持有对象,那么仅仅会对新的对象的引用计数加1。

    preson *a = [[preson alloc] init];preson *b = [[preson alloc] init];room *r = [[room alloc] init];r.no = 888;a.room = r;//是新对象引用计数加1b.room= r;NSLog(@"%lu", [r retainCount]);

在这里插入图片描述

对于id obj1 = obj;,这段代码只是创建了一个新的引用obj1,它引用的是obj对象。这个操作并没有改变obj的引用计数,因为obj1只是对obj的一个新引用,并没有增加或减少obj的引用计数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/120436.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

selenium中ActionChains方法详细讲解

前言 本文将介绍Selenium中的ActionChains类及其使用方法,帮助您模拟用户在网页上的鼠标和键盘操作。了解ActionChains的常用方法和示例代码,可轻松实现移动鼠标、点击元素、拖拽元素等操作。通过本文的学习,您能更好地应用ActionChains解决自…

SpringMVC 学习(二)Hello SpringMVC

3. Hello SpringMVC (1) 新建 maven 模块 springmvc-02-hellomvc (2) 确认依赖的导入 (3) 配置 web.xml <!--web/WEB-INF/web.xml--> <?xml version"1.0" encoding"UTF-8"?> <web-app xmlns"http://xmlns.jcp.org/xml/ns/javaee…

Android Logcat 命令行工具

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、日常用法3.1 面板介绍3.2 日志过滤…

Docker 容器技术 (上) 环境安装和部署、容器镜像、使用IDEA构建Springboot程序镜像、远程仓库、容器网络管理

Docker容器技术 文章目录 Docker容器技术容器技术入门环境安装和部署从虚拟机到容器容器工作机制简述 容器与镜像初识容器镜像镜像结构介绍构建镜像发布镜像到远程仓库实战&#xff1a;使用IDEA构建SpringBoot程序镜像 容器网络管理容器网络类型用户自定义网络容器间网络容器外…

Cocos Creator3.8 实战问题(一)cocos creator prefab 无法显示内容

问题描述&#xff1a; cocos creator prefab 无法显示内容&#xff0c; 或者只显示一部分内容。 creator编辑器中能看见&#xff1a; 预览时&#xff0c;看不见内容&#xff1a; **问题原因&#xff1a;** prefab node 所在的layer&#xff0c;默认是default。 解决方法&…

HALCON支持GPU加速的算子有哪些?

参考例程get_operator_info。 get_opencl_operators这里可以查看到所有支持gpu加速的算子。 支持的算子列表&#xff1a; crop_rectangle1&#xff0c;deviation_image&#xff0c;mean_image&#xff0c;points_harris&#xff0c;gray_opening_shape&#xff0c; gray_dilat…

防火墙开启状态下,启动docker/容器 报错

1 问题描述&#xff1a; 防火墙开启状态下&#xff0c;启动docker/容器 报错:Failed to start Docker Application Container Engine. 2 解决方案&#xff1a; step1 先检查firewall-cmd中是否有区域占用了 docker0 firewall-cmd --get-active-zonesstep2 查看firewall-cm…

1、Kafka 安装与简单使用

第 1 章 Kafka 概述 1.1 定义 Kafka传统定义&#xff1a; Kafka是一个分布式的基于发布/订阅模式的消息队列&#xff08;Message Queue&#xff09;&#xff0c;主要应用于大数据实时处理领域。 Kafka最新定义 &#xff1a; Kafka是 一个开源的 分 布式事件流平台 &#xff08…

编译原理简介

编译原理简介 编译原理的研究对于理解和设计编程语言、编译器和解释器都非常重要。它不仅可以提高程序的执行效率&#xff0c;还可以帮助开发人员更好地理解程序的运行机制。编译原理是计算机科学中的一个重要分支&#xff0c;研究的是编译器的设计和实现。对于从事编译器开发…

远程访问:让您随时与电脑保持连接

远程访问可以带来什么&#xff1f; ​如今&#xff0c;我们的大部分工作都是在电脑的帮助下完成的。但是&#xff0c;我们并不总是坐在工作电脑前&#xff0c;随身携带电脑也很不方便&#xff0c;尤其是当工作设备是台式电脑而不是笔记本电脑时。那么&#xff0c;当我们不在…

在nodejs中如何防止ssrf攻击

在nodejs中如何防止ssrf攻击 什么是ssrf攻击 ssrf&#xff08;server-side request forgery&#xff09;是服务器端请求伪造&#xff0c;指攻击者能够从易受攻击的Web应用程序发送精心设计的请求的对其他网站进行攻击。(利用一个可发起网络请求的服务当作跳板来攻击其他服务)…

Java实现word excel ppt模板渲染与导出及预览 LibreOffice jodconverter

Java Office 一、文档格式转换 文档格式转换是office操作中经常需要进行一个操作&#xff0c;例如将docx文档转换成pdf格式。 java在这方面有许多的操作方式&#xff0c;大致可以分为内部调用&#xff08;无需要安装额外软件&#xff09;&#xff0c;外部调用&#xff08;需…