内存优化之-野指针、空指针、僵尸对象

1,589 阅读4分钟

一、概念 (野指针、空指针、僵尸对象)

1、野指针和空指针
野指针

C语言:声明一个指针变量,但是没有赋值初始值,此时指针指向一个垃圾值,即 指向随机的内存空间

OC语言:指针所指的对象已经被释放/回收了,但是指针没有做任何的修改,仍然指向已经回收的内存空间

空指针

没有指向任何东西的指针,即 nil \ NULL \ 0
给空指针发送消息不会报错
2、什么是Zombie Object
僵尸对象
	是一种用来检查内存错误(EXC_BAD_ACCESS)的对象,它可以捕获任何尝试访问坏内存的调用

	如果给僵尸对象发送消息,那么将在运行期间崩溃,并输出错误日志,可以通过日志定位到
            野指针对象调用的方法和类名

	通俗来讲:僵尸对象就是执行一个OC对象引用计数为0被释放的对象,此时这个对象的内存已经被系统回收,
            该对象可能还存在。
	即数据依然在内存中,但此时对象已经不稳定,其内存可能随时被其他对象申请而占
            用,所以此时僵尸对象是不可以再访问和使用的
3. 内存回收的本质
    申请1块内存空间:其本质是 向系统申请1块别人不再使用的内存空间,即已经回收的内存空间
    释放1块内存空间:其本质是 这个空间不再使用,可以由系统分配给别的对象使用,此时内存空间虽然回收了,但是原本的数据依赖还是存在的。可以理解为垃圾数据.
			所以内存回收可以理解为以下两点
                            
	1. OC对象释放后,内存回收,表示这1块内存可分配给别的对象了
	2. 这块内存在分配给别的对象之前,仍然保留着已经释放对象的数据
4. 僵尸对象的检测
   xcode中一般默认不检查指针指向的对象是否是僵尸对象,因为如果开启会影响开发效率,此时
   能访问就访问,不能访问就报错。
   
   但是我们在做内存优化时,有些僵尸对象的访问是不会报错的,所以此时可以开启xcode中的
   Zombie Objects检测:Edit Scheme-> Diagnostic-> Zombie Objects

二、Zombie Objects 原理探究

image.png

开启前的输出 image.png

开启Zombie后的输出

image.png

发现原对象的类名LGPerson 变为_NSZombie_LGPerson, superClass也由NSObject变成nil

调用栈分析,重点再 _dealloc_Zombie

image.png

伪代码实现-

触发僵尸对象方法

image.png

2. 官方解释
  1. Zombie Objects:一个对象已经解除了对他的引用,已经标记为被释放,但是此时仍然可以接收消息,这个对象就叫做僵尸对象(Zombie Objects)。将释放的对象,全部转为僵尸对象

  2. Malloc Scribble: 申请内存alloc 时,在内存上填充0xAA, 释放内存dealloc时,在内存上填充0x55,(可以通过标识 判断是否被释放了)

三、实践

实现野指针监测流程

  1. 开启野指针监测
  2. 设置监控到野指针的回调block,在block中打印信息,或者存储堆栈
  3. 监测到野指针是否crash
  4. 最大内存占用空间
  5. 是否记录dealloc调用栈
  6. 监控策略
  • 1)只监控自定义对象
  • 2)白名单策略
  • 3)黑名单策略
  • 4)监控所有对象
  1. 交换NSObjct的dealloc方法

触发野指针

  1. 开始处理对象

  2. 是否达到替换条件 1)根据监控策略,是都属于要监测的类 2)空间是否足够

  3. 如果符合条件,则获取对象,并解除引用。如果不符合则正常释放,即调用原来的dealloc

  4. 向对象内填充数据

  5. 赋值僵尸对象的类至真替换ISA

  6. 对象+dealloc调用战,保存在僵尸对象中

  7. 根据情况是否清理内存和对象

通过僵尸对象检测的实现思路
  • 1、通过oc中Method Swizzling,交换根类NSObject 和 NSProxy 的dealloc方法为自定义的dealloc
  • 2、为避免内存空间释放后被重写造成野指针问题,通过字典存储被释放的对象,同时设置在30s后调用dealloc方法将字典中存储的对象释放,避免内存增大
  • 3、为了获取更多崩溃信息,这里同样需要创建NSProxy的子类