iOS中堆和栈的使用(Swift)

2023-11-18

堆和栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。

堆:队列优先,先进先出(FIFO—first in first out)。

栈:先进后出(FILO—First-In/Last-Out)。

堆栈空间分配

1.栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量等值。其操作方式类似于数据结构中的栈。

2.堆区(heap):一般由程序员分配释放,若程序员不释放,则可能会引起内存泄漏。其类似于链表。

堆栈缓存方式

iOS 中应用程序使用的计算机内存不是统一分配空间,运行代码使用的空间在三个不同的内存区域,分成三个段:“text segment “,“stack segment ”,“heap segment ”。

 

代码区(text segment ):是应用程序运行时应用程序代码存在的内存段,运行前就已经确定(编译时确定),通常为只读的。代码区的指令中包括操作码和要操作的对象(或对象地址引用),代码区指令根据程序设计流程依次执行,每一个指令,每一个单个函数、过程、方法和执行代码都存在这个内存段中直到应用程序退出。一般使用中很少涉及。

栈(Stack):当我们创建一个值类型,如结构体,系统将其存储在一个被称为栈的内存区域中,是由CPU直接管理和优化的。当一个函数声明一个变量,变量将存储在栈中,当函数调用完毕后栈会自动释放该变量。因此栈是非常易于管理的、有效的,由于是CPU直接控制,速度非常快。

堆(Heap):当我们创建了一个引用类型,如类,系统将把类实例存储在一个被称为堆的内存区域中。系统使用堆来存储其他对象引用的数据。堆是一个大的内存池,系统可以从该池中请求并动态分配内存块。堆不会像栈一样自动释放对象,需要额外的工作来完成。这使得在堆中创建和删除数据比栈慢。

栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。

stack 中的一个指针仅仅是一个整型变量,保存了heap(堆)中特定内存地址的数据。简而言之,操作系统使用stack 段中的指针值访问heap 段中的对象。如果stack 对象的指针没有了,则heap 中的对象就不能访问。这也是内存泄露的原因。

在iOS 操作系统的stack 段和heap 段中,你都可以创建数据对象。stack 对象的优点主要有两点,一是创建速度快,二是管理简单,它有严格的生命周期。stack 对象的缺点是它不灵活。创建时长度是多大就一直是多 大,创建时是哪个函数创建的,它的owner 就一直是它。不像heap 对象那样有多个owner ,其实多个owner 等同于引用计数。只有 heap 对象才是采用“引用计数”方法管理它。

堆栈数据结构区别

堆(数据结构):堆可以被看成是一棵树,如:堆排序。

栈(数据结构):一种先进后出的数据结构。

堆和栈究竟有什么区别? 主要的区别由以下几点:

1、管理方式不同;

管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。

2、空间大小不同;

空间大小:栈是一块空间较小,但是运行速度很快的内存区域。栈上的内存分配遵循后进先出的原则,通过移动栈的尾指针实现 push(入栈)和 pop(出栈)操作。我们的程序是由一个个方法组成的,CPU 会负责调度并执行这些方法。当我们的程序执行到某个方法的时候,需要在栈上为方法需要的内存开辟空间,此时把栈的尾指针向栈底移动。当方法执行完毕后需要释放掉这些空间,此时会把栈的尾指针移向栈顶,这就完成了一次栈上的内存分配。只要栈的剩余空间大于stack 对象申请创建的空间,操作系统就会为程序提供这段内存空间,否则将报异常提示栈溢出。

堆是内存中的另一块区域,空间比栈大的多,但是运行速度要比栈上的运行速度慢。堆可以在运行时动态的分配内存,补充栈上内存分配的不足。一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。

操作系统对于内存heap 段是采用链表进行管理的。操作系统有一个记录空闲内存地址的链表,当收到程序的申请时,会遍历链表,寻找第一个空间大于所申请的heap 节点,然后将该节点从空闲节点链表中删除,并将该节点的空间分配给程序。iOS使用了名为 ARC(自动引用计数)的技术。在多线程环境中,多个线程会共享堆上的内存,为了确保线程安全,不得不在堆上进行加锁操作,但是加锁操作是很耗费性能的,你在堆上所获的的数据安全性实际上是在牺牲性能的代价下得来的。

NSString 的对象就是stack 中的对象,NSMutableString 的对象就是heap 中的对象。前者创建时分配的内存长度固定且不可修改;后者是分配内存长度是可变的,可有多个owner, 适用于计数管理内存管理模式。

3、能否产生碎片不同;

碎 片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因 为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出。

4、生长方向不同;

生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

5、分配方式不同;

分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

6、分配效率不同;

分 配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较 高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内 存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到 足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

从这里我们可以看到,堆和栈相比,由于大量new/delete的使用,容 易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广 泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用 堆。

但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据在多个线程或者多个栈之间是不可以共享的,但是在栈内部多个值相等的变量是可以指向一个地址的。和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。无论是堆还是 栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果,就算是在你的程 序运行过程中,没有发生上面的问题,你还是要小心,说不定什么时候就崩掉了。

Swift中的使用

Swift 中的数据类型分为引用类型(类)和值类型(枚举、结构体)。引用类型存储在 “堆” 上,值类型存储在 “栈” 上。Swift 管理引用类型采用自动引用计数(ARC)的管理方法。值类型是由处理器来管理的,不需要程序员来管理。

 

在 Swift 中,典型的有 struct,enum,以及 tuple 都是值类型。而平时使用的Int,Double,Float,String,Array,Dictionary,Set 其实都是用结构体实现的,也是值类型。Swift 中,值类型的赋值为深拷贝(Deep Copy),值语义(Value Semantics)即新对象和源对象是独立的,当改变新对象的属性,源对象不会受到影响,反之同理。

在 Swift 中,class 和闭包是引用类型。引用类型的赋值是浅拷贝(Shallow Copy),引用语义(Reference Semantics)即新对象和源对象的变量名不同,但其引用(指向的内存空间)是一样的,因此当使用新对象操作其内部数据时,源对象的内部数据也会受到影响。

值类型作为参数传入时,函数体内部不能修改其值。引用类型作为参数传入时,函数体内部不能修改其指向的内存地址,但是可以修改其内部的变量值。

值类型的优点是:不变性,值类型的变量是严格的被一个所有者控制的;独立性,引用类型是相互依赖的,是一种隐式的依赖;还有可交换性。

对于面向对象编程,由于实例对象是可变的,导致对象的另一个享有者在合适的时候会去改变这个对象的属性。swift支持类的单继承,导致从多个class继承到更多地功能,增加了复杂度,并且会导致class紧耦合的问题。在多线程情况下,可以同时改变同一个引用。

选择值类型而不是引用类型的一个主要原因是能让你的代码变得更加简单。Swift的核心是面向协议,引用类型有许多的享有者。值类型被赋给一个变量或者常量,传给函数做参数时是它的值被拷贝的。这就让值类型在任何时候只有一个享有者,从而降低复杂度。你在任何情况下用一个值类型,都能够假设你的其他代码不会使它改变,这通常在多线程环境中很有用,如果一个线程中使用的数据被另一个线程给意外的修改了,这通常会产生非常严重的Bug,且相当难以调试。Class = 高复杂度,值 = 低复杂度。而且,swift对值类型的操作上进行了一些优化,因此才有了swift大量使用值类型代替引用类型的说法。

由于只有当你需要修改数据时两者的区别才会得到体现,所以当你的实例不会对数据进行修改的时候,值类型和引用类型看起来是完全相同的。你也许会想,写一个完全不可变的类,通过使用不可变的存储属性,以及避免暴露修改数据的接口,从而在Swift里实现一个不可变的类。事实上,大多数的Cocoa类,比如NSURL等,都被设计为不可变的类,然而,Swift当前并没有提供任何语言机制去强制申明一个类不可改变(比如子类化就能修改一个类的实现),只有结构体和枚举才是强制不可变的。

在Swift里,Array、String和Dictionary都是值类型,他们的行为和C语言中的int类似,每个实例都有自己的数据,你不需要额外做任何事情,比如做一个显式的copy,防止其他代码在你不知情的情况下修改等,更重要的是,你能安全地在线程间传递它,而不需要使用同步技术。在提高安全性的精神下,这个模型将帮助你在Swift中写出更多可预知的代码。

除此之外,Swift和OC还有其他的类型对应,对应关系如下:

 

但是,需要关注的是,对于原来OC中的数据的引用类型,swift中并没有真正完全的实现一套数据存储逻辑。只是内部保存了对oc对象的引用,使得swift api访问时行为逻辑和值类型一致。

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

iOS中堆和栈的使用(Swift) 的相关文章

随机推荐

  • 加解密和签名验签简述

    文章目录 一 数字加密算法 1 对称加密 2 非对称加密 3 对称加密和非对称加密的区别 二 使用keytool生成证书 1 创建证书 2 查看密钥库 2 1 keytool list 命令 2 2 keytool list v 命令 3
  • Transformer:Attention is All You Need

    Transformer论文逐段精读 论文精读 https www bilibili com video BV1pu411o7BE share source copy web vd source 30e93e9c70e5a43ae75d429
  • 分析 ExitCode 定位 Pod 异常退出原因

    使用kubectl describe pod
  • vue 表单校验不通过时拦截提交表单

    上代码
  • JSP 弹出框 子页面给父页面回传参数

    做一个jsp的页面 然后又弹出一个对话框 并且把输入框的值返回到文本中 具体代码如下 1 父页面 写道
  • 引入纯 ESM 模块化的第三方包

    CSDN中文章不一定能及时更新 欢迎点击前往我的博客查看最新版本 许盛的博客 背景 今天要做个 CLI 工具 一路调研学习加实践都比较顺利 但是在引入 globby 这个库时 就开始报错了 Users xusheng workspace t
  • 2023华为OD机试真题【二元组个数/哈希表】

    题目描述 给定两个数组a b 若a i b j 则称 i j 为一个二元组 求在给定的两个数组中 二元组的个数 输入描述 第一行输入 m 第二行输入m个数 表示第一个数组 第三行输入 n 第四行输入n个数 表示第二个数组 输出描述 二元组个
  • mysql cstmt_MySQL

    创建一个以JDBC连接数据库的程序 包含7个步骤 1 加载JDBC驱动程序 在连接数据库之前 首先要加载想要连接的数据库的驱动到JVM Java虚拟机 这通过java lang Class类的静态方法forName String class
  • 《上海市居住证》签注和积分确认流程指南

    上海市居住证 签注和积分确认流程指南 一 办理条件 员工已经自行至社区事务受理服务中心办理 上海市居住证 签注 由于上海市居住地所属的社区事务中心非常多 具体申请相关流程指南及材料办理清单最好事先和居住地所属的社区事务中心确认 办理 上海市
  • SSL_CTX结构体

    定义在ssl h头文件中 struct ssl ctx st SSL METHOD method unsigned long options unsigned long mode STACK OF SSL CIPHER cipher lis
  • E-R图(Entity Relationship Diagram)实体关系模型

    E R图也称实体 联系图 Entity Relationship Diagram实体关系模型 提供了表示实体类型 属性和联系的方法 用来描述现实世界的概念模型 它是描述现实世界关系概念模型的有效方法 是表示概念关系模型的一种方式 用 矩形框
  • 为什么要用缓冲流

    一开始学习处理流会疑问为什么速度会加快呢 好比一个10KB的文件 使用最基本的字节流读写 只要读一次10KB到内存 存一次10KB到目标文件就行了 但是使用缓冲不是要读1次10KB到缓冲 再从缓冲写一次10KB到CPU 再从CPU写10KB
  • No matching distribution found for tensorflow 解决方法

    2020 8最新版本为TF2 3 安装时容易出现的问题是各软件程序版本不统一的问题 GPU版本对应表如下图所示 图片来源于Tensorflow 官网 分割线 分割线
  • 使用JavaScript编写HTML

    使用JavaScript编写HTML 1 什么是JavaScript JavaScript是一种脚本语言 通过Dom定义表示和修改文档所需的对象和这些对象的行为和属性以及这些对象之间的关系从而操作HTML文档 比如添加 移除 改变或重排页面
  • ARM之未定义指令异常和SVC异常

    异常向量表的概述 在上一章 我们学习了建立异常向量表 这里我们可以通过看arm的手册 我们每一种异常都对应一个工作模式 下面我就来尝试触发一下未定义指令异常和SVC异常 异常发生的说明 简单的来说就是先保存现场 之后恢复现场 保存现场 我们
  • 第一章 计算机系统概论

    一 计算机系统简介 1 计算机软硬件概念 计算机是一种能够执行指令的电子设备 它由硬件和软件两部分组成 计算机硬件是指计算机系统中的物理组件 包括中央处理器 CPU 内存 硬盘 输入设备 如键盘 鼠标 输出设备 如显示器 打印机 等 这些硬
  • windows10安装RDKit化学信息学python库(通过anaconda)

    RDKit是一款功能强大的化学信息学工具 推荐使用anaconda安装 安装时应当参考 1 官方安装指南 Rdkit Anaconda org 2 中文教程 RDKit简介 RDKit 中文教程 2020 09 文档 chenzhaoqia
  • python爬虫之数据解析

    python爬虫之数据解析 正则表达式 bs4 xpath 主要运用在聚焦爬虫模块中 涉及到的数据解析方法有 正则表达式 bs4以及xpath 1 使用对象 聚焦爬虫 聚焦爬虫 爬取页面中指定的页面内容 2 数据解析原理概述 解析的局部的文
  • word在另外计算机格式不对,为什么word 2007文件在不同电脑上排版显示不同?应该如何解决?...

    为什么word文件在不同电脑排版显示不同 最近打印论文 自己的笔记本是word2007 保存的通用格式 预览排版没有问题 去打印店打印时 发现插入的图片窜行了 幸亏即使调整过来 并且保存 但拿回自己的笔记本打开后图片的位置还是窜行的 请问这
  • iOS中堆和栈的使用(Swift)

    堆和栈都是一种数据项按序排列的数据结构 只能在一端 称为栈顶 top 对数据项进行插入和删除 堆 队列优先 先进先出 FIFO first in first out 栈 先进后出 FILO First In Last Out 堆栈空间分配