美文网首页
内存管理

内存管理

作者: 纳兰沫 | 来源:发表于2020-10-13 16:21 被阅读0次

摘自内存管理

1.iOS 内存管理的理解

@TaggedPointer(针对类似于NSNumber的小对象类型)
@NONPOINTER_ISA(64位系统下)
 1.第一位的0或1代表的是纯地址型isa指针 还是NONPOINTER_ISA指针
 2.第二位 代表是否有关系对象
 3.第三位代表是否有C++代码
 4.接下来33位代表指向的内存地址
 5.接下来有弱引用的标记
 6.接下来有是否delloc的标记。。。等等
@散列表(引用计数表 weak表)
 1.SideTables表在非嵌入式的64位系统中 有64张SideTable表
 2.每一张SideTable主要是由三部分组成 自旋锁 引用计数表 弱引用表
 3.全局的引用计数之所以不存在同一张表中 是为了避免资源竞争 解决效率的问题
 4.引用计数表中引入了分离锁的概念 将一张表分拆成多个部分 对他们分别加锁 可以实现并发操作 提升执行效率

2.使用自动引用计数应该遵守的原则

1.不能使用retain  release retainCount autorelease
2.不可以使用NSAllocateObject  NSDeallocateObject
3.必须遵守内存管理方法的命名规则
4.不需要显示的调用Dealloc
5.使用@autoreleasePool来代替NSAutoreleasePool
6.不可以使用区域NSZone
7.对象变量不可以作为C语言的结构体成员
8.显示转换id和void*

3.ARC自动内存管理的原则

1.自己生成的对象 自己持有
2.非自己生成的对象 自己可以持有4
3.自己持有的对象不再需要时 需要进行释放
4.非自己持有的对象无法释放

4.访问__weak修饰的变量 是否已经被注册在了@autoreleasePool中 为什么

肯定
__weak修饰的变量属于弱引用 如果没有被注册到@autoreleasePool中 创建之后也将会随之销毁 为了延长他
的生命周期 必须注册到@autoreleasePool中 以延缓释放

5.ARC的retainCount怎么存储的

存在64张哈希表中 根据哈希算法查找所在位置 无需遍历 十分快捷
- 散列表(引用计数表 weak表)  -SideTables表在非嵌入式的64位系统中 有64张SideTable表 每一张
SideTable主要由三部分组成(自旋锁 引用技术表 弱引用表) 
- 全局的引用计数之所在不存在一张表中 是为了避免资源竞争 解决效率的问题- 
- 引用计数表中引入了 `分离锁`的概念 将一张表分拆成多个部分 对他们分别加锁 可以实现并发操作 提升执行效率

引用计数表(哈希表)

通过指针的地址 查找到引用计数的地址 大大提升查找效率
通过DisguisedPtr(objc_object)存储函数 同时也通过这个函数查找 避免了循环引用

6.简要说下@autoreleasePool的数据结构

双向链表 每张链表头尾相连 有parent child指针
每创建一个池子 会在首部创建一个哨兵对象 作为标记
最外层池子的顶端会有一个next指针 当链表容量满了 就会在链表的顶端 并指向下一张表

7.__weak和_Unsafe_Unretain的区别

弱的修饰的指针变量 在指向的内存地址销毁后 会在runtime的机制下 自动置为nil
_Unsafe_Unretain不会置为nil 容易出现悬垂指针 发生崩溃 但是_Unsafe_Unretain比__weak效率高

8.为什么有了ARC 但还是需要@AutoreleasePool的存在?

避免内存不足 及时释放不需要的内存空间

9.__weak属性修饰的变量 如何实现在变量没有强引用后自动放置为nil

用的弱引用-weak表 也是一张哈希表
被weak修饰的指针变量所指向的地址是key 所有指向这块内存地址的指针会被添加到一个数组里 这个value数组是 当内存地址销毁 value里面的所有对象都被放置为nil

10.说一下对 retain,copy,assign,weak,_Unsafe_Unretain 关键字的理解。

strong 
  表示指向并持有该对象 其修饰对象的引用计数会加1 该对象只要引用计数不为0就不会被销毁 当然可以通过将
变量强制赋值nil来进行销毁
weak
  指向但是并不持有该对象 引用计数也不会加1 在runtime中对该属性进行了相关操作 无需处理 可以自动销毁 
weak用来修饰对象 多用于避免循环引用的地方  weak 不可以修饰基本数据类型
assign
  主要用于修饰基本数据类型 列如NSInterger CGFloat 存储在栈中 内存不用程序员管理 assgin是可以
修饰对象 但是会出现问题
copy
  copy关键字和strong类似 copy多用于修饰有可变类型的不可变对象上NSString NSArray NSDictionary上
__unsafe_unretain
  __unsafe_unretain类似于weak 但是当对象被释放后 指针已然保存了之前的地址 被释放的地址变为僵尸
对象 访问被释放的地址就会报错 所以是不安全的
__autoreleasing
  将对象赋值给附有 __autoreleasing修饰的变量等同于 ARC 无效时调用对象的 autorelease 方法,实质
就是扔进了自动释放池

11.ARC在编译时做了什么

根据代码执行的某些语境 在适当的位置插入retain release

12.ARC在运行时做了些什么工作?

weak修饰的变量能够在引用计数为0时被自动设置分类中翻译nil,显然是有运行时逻辑在工作的
为了保证向后兼容性,ARC在运行时检测到类函数中的autorelease后紧跟其后retain,此时不直接调用对象的
autorelease方法,而是转换调用objc_autoreleaseReturnValue。 objc_autoreleaseReturnValue会
查看当前方法返回之后即将要执行的那段代码,如果那段代码要在返回对象上执行retain操作,则设置变量数据
结构中的一个标志位,而不执行autorelease操作,与之相似,如果方法返回了一个自动释放的对象,而调用方法
的代码要保留此函数retain,然后此时不直接执行,objc_retainAoutoreleasedReturnValue替代替换
执行函数。此函数要检测刚才提到的标志位,若已经放置位,则不执行retain操作,设置并检测标志位,要比调用
autorelease和retain 替换

13.函数返回一个对象时,造成对象autorelease么?为什么?

会,为了延长返回对象的生命周期,给其他使用者留足调用的时间

14.说一下什么是 悬垂指针?什么是 野指针?

悬垂指针
  指针指向的内存已经被释放了 但是 指针还在 这就是悬垂指针或迷途指针
野指针
  没有进行初始化的指针 

15.内存管理预设的关键字是什么

MRC
  @property(atomic,readWrite,retain)UIView * view;
ARC
  @property(atomic,readWrite,strong)UIView * view;

16.内存中的5大区分别是什么?

- 栈区         由编译器自动分配释放 存放函数的参数值 局部变量的值等 其操作方式类似于数据结构中的栈
- 堆区         一般由程序员分配释放 若程序员不释放 程序结束时可能由OS回收 分配方式类似于链表
- 全局区        全局变量和静态变量的存储在一起 初始化的全局变量和静态变量在一块区域 未初始化的全局变量和静态变量在相邻的另一块区域 程序结束后由系统释放
- 文字常量区     常量字符串就是放在这里的 程序结束后由系统释放
- 程序代码区     存放函数体的二进制代码

18.BAD_ACCESS在什么情况下出现?

访问了已经被销毁的内存空间,就会报出这个错误。根本原因是有`悬垂指针`没有被释放。

19.讲一下@dynamic关键字?

@dynamic意味着编译器不会帮助我们自动合成setter和getter方法。我们需要手动实现,这里就涉及到Runtime的动态添加方法的知识点。

20.autoReleasePool什么时候释放?

App启动后,苹果在主线程RunLoop里注册了两个Observer,其本身都是_wrapRunLoopWithAutoreleasePoolHandler()。
  第一个Observer监视的事件是Entry(即将进入Loop),其相互内部会调用_objc_autoreleasePoolPush()
创建自动释放池。其order是-2147483647,优先级最高,保证创建释放池发生在其他所有之前。
  第二个Observer监视了两个事件:(BeforeWaiting准备进入休眠)时调用_objc_autoreleasePoolPop() 
和_objc_autoreleasePoolPush()释放旧的池并创建新池;Exit(即将退出循环)时调用_objc_autoreleasePoolPop()
来释放自动释放池。这个Observer的order是2147483647,优先级最低,保证其释放池子发生在其他所有
可能之后。
  在主线程执行的代码,通常是写在诸如事件回调,Timer回调内的。回调这些会被RunLoop创建好的
AutoreleasePool环绕关系着,所以不会出现内存泄漏,者开发也。不必显示创建Pool了

21. retain,release的实现机制?

1.Retain的实现机制
SideTable&table = SideTables()[This];
size_t&refcntStorage = table.refcnts [This];
refcntStorage + = SIZE_TABLE_RC_ONE;
2.Release的实现机制
SideTable&table = SideTables()[This];
size_t&refcntStorage = table.refcnts [This];
refcntStorage-= SIZE_TABLE_RC_ONE;
两者的实现机制类似,概括讲就是通过第一层hash算法,找到指针变量所对应的sideTable。然后再通过一层
hash算法,找到存储引用计数的size_t,然后进行增减操作。retainCount不是固定的1,
SIZE_TABLE_RC_ONE是一个宏定义,实际上是一个收益4的偏移量

22.能不能简述一下Dealloc的实现机制

1.`Dealloc`调用流程

*   1.首先开始 `_objc_rootDealloc()`
*   2.接下来开始 `rootDealloc()`
*   3.这时候会判断是否可以被释放,判断的依据主要有5个,判断是否有以上五种情况
    *   `NONPointer_ISA`
    *   `weakly_reference`
    *   `has_assoc`
    *   `has_cxx_dtor`
    *   `has_sidetable_rc`
*   4-1。如果有以上五中任意一种,将会调用`object_dispose()`方法,做下一步的处理。
*   4-2。如果没有之前五种情况的任意一种,则可以执行释放操作,C函数的`free()`。
*   5.执行完毕。

2.`object_dispose()`调用流程。

*   1.直接调用`objc_destructInstance()`。
*   2.之后调用C函数的`free()`。

3.`objc_destructInstance()`调用流程

*   1.先判断`hasCxxDtor`,有如果`C++`的相关内容,要调用`object_cxxDestruct()`,销毁`C++`相关的内容。
*   2.再判断`hasAssocitatedObjects`,如果有的话,要调用`object_remove_associations()`,销毁关联对象的样式操作。
*   3.然后调用`clearDeallocating()`。
*   4.执行完毕。

4.`clearDeallocating()`调用流程。

*   1.先执行`sideTable_clearDellocating()`。
*   2.再执行`weak_clear_no_lock`,在这一步骤中,替换指向该对象的弱引用指针放置为`nil`。
*   3.接下来执行`table.refcnts.eraser()`,从引用计数表中纳入该对象的引用计数。
*   4.至此为止,`Dealloc`的执行流程结束。

23.在MRC下如何重写属性的Setter状语从句:Getter?

-(void)setBrand :( NSString *)brand {
 //如果实例变量指向的地址和参数指向的地址不同
    if(_brand!= brand)
    {
        //将实例变量的引用计数减一
        [_brand release ];
       //将参数变量的引用计数加一,并赋值给实例变量
        _brand = [品牌保留];
    }
}
-(NSString *)brand {
     //将实例变量的引用计数加1后,添加自动减1 
    //作用,确保调用getter方法取值时可以取到值的同时在完全不需要使用后释放
    return [ _brand保留]自动释放];
}
// MRC下手动释放内存可重写dealloc但不要调用dealloc会崩溃
-( void)dealloc {
    [_string release ];
    //必须最后调用super dealloc 
    [ super   dealloc ];
}

24.在 Obj-C 中,如何检测内存泄漏?你知道哪些方式?

目前我知道的方式有以下几种

Memory Leaks
Alloctions
Analyse
Debug Memory Graph
MLeaksFinder
泄露的内存主要有以下两种:

Laek Memory 这种是忘记 Release 操作所泄露的内存。
Abandon Memory 这种是循环引用,无法释放掉的内存

相关文章

  • iOS内存管理详解

    目录 block内存管理 autorelease内存管理 weak对象内存管理 NSString内存管理 new、...

  • 第10章 内存管理和文件操作

    1 内存管理 1.1 内存管理基础 标准内存管理函数堆管理函数虚拟内存管理函数内存映射文件函数 GlobalMem...

  • 操作系统之内存管理

    内存管理 包括内存管理和虚拟内存管理 内存管理包括内存管理概念、交换与覆盖、连续分配管理方式和非连续分配管理方式(...

  • JavaScript —— 内存管理及垃圾回收

    目录 JavaScript内存管理内存为什么需要管理?内存管理概念JavaScript中的内存管理JavaScri...

  • OC - OC的内存管理机制

    导读 一、为什么要进行内存管理 二、内存管理机制 三、内存管理原则 四、MRC手动内存管理 五、ARC自动内存管理...

  • 3. 内存管理

    内存管理 内存管理包含: 物理内存管理; 虚拟内存管理; 两者的映射 除了内存管理模块, 其他都使用虚拟地址(包括...

  • Go语言——内存管理

    Go语言——内存管理 参考: 图解 TCMalloc Golang 内存管理 Go 内存管理 问题 内存碎片:避免...

  • jvm 基础第一节: jvm数据区

    程序内存管理分为手动内存管理和自动内存管理, 而java属于自动内存管理,因此jvm的职能之一就是程序内存管理 j...

  • 内存管理

    内存管理的重要性。 不进行内存管理和错误的内存管理会造成以下问题。 内存泄露 悬挂指针 OC内存模型 内存管理是通...

  • 11-AutoreleasePool实现原理上

    我们都知道iOS的内存管理分为手动内存管理(MRC)和自动内存管理(ARC),但是不管是手动内存管理还是自动内存管...

网友评论

      本文标题:内存管理

      本文链接:https://www.haomeiwen.com/subject/hhqppktx.html