iOS开发——MRC(手动内存管理)

  • 内存分配区域
    • 栈区
    • 堆区
      • 总结
    • 常量区
      • 总结
    • 代码区
      • 总结
    • 全局区
    • 关于如何查看一个对象在堆区 / 栈区
  • 需要知道的知识
  • 手动引用计数MRC
    • 四个法则
    • 非自己生成的对象,且该对象存在,但自己不持有
    • dealloc
    • retainCount
      • 不要使用retainCount

我们要想了解内存管理的知识前,必须先搞明白计算机的内存分配以及计算机是如何处理内存的。

内存分配区域

内存指的就是RAM(random access memory),内存分配区域主要分为五个区:栈区(系统管理的地方)、堆区(程序员控制的地方)、静态区(全局区)、常量区、代码区。

栈区

栈区(stack)由编译器自动分配并释放,存放的是函数的参数值局部变量基本类型的变量和对象引用类型方法调用的实参也是保存在栈区。所以我们可以把栈看做一个临时寄存交换的内存区
栈是系统数据结构,对应线程/进程是唯一的。优点是快速高效,缺点是有限制,数据操作不灵活。

堆区

由程序员分配(malloc、new)和释放(delete),主要存在new构造的对象和数组如果不释放,可能造成内存泄露,程序结束时可能会由操作系统回收。
堆向高地址扩展的数据结构,是不连续的内存区域。 程序员负责在何时释放内存,在ARC程序中,计数器为0的时候,在当次的runloop结束后,释放掉内存。堆中的所有东西都是匿名的,这样不能按名字访问,而只能通过指针访问
对于堆来讲,频繁的new/delete势必会造成内存空间的不连续性,从而造成大量的碎片 ,使程序效率降低。

总结

  1. 灵活方便,数据适应面广泛,但是效率有一定降低。[顺序随意]
  2. 堆是函数库内部数据结构,不一定唯一。
  3. 不同堆分配的内存无法互相操作。
  4. 堆空间的分配总是动态的
  5. 虽然程序结束时所有的数据空间都会被释放回系统,但是精确的申请内存,释放内存匹配是良好程序的基本要素。

常量区

文字常量区存放常量字符,程序结束后自动释放,不允许修改。

总结

  1. 该区是编译时分配的内存空间,在程序运行过程中,此内存中的数据一直存在,程序结束后由系统释放。
  2. 存放常量:整型、字符型、浮点、字符串等。

代码区

存放函数体的二进制代码

总结

  1. 它是可执行程序在内存中的镜像。
  2. 代码段需要防止在运行时被非法修改,所以只允许读取操作,而不允许写入操作。

全局区

全局变量和静态变量都被分配到同一块内存。

全局区(静态区) (static): 全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量存放在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域,程序结束后由系统释放。

注意:全局区又可分为未初始化全局区(BSS段)和初始化全局区(DATA段)。

关于如何查看一个对象在堆区 / 栈区

  1. 你初始化方法以 new, alloc, retain,copy 开头都是在堆区,也包括被引用计数管理的对象。常量这些就会在栈区。简单一点,除去 NSString *aString = @“aaa” 这种编译时会转换为常量其它的 Objective-C 对象,理论上来说,都是在堆区。此外,block 也是个例外,具体的可以去了解内存管理相关的知识点。
  2. 如果是在方法执行过程当中,定义在本地的原生类型(或者说值类型)。那么肯定它是在栈上。当函数执行结束时直接销毁。而其它的引用类型(或者oc中的interface)都是在堆上创建的,由ARC负责清理
  3. 一般情况下你可以打印出地址,判断他是在堆还是栈内存中,栈是高地址往下,堆是低地址往上,一般看到0x7fff…这种的肯定是是在栈中的,要注意一点的是TEXT段,像NSString这种基本放在TEXT段中,这种地址比堆地址更低,也很好区分;你可以去看一下内存是怎么划分的;

需要知道的知识

在学习MRC之前除了要了解计算机关于内存的分配区域之外,还需要了解一些知识,具体如下:

  1. 什么样的数据需要进行内存管理?任何继承了NSObject的对象都需要进行内存管理,而那些非对象类型的数据比如:int、char、double、struct等等就不需要进行内存管理。
  2. 继承了NSObject的对象存储在操作系统的堆里,堆一般由程序员分配释放,程序结束时可能有OS回收。
  3. 非OC对象一般放在操作系统的栈里,栈由操作系统自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈(先进后出)。

另外现在我们的XCode默认为ARC环境,如果需要MRC我们需要手动设置:

手动引用计数MRC

我们要想手动进行内存管理就需要知道哪些情况会引起计数的变化:

对象被废弃对象所占内存解除分配 并放回“可用内存池中”。

四个法则

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

/** 自己生成并持有该对象*/id obj0 = [[NSObeject alloc] init];id obj1 = [NSObeject new];

非自己生成的对象,自己也能持有。

/** 持有非自己生成的对象*/
id obj = [NSArray array]; // 非自己生成的对象,且该对象存在,但自己不持有
[obj retain]; // 自己持有对象

不在需要自己持有对象的时候,释放。

/** 不在需要自己持有的对象的时候,释放*/
id obj = [[NSObeject alloc] init]; // 此时持有对象[obj release]; // 释放对象
/** 指向对象的指针仍就被保留在obj这个变量中* 但对象已经释放,不可访问*/

非自己持有的对象无需释放。

/** 非自己持有的对象无法释放*/
id obj = [NSArray array]; // 非自己生成的对象,且该对象存在,但自己不持有[obj release]; // ~~~此时将运行时crash 或编译器报error~~~ 非 ARC 下,调用该方法会导致编译器报 issues。此操作的行为是未定义的,可能会导致运行时 crash 或者其它未知行为

非自己生成的对象,且该对象存在,但自己不持有

这句话听着很绕口,但其实很好理解,但是要了解这句话首先我们要知道一个叫autorelease的东西。
所谓autorelease就是自动释放,注意:autorelease和ARC 是完全不同的两个东西,没有任何联系。
类似于C语言的局部变量,超出变量作用域,局部变量就会被废弃,不可再访问。
autorelease会在超出作用域时,调用对象的release实例方法,与C语言不同的是,编程人员可以设定变量的作用域,类似下图:

我们来看一下代码:

- (id) getAObjNotRetain {id obj = [[NSObject alloc] init]; // 自己持有对象[obj autorelease]; // 取得的对象存在,但自己不持有该对象return obj;
}

取得的对象存在,但自己不持有该对象,那么该对象被谁持有了呢?
调用 autorelease 方法,就会把该对象放到离自己最近的自动释放池中(栈顶的释放池,多重自动释放池嵌套是以栈的形式存取的),即:使对象的持有权转移给了自动释放池(即注册到了自动释放池中),调用方拿到了对象,但这个对象还不被调用方所持有。

那么问题来了,autorelease和release有什么区别?
Autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该对象放入了当前的Autorelease pool中,当该pool被释放时,该pool中的所有对象会被调用Release

autorelease pool的作用就是来避免频繁申请/释放内存(就是pool的作用了)。
[NSMutableArray array][NSArray array]都可以取得谁都不持有的对象,这些方法都是通过 autorelease 实现的。

autorelease 方法不会改变调用者的引用计数,它只是改变了对象释放时机,不再让程序员负责释放这个对象,而是交给自动释放池去处理。等到自动释放池销毁时,所有对象统一都会被释放。

看一下下面的代码:

int main(int argc, const char * argv[]) {NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];id obj = [[NSObject alloc]init];NSLog(@"%lu",(unsigned long)[obj retainCount]);[obj autorelease];NSLog(@"%lu",(unsigned long)[obj retainCount]);[pool drain];NSLog(@"%lu",(unsigned long)[obj retainCount]);return 0;
}

它的打印结果是什么呢?

首先生成对象引用计数+1,接着将持有权转移给了自动释放池引用计数不变,然后销毁池子,池子中的所用对象调用release,引用计数-1,变为0。
假如我将代码改成如下:

int main(int argc, const char * argv[]) {NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];id obj = [[NSObject alloc]init];NSLog(@"%lu",(unsigned long)[obj retainCount]);[obj autorelease];NSLog(@"%lu",(unsigned long)[obj retainCount]);[pool drain];NSLog(@"%lu",(unsigned long)[obj retainCount]);[obj autorelease];NSLog(@"%lu",(unsigned long)[obj retainCount]);return 0;
}

这样会发生什么呢?这样的话程序会崩溃,因为池子销毁的时候,池子里的所有对象 都被释放了,调用一个不存在的对象,程序当然会崩溃。

dealloc

NSObject 协议中定义的内存管理方法与遵守这些方法命名约定的自定义方法的组合提供了用于引用计数环境中的内存管理的基本模型。NSObject 类还定义了一个dealloc 方法,该方法在对象被销毁时自动调用,也就是在引用计数为0时。

retainCount

不要使用retainCount

看一段代码:

    NSNumber *number = [NSNumber numberWithInt:1];NSLog(@"retainCount = %lu",[number retainCount]);

我们看一下打印结果:

结果大的离谱啊,查阅了一下官方的文档,第一句就是“Do not use this method.”,后面给出了说明,因为Autorelease pool的存在,对于内存的管理会相当复杂,retainCount就不能用作调试内存时的依据了。

iOS开发——MRC(手动内存管理)相关推荐

  1. 【iOS开发】——MRC(手动内存管理)的一些补充

    [iOS开发]--MRC(手动内存管理)的一些补充 前言 野指针与空指针 多个对象内存管理的思想 玩家没有使用过房间 一个玩家使用一个游戏房间的情况 一个玩家使用一个房间 r 后,换到另一个房间 r2 ...

  2. 拒绝内存泄露,谈一些纯代码iPhone开发中的内存管理

    笔者刚从学校出来,算上实习期,接触iPhone开发已有半载,也算是对iPhone(iOS)开发中的内存管理有了些认识,由于开发中一直没有用Interface Builder,本文就谈谈纯代码iPhon ...

  3. 理解 iOS 和 macOS 的内存管理

    在 iOS 和 macOS 应用的开发中,无论是使用 Objective-C 还是使用 swift 都是通过引用计数策略来进行内存管理的,但是在日常开发中80%(这里,我瞎说的,8020 原则嘛?)以 ...

  4. 使用jemalloc在Go中进行手动内存管理

    目录 通过Cgo创建内存 jemalloc 在字节片上放置Go结构 用分配器摊销Calloc的成本 明智地参考 处理分配的GB 排序可变长度数据 捕捉内存泄漏 结论 推荐阅读 曼尼斯·赖·贾 因(Ma ...

  5. Go 语言中手动内存管理

    2019独角兽企业重金招聘Python工程师标准>>> Go 语言是自带GC的, 相对C语言等的手动内存管理省事很多, 弊端便是会消耗更多的内存, 以及在GC时导致整个程序的停顿. ...

  6. iPhone开发资料之内存管理 ,循环引用导致的内存问题

    iPhone开发资料之内存管理 ,循环引用导致的内存问题 https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual ...

  7. go 1.20 手动内存管理arena

    go1.20新特性,手动内存管理 测试特性安装go1.20rc go install golang.org/dl/go1.20rc1@latest go1.20rc1 download 使用go1.2 ...

  8. 关于IOS的多任务以及内存管理

    看了很多FY为自己的可用内存是350MB还是380MB纠结.为了多优化出一点可用内存费脑筋.  IOS的任务管理和内存管理,跟windows是有很大差别的.很多FY习惯于用 windows的思维去看待 ...

  9. iOS之深入解析内存管理MRC与ARC机制

    一.内存管理 ① 什么是内存管理? 当我们编写程序的时候,会声明各种各样的变量,编写各种各样的代码,它们都会占用内存,但是并不是所有的代码和内存都是由我们进行释放. 内存分为 5 个区域:栈.堆.bs ...

最新文章

  1. 吹灭蛋糕上蜡烛的节能小车
  2. 查看 centos 版本
  3. 徐教授的对于商业模式创新的讲座!十分有用!
  4. AMD RX 8000系列将采用3纳米和5纳米工艺
  5. nginx 电子书_13本免费的电子书,拿走,不谢
  6. SQL文档阅读笔记-对水平分区和垂直分区理解
  7. 给你安利一款国产良心软件uTools
  8. 少一些计较多_做人,少一点套路,多一些真诚,少一点计较,多一些宽容
  9. printf(%d,5.01)和printf(%f,5)的输出结果
  10. v9更新系统后为何显示服务器连接,V9服务器
  11. 测试集的准确率为什么高于训练集的准确率?
  12. 广域网SDN功能与架构
  13. sqlserver行列转换,动态行转换
  14. 八数码问题matlab实现,A* 算法解决八数码问题 matlab
  15. 开源项目9GAG源码解析与Material改造(一)
  16. playwright 组件超时问题
  17. Day 5:自己编写的mysql类
  18. 十五、分布式相关理论
  19. 计算机少年宫活动计划,少年宫活动计划3篇
  20. android面试题库,Android面试试题及答案

热门文章

  1. Java中的时区转换
  2. Wi-Fi显示“无Internet,安全”是怎么回事?
  3. 一碗阳春面的故事--你还记得吗?
  4. 基于网易企业邮箱的JavaMail配置
  5. 美妙人生的关键在于你能迷上什么东西(转自刘慈欣球状闪电)
  6. 如何抢走亚马逊竞品手上的客户和流量?
  7. 一沙一世界(10亿光年),科学的图文介绍
  8. ASEMI整流桥MB10S参数,MB10S封装,MB10S规格书
  9. 剑指 Offer 11. 旋转数组的最小数字
  10. 后台页面-制作铃铛带数字消息提示样式