深入浅出iOS内存管理-技术创作101训练营

时间:2022-07-25
本文章向大家介绍深入浅出iOS内存管理-技术创作101训练营,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

前言

本文阅读建议
1.一定要辩证的看待本文.
2.本文所表达观点并不是最终观点,还会更新,因为本人还在学习过程中,有什么遗漏或错误还望各位指出.
3.觉得哪里不妥请在评论留下建议~
4.觉得还行的话就点个小心心鼓励下我吧~

在这篇文章中,我会总结最近对iOS开发内存课题的查阅和学习,文章的中心还是围绕着面试题来的。因为网上目前确实存在着很多的面试题和答案,但他们大多数都是拷贝粘贴,甚至答案都是错的。

当然,本文的答案也是仅供参考,更希望大家与我一起讨论修改。因为本文面试题过多,不再罗列所有面试题,采取循序渐进的方式讲解。

本文的用途是在于巩固复习,可能在部分之处有遗漏知识点,后期有时间将会单独抽出来去说。


面试题(OC内存)

  • OC如何对内存管理的,说说你的看法和解决方法?
  • iOS 是如何管理内存的?
  • 讲一下你对 iOS 内存管理的理解?
  • 介绍下内存的几大区域?

苹果官方文档-内存使用性能指南

苹果官方文档-高级内存管理编程指南

这几道题从14年到16年频频出现,记得自己背的回答是OC对象采取引用计数管理,遵循“谁创建,谁释放。谁引用,谁管理”的原则

简答(OC内存)

在iOS中,使用引用计数来管理OC对象的内存。在iOS 5之前是MRC(手动管理引用计数),iOS 5推出了ARC(自动管理引用计数)。ARC 是 LLVM 3.0 编译器的特性,用来自动管理内存。

在MRC下,当对象调用alloc、new、copy、mutableCopy方法会使该对象引用计数+1,在调用release,或者autorelease会对引用计数-1。如果一个对象的引用计数器为0,则系统就会自动调用这个对象的dealloc方法来销毁这个对象。

在代码中,一般使用dealloc方法来查看一个对象是否被回收,如果没有被回收,则有可能会造成内存泄露。如果确认一个对象已经被释放,那么最后需要将他的变量手动设置为nil,否则可能会造成野指针错误。

在ARC下,由系统自动管理内存,并分别用strong、weak、unsafe_unretained修饰词代替retain、assign,ARC会自动添加release和autorelease调用,无须手动调用。

内存大致分为保留区、代码段、数据段、堆区、栈区、内核区

代码段:编译之后的代码。

数据段:常常称为静态常量区,其包含字符串常量、未初始化的全局/静态常量、已初始化的全局/静态常量

栈:函数调用开销,比如局部变量。分配的内存空间地址越来越小,由编译器来进行管理。

堆:通过alloc、malloc、calloc等动态分配的空间,分配的内存空间地址越来越大,由开发者进行管理。


面试题(ARC 强/弱引用 & LLVM)

在上面问题的基础上会引出这些问题:

  • 什么是ARC(ARC是为了解决什么问题诞生的)?
  • ARC通过什么方式帮助开发者管理内存?
  • ARC 都帮我们做了什么?
  • 说明并比较关键词:strong、weak、assign、copy?
  • Weak、strong、copy、assign 使用?
  • retain、copy、assign的set方法和含义?

简答(ARC 强/弱引用)

我是15年开始入坑iOS开发的,从Xcode 5学起,当时大多数项目还都是MRC项目,其中最有名的还数ASIHTTPRequest库,MRC下需要手动进行引用计数管理,不乏就要写大量的retain和release。ARC可以说是对程序员非常友好的一个功能。它就是为了解决书写MRC代码占用过多时间的问题。

大幅减少了项目整体开发时间

那么ARC通过什么方式帮助开发者管理内存?这就要说起LLVM这个强大的编译器了。

image.png

前端获取你的源代码进行词法分析、语法分析、语义分析、生成中间代码;然后将它编译为某种LLVM中间代码表示这就是支持混编的原因。后端部分可以根据平台生成实际运行的机器码。

ARC作为LLVM 3.0的一个功能,会在编译阶段自动插入retain以及release、autorelease,但清除weak引用的时候靠的是runtime,后面会讲到。ARC只负责管理Objective-c 对象的内存,CoreFoundation 对象不归ARC管理。基础数据类型不需要内存管理。

ARC都帮我们做了什么呢,下面一一列举:

  • 引用计数(自动插入retain/release)
  • 省去了@synthesize
  • 在dealloc中置空属性
  • 数组对象autorelease

修饰词说明:

atomic:修饰的对象会保证setter和getter的完整性,任何线程访问它都可以得到完整的初始化的对象。atomic比nonatomic安全,但不是绝对的线程安全。
nonatomic:修饰的对象不保证setter和getter的完整性。所以访问效率要比atomic快。
strong:表示指向并拥有该对象。其修饰的对象引用计数会加1.该对象只要引用计数不为0,就不会被销毁。
weak:表示指向但不拥有该对象。其修饰的对象引用计数不会增加。对象销毁时该指针自动置为nil。
assign:主要用于修饰基本数据类型,如NSInteger和CGFloat。在MRC下,也常用于修饰delegate。
copy:与strong类似,但内部实现不同,常用于修饰不可变对象,例如:NSString、NSArray、NSDictionary。
readwrite:这个属性是默认的情况,会自动为你生成存取器。
readonly:只生成getter不会有setter方法。

readwrite、readonly这两个属性的真正价值,不是提供成员变量访问接口,而是控制成员变量的访问权限。

在Objective-C中,基本数据类型的默认关键字为atomic,readwrite,assign;普通属性的默认关键字为atomic,readwrite和strong。
在MRC下retain、assign、copy的修饰词setter方法实现如下图:

在这里推荐一篇喵神的文章-手把手教你ARC——iOS/Mac开发ARC入门和使用


经过时间的推移,这些问题不再以简单的形式出现,涉及到以下知识点:

  • NSObject 对象原理
  • 引用计数存储原理
  • 修饰词
  • 深浅拷贝
  • 弱引用
  • 循环引用
  • AutoRelease 原理
  • Block内存
  • Core Foundation 对象内存

并且也有一些比较新颖的面试题出现:

面试题(NSObject 对象原理)

简答(NSObject 对象原理)

要搞清楚这些,就务必要进行Runtime的学习

苹果开源-objc4代码列表:选择最新的,我下载的源码是objc-723。

首先我们要理清楚的就是三个概念对象本质、isa指针、struct结构体

苹果官方文档-对象分配

在调用alloc或者allocWithZone:方法后,将会得到该类的未初始化的实例变量,alloc方法将会在应用中开辟一段空间,用于存储相关对象和数据。在将分配集设置isa到对象的类之后,该对象将集成到继承层次结构的运行时视图和构成程序的当前对象网络(类和实例)中。因此,对象可以找到它需要的任何信息运行时,例如另一个对象在继承层次结构中的位置,其他对象符合的协议,以及它可以响应消息执行的方法实现的位置。

alloc的内部实现

除了进行开辟内存外还会执行其他重要操作:

  • 它设置对象的 将计数保留为一。
  • 初始化对象的 isa实例变量指向对象的类,它是一个从类定义编译的运行时对象。
  • 初始化所有其他 实例变量为零(或等效类型为零,如nilNULL0.0)。

但是调用alloc或者allocWithZone:返回的对象尚不可用。需要调用初始化方法,init必须初始化具有特定特征的对象并返回功能对象。

init方法实现
  • alloc和new是什么区别呢?

alloc只是单纯的返回一个未进行初始化的对象,并不能进行使用,需要进行init的操作;而new的本质是调用alloc并默认发送init消息,返回一个已经初始化的对象。如果你需要调用自定义的init的方法就不要调用new方法。new方法默认调用init。

在进行了allocinit方法后,我们就可以获得对象实例了,那么什么是isa,isa指针又是如何运作的呢?

使用Xcode点到NSObject类当中,我们会发现NSObject类里包含着一个isa指针,isa指针指向类对象Class。再点一下Class,我们会发现,它是一个结构体,结构体里具体的实现就要去objc源码当中查询。

  • 一个Objective-C对象如何进行内存布局? 文字描述:每一个实例对象都包含一个isa指针,isa指针指向类对象,类对象的本质是结构体struct,类对象当中包含了isa指针、superclass、实例变量列表(自己以及各个类继承)、对象方法、属性列表、协议列表。类对象的isa指针指向自己的元类对象meta-class,元类对象当中存储的是类对象的静态方法。
  • sizeof、class_getInstance、malloc_size 的区别 class_getInstance:是OC的函数,在运行时返回结果。获取创建一个实例对象,至少需要多少内存。 malloc_size:是C的函数,获取这个实例对象实际占用了多少内存。 sizeof:是运算符,在编译器运行阶段就返回结果,返回传入的指针或类的占用大小。

在这里先推荐一篇对我帮助颇大的文章:


结束语

如果您对这篇文章有什么意见或者建议,请评论与我讨论.

如果您觉得还不错的话~可以点个喜欢鼓励我哦.

如果您想和我一起学习,请毫不吝啬的私信我吧~