《iOS高级编程》中的内存管理基础【MRC】

2023-05-16

文章目录

  • 关于我的仓库
  • 前言
  • 准备工作
  • 内存管理(引用计数)的思考(p.2 ~ p.13)
    • 记住一张表格
    • 内存管理四大原则
      • 注意点
      • 疑点
  • 内存管理四兄弟【alloc】【retain】【release】【dealloc】(p.13 ~ p.20)
    • 说明
    • alloc
      • 计数
      • GNU实现
    • retainCount
    • retain
    • release&&dealloc
    • Apple实现
      • 引用计数&&哈希表
  • autorelease(p.20 ~ p.28)
    • arrayWithCapacity
    • 苹果实现
      • SEL&&IMP
      • push&&pop
  • 困惑

关于我的仓库

  • 这篇文章是我为面试准备的iOS基础知识学习中的一篇
  • 我将准备面试中找到的所有学习资料,写的Demo,写的博客都放在了这个仓库里iOS-Engineer-Interview
  • 欢迎star??
  • 其中的博客在简书,CSDN都有发布
  • 博客中提到的相关的代码Demo可以在仓库里相应的文件夹里找到

前言

  • MRC是在ARC没有出现前的荒蛮时代采用的管理引用计数的方法,学习它主要是为了能理解引用计数的问题,为理解属性关键字,ARC,特别是ARC中的Autoreleasepool

准备工作

  • 开启MRC

    • 将项目改为MRC的方法(有图有真相)
  • 阅读《Objective-C 高级编程》中的p.1 ~ 29

内存管理(引用计数)的思考(p.2 ~ p.13)

记住一张表格

对象方法引用计数
生成对象并自己持有alloc/copy变成 1
持有对象retain++
释放对象release
废弃对象dealloc
生成但不持有对象array-1

内存管理四大原则

  1. 自己生成的对象,自己持有
  2. 非自己生成的对象,自己也能持有
  3. 不再需要自己持有的对象时释放
  4. 非自己持有的对象无法释放

注意点

  1. 我们不要纠结于引用计数的事,引用计数本质上只是一个帮助我们观察状态的工具,合理的思考方式就是按照上面四个原则去思考
  2. 持有到底是什么意思?持有的本质其实就是强引用
NSObject *obj1 = [[NSObject alloc] init];
//这句话里面我们在内存中分配了一块空间,存放一个NSObject对象,而我们的obj1是一个指针,指向该对象
//同时,obj1也强引用即持有了该对象,会使该对象的引用计数+1
//强引用即__strong,在OC中,不佳特别的修饰符会默认为强引用

疑点

  1. 这里按照强应用即持有的观点,obj1持有了该对象,这和书上的是对象的使用环境(编程人员自身)持有对象这一观点不符
  2. 但是如果按照书上的理解,实在很难想象为什么可以对象被多个持有

内存管理四兄弟【alloc】【retain】【release】【dealloc】(p.13 ~ p.20)

说明

  • 《高级编程》中在p.13页提到Apple没有公布NSObject中的源代码,也就是说看不到这四个方法的具体实现

  • 这么说其实是因为该书写于2013年,现在都2019年了,这部分的源码苹果其实已经早就公开了

  • 也就是说从13到20这么多页其实都是作者通过研究GNUstep推测的Apple的实现

  • 当然,这些依然还是有意义的,作者对于其中原理的讲解很是透彻

  • 但除此之外在认真研究下Apple公司真正的实现方式也是很有必要的

alloc

  • 在ARC中,我们是无法调用【retain】【release】【dealloc】这几个操作的,只有alloc是可以使用的
  • ARC与MRC相比,我们可以理解成,ARC帮助我们在适当的使用调用了上面几个方法,对引用计数进行了管理

计数

  • 对某一对象进行alloc初始化,在打印它的retainCount
		NSObject *obj = [[NSObject alloc] init];
   [Print printRetainCount:obj];
  • 结果为1,认为obj持有了这个对象

GNU实现

  • 它存储引用计数的方式是存在一个结构体里面,该结构体里只有一个NSInteger类型的数据retained,也就是引用计数
//p.15		
struct obj_layout {
    NSUInteger retained;	//引用计数
};
 
+ (id)alloc
{
    int size = sizeof(struct obj_layout) + 对象大小;	//size大小就是对象的是实际大小加上一个结构体
    struct obj_layout *p = (struct obj_layout *)calloc(1, size);
  	return (id)(p + 1)	//这里应该是返回该对象的内存地址,它返回了p+1,也就是说返回了掠过引用计数结构体的内存地址
		//也就是说引用计数存在了对象的头地址里
}

retainCount

  • 首先,我们看到初始化的时候其实是将里面的数据全部记为0的,在进行了alloc之后,按道理引用计数应该是0
  • 而我们调用retainCount打印出来却会是1
//p.15
- (NSUInteger) retainCount
{
    return NSExtraRefCount(self) + 1;
}
 
inline NSUInteger
NSExtraRefCount(id anObject)
{
    return ((obj_layout)anObject)[-1].retained;	//这个[-1]实在是有点迷人,我的理解是我们这里的anObject是对象的内存地址,而上面我们看到了刨掉引用计数才是我们的内存地址,对于对象的地址是[0],[-1]是它的前一个,也就是引用计数了
}
  • return的是self+1,所以打印出来会是1

retain

  • retain要注意的是判断了下引用计数是否到达了最大值(防止引用计数溢出)

release&&dealloc

  • release和retain的套路基本一致,不同的是它要判断的是是否到0
  • 当引用计数是1的时候,进行release会自动调用dealloc
  • 这里的dealloc是直接将该内存空间free了

Apple实现

  • Apple的实现和前面分析的其实没什么区别,这里关键的其实是发现了Apple存储引用计数的方式——哈希表(看到散列表突然懵逼的同学请回去重修下数据结构?)

引用计数&&哈希表

  • 先大概回顾下哈希表:哈希表(hash table,也叫散列表),是根据(key)直接访问访问在内存储存位置的数据结构。
    哈希表本质是一个数组,数组中的每一个元素成为一个箱子,箱子中存放的是键值对。根据下标index从数组中取value。关键是如何获取index,这就需要一个固定的函数(哈希函数),将key转换成index。不论哈希函数设计的如何完美,都可能出现不同的key经过hash处理后得到相同的hash值,这时候就需要处理哈希冲突。
  • 这里先不讨论Apple使用何种方式解决的哈希冲突,单单将该表理解成数组的话,其实就是一个键是对象的地址,值是引用计数的表,两者一一对应
  • 而这样存储的优点和GNU比也很好理解,就是别把鸡蛋放在一个篮子里,用哈希表存储,两者相互独立,内存损坏也不影响查引用计数

autorelease(p.20 ~ p.28)

  • autorelease我的理解就是它使用了一个NSAutoreleasePool这个来进行辅助,帮助判断何时调用release方法
  • 进行[pool drain]的时候,则同时会进行release
  • 同时如果生成了好几个pool,使用的会是最近生成的一个
  • pool等于是一个数组,每个调用了autorelease的对象,被添加到这个数组里面

arrayWithCapacity

 NSMutableArray *orderIds = [NSMutableArray arrayWithCapacity:self.dataArray.count]
 初始化可变数组对象的长度,如果后面代码继续添加数组超过长度以后长度会自动扩充.
初始化方法 capacity后的NSUInteger代表了开辟内存的一个单位初始在内存中开辟5个内存,如果之后数组元素多余5个,则会再开辟新的5*2个新的内存,[考虑到数组的连续内存的特性]单位是以5,把之前的5个元素的内容拷贝到新的十个新的内存里面,把第六个也放进去,然后释放初始状态创建的内存5个最后得到了一块够用的连续的内存5*2
   
 NSMutableArray *array = [NSMutableArray alloc] init];//这并不是一个好方法

 NSMutableArray *array = [NSMutableArray arrayWithCapacity:10]; //创建一个可变的数组长度为10

区别:

1. arrayWithCapacity是类autorelease的.

2. [NSMutableArray alloc]initWithCapacity需要自己release.

苹果实现

SEL&&IMP

  • SEL : 类成员方法的指针,但不同于C语言中的函数指针,函数指针直接保存了方法的地址,但SEL只是方法编号。
  • 一个函数指针,保存了方法的地址
  • 每一个继承于NSObject的类都能自动获得runtime的支持。在这样的一个类中,有一个isa指针,指向该类定义的数据结构体,这个结构体是由编译器编译时为类(需继承于NSObject)创建的.在这个结构体中有包括了指向其父类类定义的指针以及 Dispatch table. Dispatch table是一张SEL和IMP的对应表。
  • 也就是说方法编号SEL最后还是要通过Dispatch table表寻找到对应的IMP,IMP就是一个函数指针,然后执行这个方法

push&&pop

  • Apple实现从push,pop这两个方法命名也就可以看出他本质上和前面一样,就是在数组里面进行压入压出操作

困惑

  [obj alloc]
  [obj release]
  • 执行完release后,打印计数依然是1,此处存疑
    • 我们此时引用计数由1到0,应该自动调用了dealloc,而此时依然能打印计数,也就是对其进行了访问
      • 在此基础上发现此时可以对obj在访问一次,不管是进行release,访问计数,都可以,但是只有这一次,在进行访问就会报错
      • 另外很神奇的是retain不在此列,它不占这个名额,你可以进行任意多次的retain操作,也不会报错
      • 我们可以推测retain确实是指判断最大值的
    • 这是在MRC中进行的操作,我们如果再来一句dealloc,会提示obj已经被free,然后依然能打印出obj的地址
  • 我们可以推测出release确实自动调用了dealloc,但是也不是像GNU里面一样简单的free了
  • 当然,使用p.20的哈希表可以解决部分疑惑,我们唯一可以明确的就是计数1到0的时候自动dealloc了,且绝对没有将obj指针free。显然这中间到底发生了什么神秘的变化需要我们从源码中探究了
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《iOS高级编程》中的内存管理基础【MRC】 的相关文章

  • 由于没有系统应用程序而终止

    这个错误让我发疯 我有一个按钮 我想移动到视图控制器 如果我将其设置为初始视图控制器 我可以打开该视图控制器 但现在我通过单击按钮来调用它 视图控制器包含一个自定义视图 我在绘制矩形上放置了一个断点 当绘制矩形完成时 模拟器终止 我在屏幕上
  • IOS 8 CLLocationManager 问题(授权不起作用)

    import MyLocationViewController h define NSLog FORMAT printf s n NSString stringWithFormat FORMAT VA ARGS UTF8String int
  • 通过 iBeacon 监控和测距与 CoreBluetooth scanForPeripheralsWithServices 检测信标

    对于 iOS 对想要扫描 BLE 信标 外设的应用程序施加的限制 存在很多困惑 在阅读了几个博客和 Stack Overflow 答案后 我想看看我是否正确理解了所有问题 如果有什么理解错误或者遗漏的地方请指正 我仅指 iOS 7 及更高版
  • Firebase Analytics DebugView 收集的事件数据不完整

    我正在将事件发送到 Firebase Analytics 并发现 DebugView 中缺少部分事件参数 下面是发送到 Firebase 的两个相同事件 我检查了 Xcode 调试控制台中是否存在所有参数 缺失的参数似乎是随机的 有时根本没
  • 从 Firestore Swift 获取文档 ID

    我正在尝试从中获取文档IDFirestore通过执行这样的查询 func updateStatusInFirestore let orderid saleOrder first Orderid print orderid let setti
  • 将 Xcode 4.5 新 XIB 文件恢复到 iOS<6

    我已经安装了Xcode 4 5 with iOS6 SDK以及其他用于测试目的的旧 SDK 从 4 3 到 6 0 很美 但是有一个BIG问题 生成一个新的 XIB 文件以兼容 iOS6 这是一个问题 因为我的应用程序需要运行在旧设备 不只
  • Cordova iOS 启动画面

    我无法让闪屏停留在页面加载之前 我已经阅读了谷歌搜索第一页 第二页和第三页中的所有条目 我阅读了 stackoverflow 上发布的所有问题 包括一个很好的解释 https stackoverflow com questions 1800
  • 在 SwiftUI 中,如何执行手势但将手势转发到其后面的视图?

    我正在创建一个工具提示系统 如果用户触摸工具提示之外的任何地方 我想关闭工具提示 我希望这样在工具提示之外的触摸既可以消除工具提示 又可以激活用户点击的任何控件 因此 您可以打开一个工具提示 然后仍然单击工具提示外部的按钮 并在第一次点击时
  • React Native ios运行问题

    我是反应本机和运行新手yarn ios我的 React Native 项目不断失败并出现以下错误 构建失败 以下构建命令失败 编译C Users gift Library Developer Xcode DerivedData gainer
  • Mapkit 在 IOS 13 中使用过多的 CPU

    最近 在一些用户更新到 iOS 13 x 后 我的 iOS 应用程序开始频繁崩溃 在 iOS 12 x 中没有出现该问题 我正在使用 Mapkit 渲染一些 MKPolygons 和 MKPolylines MKPolylines 被删除并
  • Cordova - 启动后出现白屏,控制台中没有例外

    我已经离开我的 Cordova 应用程序一段时间了 但昨天刚刚进行了一次新的克隆 发现它出现了 死机白屏 症状 启动画面显示 程序加载 然后我就得到一个空白屏幕 更多细节 CLI 科尔多瓦 6 1 1 安卓 5 1 1 ios 4 1 1
  • 从 plist 文件中解码数据

    我丢失了在 Macbook Air 上用 Textwrangler 编写的文本文件中的一些数据 我在扩展名为 plist 的文件中找到了其中一些 文件是用 xml 编写的 如下所示
  • 将 UIRefreshControl 用于 UIWebView

    我在 iOS 6 中看到了 UIRefreshControl 我的问题是是否可以通过下拉来刷新 WebView 而不是像在邮件中那样让它弹出 我使用 rabih 的代码是 WebView UIRefreshControl refreshCo
  • Swift 3 中数组的 indexOf(_:) 方法的替换

    在我的项目 用 Swift 3 编写 中 我想使用从数组中检索元素的索引indexOf 方法 存在于 Swift 2 2 中 但我找不到任何替代方法 Swift 3 中是否有任何好的替代方法或类似的方法 Update 我忘记提及我想在自定义
  • 如何将 CIFilter 输出到相机视图?

    我刚刚开始使用 Objective C 我正在尝试创建一个简单的应用程序 它显示带有模糊效果的相机视图 我得到了与 AVFoundation 框架一起使用的相机输出 现在 我正在尝试连接 Core 图像框架 但不知道如何连接 Apple 文
  • 无法识别的选择器调用静态 iOS 库中的类别方法

    我正在使用一些第三方软件来帮助使用 Xcode 4 3 2 编写 iPad 应用程序 该软件是开源的 通常经过设置 因此其代码将与开发人员为应用程序编写的任何代码一起编译 因为我在很多地方使用该软件 所以我决定将其构建为 iOS 模拟器的静
  • 禁用 iPhone 4S / 新 iPad 键盘上的听写按钮

    我们的应用程序是一个医疗保健应用程序 我们的应用程序中有一个符合 HIPAA 标准的语音识别器 所有听写都可以通过它进行 医院不希望医生意外开始与不符合 HIPAA 标准的 Nuance Dragon 服务器进行对话 因此 我正在寻找可以抑
  • 使用 NSFileHandle 覆盖数据

    使用 NSFileHandle 使用 truncateFileAtOffset 从文件末尾删除 n 个字符非常容易 void removeCharacters int numberOfCharacters fromEndOfFile NSF
  • 如何在运行仅 iPhone 应用程序(非通用)的 iPad 上的 UIPickerView 中支持 VoiceOver?

    一般来说 我会查看 Apple 的 UICatalog 示例代码以获取基本的 VoiceOver 支持 但示例代码中似乎有对 UIPickerViews 的 VoiceOver 支持 我是否需要在某处提供accessibilityLabel
  • 在iOS中设置框架的原点

    我正在尝试以编程方式设置框架的原点 Method1 button frame origin y 100 方法二 CGRect frame button frame frame origin y 100 我尝试了方法 1 但它不起作用 显示错

随机推荐

  • java虚拟机内存区域详解

    运行时数据区域 java虚拟机在执行java程序的过程中会把它管理的内存划分为若干个不同的数据区域 这些区域各自为政 xff0c 有的区域随着虚拟机进程的启动而存在 xff0c 而有些区域的生命周期则依赖于用户线程的生命周期 java虚拟机
  • csdn博客如何设置插入代码段的背景色

    最近在写博客的过程中发现自己插入的代码背景色全是米色的 xff0c 看起来很不爽 xff0c 就想要炫酷黑 经过一番研究 xff0c 终于发现如何设置插入代码段的背景颜色 xff1a 大体操作 xff1a 我的博客 gt 管理博客 gt 博
  • Exception in thread "main" java.lang.AbstractMethodError(Spring boot启动报错)

    具体报错 xff1a Exception in thread 34 main 34 java lang AbstractMethodError org springframework boot context config ConfigFi
  • Unregistering JMX-exposed beans on shutdown Disconnected from the target VM(springboot启动报错: )

    具体报错 xff1a Unregistering JMX exposed beans on shutdown Disconnected from the target VM address 39 127 0 0 1 52998 39 tra
  • python排序算法——归并排序(附代码)

    python排序算法 归并排序 文章目录 python排序算法 归并排序一 前言二 算法描述三 代码实现总结 一 前言 相关知识来自 python算法设计与分析 初级排序算法是指几种较为基础且容易理解的排序算法 初级排序算法包括插入排序 选
  • springboot使用fastjson解析json中文乱码问题

    使用fastjson解析json 需要在配置文件application properties中添加 spring http encoding force 61 true 否则 中文会出现乱码
  • springboot访问数据库报 Invalid bound statement (not found)

    如果在使用mapper xml时报了Invalid bound statement not found 的错误 xff0c 并且自己的mapper xml的路径和文件名都没有错误的情况 xff0c 很可能是因为没有在pom xml中配置如下
  • springboot项目创建

    1step xff1a File gt New gt project 2step 在New Project窗口选择Spring Initializer gt 选择default gt Next 3step 命名好项目名等信息点击 Next
  • SpringBoot存入数据库问号问题

    将resources目录下的application properties配置文件中数据库URL配置改为 xff1a spring datasource url 61 jdbc mysql localhost 3306 student use
  • 线程之wait/notify机制

    什么是等待 通知机制 举个栗子 xff1a 餐厅里 xff0c 厨师做好菜之后 xff0c 通知等待中的服务员就是一个简单的等待通知机制 wait xff1a wait方法代码的作用是使当前执行代码的线程进入等待 xff0c wait方法是
  • MySQL常用函数

    开始来熬大骨汤 xff0c 每周一碗大骨汤 xff0c 你也能变成光 于南京 MySQL函数的位置 xff1a 函数可以在select语句及其子句 xff08 例如 where order by having等 xff09 中 xff0c
  • idea的Browse Repositories搜索下载安装插件失败

    我在下载一个插件时 xff0c 刚开始出先下载超时time out xff0c 后来改了一些东西 xff0c 在Browse Repositories界面中 xff0c 直接搜不到插件了 xff08 一个插件都搜不到 xff09 xff0c
  • 安装goole浏览器报错

    1 下载一个谷歌的rpm包 xff0c 配置repo源 root 64 physical cat etc yum repos d google chrome repo google chrome name 61 google chrome
  • 经典编程书籍(C++, 网络, Windows, Linux)

    导读 xff1a 书单中列举的都是相关领域的经典书籍 xff0c 必读之作 此书单的编辑参考了很多网站 xff0c 包括一些名家的推荐 xff0c 例如侯捷 xff0c 孟岩 xff0c 荣耀 xff0c 潘爱民等等 xff0c 在此也向这
  • 域名+解析+GitHub来搭建自己的个人网站

    摘要 文章主要介绍域名解析 xff0c 使用GitHub上的开源项目来搭建一个个人博客 并不需要购买服务器 xff0c 没有数据库访问 xff0c 适合搭建自己的博客或者个人网站 xff0c 不适合大型网站 购买域名 可以在万网上面注册一个
  • Linux服务器批量管理工具 - TeamRemote

    产生背景 xff1a 当前网上流行的一些优秀且功能强大的工具 xff0c 如puppet xff0c dsh xff0c parallel ssh xff0c ansible等 但不得不说 xff0c 他们的上手起来太复杂了 xff0c 导
  • C语言书籍推荐

    C 语言书籍推荐 宗旨 xff1a 技术的学习是有限的 xff0c 分享的精神是无限的 一 基础 1 C语言入门很简单 零起点学通C语言 xff08 多媒体范例教学 xff09 C语言从入门到精通 2 C程序设计语言 第2版 新版 C程序设
  • MATLAB排序函数

    MATLAB排序函数 2011 06 29 13 02 08 源自网络 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
  • 具有FTP、FTPS和sftp功能的文本编辑器——EditPlus功能介绍

    EditPlus是Windows的文本编辑器 xff0c 具有内置的FTP FTPS和sftp功能 虽然它可以很好地替代记事本 xff0c 但它也为网页作者和程序员提供了许多强大的功能 功能特征 01 语法高亮 EditPlus 默认支持H
  • 《iOS高级编程》中的内存管理基础【MRC】

    文章目录 关于我的仓库前言准备工作内存管理 引用计数 的思考 p 2 p 13 记住一张表格内存管理四大原则注意点疑点 内存管理四兄弟 alloc retain release dealloc p 13 p 20 说明alloc计数GNU实