Objective-C 对象存储在堆上而不是栈上 why?

FeynRules的上手使用3--运行 FeynRules的运行 接上两篇,在我们正确的设定模型参数和拉氏量之后,就可以运行FeynRules来推导费曼规则。 装载文件 首先需要设定FeynRules文件所在位置,并装载FeynRules模块: $FeynRulesPath = SetDirectory[ ] << FeynRules` 接下来是装载写好的模型文件: LoadModel[ < file.fr >, < file2.fr >, … ] 模型文件后缀为".fr",可以分几块构成。模型描述可以写在 阅读详情

一、什么是栈对象和堆对象

在Objective-C 中,对象通常是指一块有特定布局的连续内存区域。我们通常这样创建一个对象:

NSObject *obj = [[NSObject alloc] init]; 
这行代码创建了一个 NSObject 类型的指针 obj 和一个 NSObject 类型的对象,obj 指针存储在栈上,而其指向的对象则存储在堆上(简称为堆对象)。

目前 Objective-C 并不支持直接在栈上创建对象(简称为堆对象),但可以通过如下方式间接地创建:

struct { 
Class isa; 
} fakeNSObject; 
fakeNSObject.isa = [NSObject class];

NSObject obj = (NSObject )&fakeNSObject; 
NSLog(@”%@”, [obj description]);

栈对象 obj 也能正常工作,由此可见栈对象和堆对象都是可行的,但为什么 Objective-C 不默认使用栈对象呢?

二、栈对象优缺点

1、优点

速度

在栈上创建对象是非常快的,因为很多东西在编译时就确定了,运行时分配空间几乎不耗时;相对而言在堆上创建对象就非常耗时。

简单

栈对象的生命周期是确定的,对象出栈以后就会被释放,不会存在内存泄漏,但这同时也是栈对象的最大缺点。

2、缺点

生命周期固定

Objective-C 变量有效范围是由 “{}” 包含的块来决定的,也就是说栈对象的生命周期仅限于其所在的块里,出了块立马会被释放。一个对象被创建以后有可能会通过方法调用传递到别的方法,当栈对象的创建方法返回时,栈对象会被一起 pop 出栈而释放,导致其没法在别处被继续持有。此时 retain 操作会失效,除非用 copy 方法在想持有该栈对象的地方重新拷贝一份属于自己的栈对象。

因此,栈对象回给对象的内存管理造成相当大的麻烦。

空间

现代操作系统的栈和线程绑定,而栈空间是有限的,具体如下:

512 KB (secondary threads) 
8 MB (OS X main thread) 
1 MB (iOS main thread) 
因此对象如果都在栈上创建不太现实,而堆只要物理内存不告警可以无限制使用。

综合以上优缺点,Objective-C 选择用堆存储对象。

三、真的没有栈对象吗

实际上 Objective-C 里的 block 却是栈对象,因此栈对象面临的问题在 block 身上一个都不少,但由于 block 是仅有的特殊对象,大家对它的特殊都已经习惯了,比如入行第一年的时候老师就告诉我们想持有一个 block 要用 copy 将 block 从栈拷贝到堆上。

另外,根据前面所说,栈对象的有效区域仅限于其所在的块,因此像下面的代码就无法输出期望的结果:

void (^block)();
if(x)
{
block = ^{ printf("x\n"); };
}
else
{
block = ^{ printf("not x\n"); };
}
block();

这也是大家需要特别注意的地方。

四、参考文档

Threading Programming Guide

Stack and Heap Objects in Objective-C

五、其他

为了访问你创建在heap 中的数据,你最少要求有一个保存在stack 中的指针,因为你的CPU 通过stack 中的指针访问heap 中的数据。

你可以认为stack 中的一个指针仅仅是一个整型变量,保存了heap 中特定内存地址的数据。实际上,它有一点点复杂,但这是它的基本结构。

简而言之,操作系统使用stack 段中的指针值访问heap 段中的对象。如果stack 对象的指针没有了,则heap 中的对象就不能访问。这也是内存泄露的原因。

在iOS 操作系统的stack 段和heap 段中,你都可以创建数据对象。

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

stack 对象的创建

只要栈的剩余空间大于stack 对象申请创建的空间,操作系统就会为程序提供这段内存空间,否则将报异常提示栈溢出。

heap 对象的创建

操作系统对于内存heap 段是采用链表进行管理的。操作系统有一个记录空闲内存地址的链表,当收到程序的申请时,会遍历链表,寻找第一个空间大于所申请的heap 节点,然后将该节点从空闲节点链表中删除,并将该节点的空间分配给程序。 

例如:NSString 的对象就是stack 中的对象,NSMutableString 的对象就是heap 中的对象。

引入堆和栈的概念

  • 所以问题就来了,为什么OC对象需要进行内存管理,而其它非对象类型比如基本数据类型就不需要进行内存管理呢?
  • 只有OC对象才需要进行内存管理的本质原因?

因为:Objective-C的对象在内存中是以堆的方式分配空间的,并且堆内存是由你释放的,就是release
OC对象存放于堆里面(堆内存要程序员手动回收)
非OC对象一般放在栈里面(栈内存会被系统自动回收)
堆里面的内存是动态分配的,所以也就需要程序员手动的去添加内存、回收内存 

总结区别

  • 按管理方式分
    • 对于栈来讲,是由系统编译器自动管理,不需要程序员手动管理
    • 对于堆来讲,释放工作由程序员手动管理,不及时回收容易产生内存泄露
  • 按分配方式分
    • 堆是动态分配和回收内存的,没有静态分配的堆
    • 栈有两种分配方式:静态分配和动态分配
      • 静态分配是系统编译器完成的,比如局部变量的分配
      • 动态分配是有alloc函数进行分配的,但是栈的动态分配和堆是不同的,它的动态分配也由系统编译器进行释放,不需要程序员手动管理 
Hcaptcha验证码破解深度解析:从环境检测到n值算法的攻防实战 本文深入解析Hcaptcha验证码的攻防技术,从环境检测到n值算法的全面突破策略。详细介绍了浏览器指纹模拟、图像识别工程化解决方案及协议层逆向分析,为安全研究人员提供高效破解Hcaptcha的实战指南。 阅读详情

相关推荐

f_fs.c 初始化流程(进行中)

前言 之所以选择f_fs.c作为这系列的开始,因为usb在/driver/usb下面的内容实在太多太多,如果讲各种各样点会太分散了。所以决定切入点以实际使用为主,就是f_fs了,目前我了解这是做bulk裸传输使用的,具体的通信协议自己确定。 初始化部分 开始点从这个路径下说起, /drivers/usb/gadget/function/f_fs.c 然后f_fs.c第一个使用的是include/composite.h中的宏定义function #define DECLARE_USB_FUNCTION(_n

weixin_36406973的博客 1967

内存分配方式?

内存分配方式?

weixin_46319994的博客 411

智能检查点优化:动态频率与差异化存储的断点续训实践(模型训练丨容错机制丨人工智能)

本文深入探讨了大规模模型训练中智能检查点优化的核心实践。针对传统固定频率保存导致的存储浪费与恢复缓慢问题,提出了基于损失变化率的动态频率算法与分层差异化存储策略。通过PyTorch代码示例,详细展示了如何实现动态调度与增量保存,有效降低存储成本,并确保训练中断后能快速、可靠地恢复,显著提升训练任务的容错能力与经济效益。

weixin_29072299的博客 333

分配在堆上还是分配在栈上及其区别

1.问题情景:由c#的string的变量分配在堆上,而不是分配在栈上引起的内容回顾。   注释:c#的string类型是引用类型,不同于int,float等值类型。 2.解析: 转载:http://blog.sina.com.cn/s/blog_74f586a50100sv6m.html 1、栈区(stack) 由编译器自动分配释放,存放函数的参数值,局部变量的值等,内存的分配...

weixin_30246221的博客 857

VASP吸附计算1:CO在Pt(111)面的吸附|Materials Studio建模

介绍用VASP计算CO-Pt体系电荷密度的流程。

weixin_50519490的博客 5945

海康工业相机LabVIEW二次开发实战——参数配置优化与图像高效存储

本文详细介绍了海康工业相机在LabVIEW平台上的二次开发实战,重点解析了相机参数配置的优化方法与图像高效存储策略。通过剖析曝光、增益、触发模式等核心参数的设置逻辑,并结合生产者-消费者设计模式,指导开发者构建稳定、高效的图像采集与异步存储流程,有效提升工业视觉项目的开发效率与系统稳定性。

weixin_29306317的博客 221

spring IOC容器管理——基于XML方式

spring IOC容器管理——基于XML方式 一、IOC容器 ​ 1、什么是IOC(控制反转) ​ (a)把对象创建和对象之间的调用过程,交给Spring进行管理 ​ (b)使用IOC目的:为了降低耦合度 ​ 2、IOC底层 ​ (a)xml解析+工厂模式+反射 ​ 3、Spring提供的IOC容器实现的两种方式(两个接口) ​ (a)BeanFactory接口:IOC容器基本实现是Spring内部接口的使用接口,不提供给开发人员进行使用 区别:加载配置文件时候不会创建对象,在获取对象时才会创建对象。 ​

qq_43828345的博客 358

损失函数大汇总(五十三)(Boundary Loss附公式推导与代码)

摘要 Boundary Loss是一种针对图像分割任务的新型损失函数,特别适用于高度类别不平衡的细粒度分割场景。与传统的区域型损失(如Dice、交叉熵)不同,它通过计算预测边界与真实边界之间的距离来优化模型,更关注边界精度而非区域重叠。核心思想是利用距离图(distance map)将边界约束转化为区域积分形式,实现端到端训练。具体实现中,损失函数为预测概率与距离图的点积,梯度仅取决于距离图,具有明确的空间引导性。相比区域型损失,Boundary Loss能更好处理小目标、细长结构等边界敏感任务,可与Dic

悦学共鸣,温柔以待,汇聚光芒,共同成长 240

关于IOC的使用

IOC两个容器IOC容器中Bean的作用域Bean后管理器定义Bean的生命周期始末基于XML的DI注入分类集合属性注入对于域属型的自动注入SPEL注入对于内部bean的注入为应用指定多个Spring配置文件基于注解的DI定义Bean@componentBean的作用域@Scope基本类型属性注入@Value域属型注解Bean的生命始末@PostConstruct与@PreDestroy使用JUn...

qq_43576155的博客 269

思远耳机充电芯片-SY5501

BES2700ZP-本文档介绍了思远耳机充电芯片SY5501的使用方法,包括芯片引脚功能说明(VCC、IRQ、RST等)、通信机制(VIN引脚的双重作用)以及相关注意事项。提供了代码片段、调试日志和电路图作为参考,并列出了待解决的问题清单。该芯片需要配合特定充电仓使用,支持私有协议和用户自定义通信功能。

云淡风轻 123

基于YOLOv8改进的目标检测算法研究及毕设实现全流程指南

在PyTorch中定义改进后的YOLOv8模型。首先,导入相关的库和模块,然后定义骨干网络、颈部和头部的结构。# 这里定义骨干网络的结构,例如使用改进的CSP结构# 卷积层、批归一化层、激活函数等# 更多的卷积层、批归一化层、激活函数等# 其他层...# 其他层的输出...然后,定义颈部和头部的结构,类似地,将注意力机制和特征增强方法集成到模型中。本毕设以YOLOv8为基础,通过集成注意力机制、应用特征增强技术等方法,改进了目标检测算法。

m0_64661770的博客 325

Object是引用数据类型,且只存储于堆(heap)中说法正确吗?(赋值、浅拷贝和深拷贝)

Object是引用数据类型,且只存储于堆(heap)中说法正确吗?(深拷贝浅拷贝)

cjn308408的博客 944

YOLO11改进|注意力机制篇|引入ELA注意力机制

YOLO11添加ELA注意力机制

A1983Z的博客 3648

为什么 Objective-C 对象存储在堆上而不是栈上

为什么 Objective-C 对象存储在堆上而不是栈上 一、什么是栈对象和堆对象 在Objective-C 中,对象通常是指一块有特定布局的连续内存区域。我们通常这样创建一个对象: NSObject *obj = [[NSObject alloc] init]; 这行代码创建了一个 NSObject 类型的指针 obj 和一个 NSObject 类型的对象,obj 指针存储在栈上,而...

Philm_iOS的博客 1836

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Objective-C学习笔记——块(block)

Objective-C学习笔记——块(block) 1.理解块的概念 块的语法结构如下 return_type (^block_name)(parameters) 块在声明它的范围里,任何变量都会被其捕获,即在这个范围里的全部变量都可以在块中使用。默认情况下,块所捕获的变量是不可以在块之中修改的,否则编译器会报错。若想修改则需要在该变量声明时在声明前加上__block修饰符。 内联块(inline block):作为方法调用的块不赋值给局部变量而是直接内联在方法调用之中。OC在引入块这一特性之前需要通过传

weixin_43809624的博客 459

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Objective-C基础(三)

而meta-class对象只存储类方法信息,因此meta-class的属性信息、协议信息、成员变量信息这些字段虽然存在,但是。sizeof是运算符,其返回值在编译期就已经决定了,例如如果传入的是一个bool类型的变量,就返回1,int返回4。,即这个结构体是可读写的,这也表明类的方法列表,属性列表,协议列表,是可读写的,这在之后的Runtime中会有相应的体现,大家可以先记住。如果对这个对象调用malloc_size方法,由于对象存储在堆上,因此需要做系统内存对齐(扩展为16的倍数),返回的结果为。

weixin_41994174的博客 222

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

【Objective-C】类与结构体的区别

只能在类里面写方法,不能在结构体里面写方法类——对象,结构体——值类——引用类型 位于栈上的指针(引用)位于堆上的实体对象 结构体——值类型 实例直接位于栈中 拷贝行为: classname *a = b; a和b都是指针(指针存储在栈上),都指向同一个对象(对象存储在堆中),对象的值改变,a和b同时改变structname a = b; 进行的是值拷贝(复制值后存储在栈上)

柳婼 の blog 1135

iOS之《Effective Objective-C 2.0》读书笔记(1)

1. 前言 2. 第一条了解Objective-C的起源 3. 延伸学习

雨花石的博客 507

九、内存分配(堆和栈)

移动设备的内存及其有限,每一个APP所能占用的内存是有限制的。 什么行为会增加APP的内存占用? 创建一个oc对象 定义一个变量 调用一个函数或者方法 复制代码 内存管理范围 任何继承了NSObject的对象 对其它非对象类型无效 简单来说: 只有oc对象需要进行内存管理 非oc对象类型比如基本数据类型不需要进行内存管理 复制代码 引入堆和栈的概念 所以问题就来了,为...

weixin_34341229的博客 159

Objective-C 之深拷贝和浅拷贝

3月箴言 人的思想是了不起的,只要专注于某一项事业,就一定会做出使自己感到吃惊的成绩来。—— 马克·吐温 1、iOS中关于深拷贝和浅拷贝的概念 浅拷贝:浅拷贝并不拷贝对象本身,只是对指向对象的指针进行拷贝深拷贝:直接拷贝对象到内存中一块区域,然后把新对象的指针指向这块内存 在iOS中并不是所有对象都支持Copy和MutableCopy,遵循NSCopying协议的类可以发送Copy协议...

weixin_33973609的博客 254

iOS开发中的内存分配(堆和栈)

移动设备的内存及其有限,每一个APP所能占用的内存是有限制的。 什么行为会增加APP的内存占用? 创建一个oc对象 定义一个变量 调用一个函数或者方法 内存管理范围 任何继承了NSObject的对象 对其它非对象类型无效 简单来说: 只有oc对象需要进行内存管理 非oc对象类型比如基本数据类型不需要进行内存管理 引入堆和栈的概念 所以问题就来了,为什么OC对象需要进行内存管理,而其...

weixin_33938733的博客 147

关于内存管理

什么是内存管理?是指软件运行时对计算机内存资源的分配和使用技术。其最主要的目的就是如何高效、快速的分配,并且在适当的时候释放和回收内存资源。 内存分配 在 iOS 中数据是存在堆和栈中的,然而我们的内存管理管理的是堆上的内存,栈上的内存并不需要我们管理。 非OC对象(基础数据类型)存储在栈上 OC对象存储在堆上 引用计数 引用计数是计算机编程语言中的一种内存管理技术,是指将资源(可以是对...

蜗牛的小房子 190

OC基础:内存结构与内存管理

OC基础:内存结构、内存管理、循环引用解决

a690197843的博客 660

Block实现-block存储域

Block转换为Block的结构体类型的自动变量,_block变量转换为_block变量的结构体类型的自动变量,所谓结构体类型的自动变量,即栈上生成的该结构体的实例。我们已经了解了block时oc对象,该block的类为_NSConcereteStackBlock,虽然该类并没有出现以变换源代码中,还有很多类似的类 _NSConcereteStackBlock 对象存储域在栈上 _NSC...

weixin_30552635的博客 85

ARC & MRC下string内存管理策略探究

下面就让我们一起看看NSString和NSMutableString在MRC下执行retain,copy,mutableCopy,以及ARC下不同的修饰__weak, __strong修饰赋值究竟发生了什么。   一、验证代码如下:  testStringAddress   代码中最开始定义了两个int型的变量,主要是为了打印出当前函数的栈地址,顺便验证了一下内存中栈是从高

lijiexiaoge的博客 427

Oc语言学习 —— 重点内容总结与拓展(下)

所以对于常量字符串的单例来说,仅仅存储地址,哪怕后面再创建新的字符串,但是只要内容相同,str对象里面存储的该字符串的地址都是一样的。大家哪怕内容相同,自己也是自己的。协议体现的是一种规范,定义协议的类可以把协议定义的方法委托给实现协议的类,这样可以让类定义具有更好的通用性,因为具体的动作将由该协议的实现类去完成。对于ConstantString,我们想查看内存分布情况,直接打印str得到的其实是str这个指针的地址信息,前8位是isa指针,17到24位是对应常量字符串的地址,25~32位是字符串的长度。

2402_86720949的博客 1127
上一篇: NSTaggedPointerString,__NSCFConstantString,__NSCFString和NSString的关系?NSString为什么用copy?
下一篇: 关于iOS 9之后通知的移除
LIN1986LIN
博客等级 码龄19年 17粉丝 93原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值