OC基础学习-04

114 阅读8分钟

内存管理

  • 内存管理的范围
    • 任何继承了NSObject的对象都需要内存管理
    • 对其他非对象类型无效(int,char,float,double)
  • 为什么只有对OC对象才需要进行内存管理
    • OC对象存放于堆里面
    • 非OC对象一般放在栈里面(栈内存会被兄台你自动回收)
  • 栈和堆
    • 栈(操作系统):由操作系统自动分配释放,存放函数的参数值,局部变量的值等,其操作逻辑类似于数据结构中的栈,先进先出
    • 堆(操作系统):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收,分配方式类似于链表
    int main(int argc, const char * argv[])
    {
    @autoreleasepool {
        int a = 10; // 栈
        int b = 20; // 栈
        // p : 栈
        // Person对象(计数器==1) : 堆
        Person *p = [[Person alloc] init];
    }
    // 经过上一行代码后, 栈里面的变量a\b\c都会被回收
    // 但是堆里面的Person对象还会留在内存中,因为它是计数器依然是1
    return 0;
    }
    

引用计数器

  • 什么是引用计数器
    • 每个OC对象都有自己的引用计数器
    • 它是一个整数,占4字节
    • 从字面上, 可以理解为”对象被引用的次数”
    • 也可以理解为: 它表示有多少人正在用这个对象
    • 任何一个对象,一创建出来,引用计数器都为1
  • 引用计数器的操作
    • retain 加一,retain方法返回对象本身
    • release 减一,并不代表销毁,仅仅减一
    • reatainCount 获取当前引用计数器值,不准确
  • dealloc方法
    • 当一个对象的引用计数器为0时,系统就会自动调用dealloc方法
    • dealloc的重写
      • 一般会重写此方法,在这里释放相关资源
      • 一旦重写了此方法,就必须调用[super dealloc],并且一定在最后调用
    • 使用注意
      • 不能直接调用dealloc方法
      • 一旦对象回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)
  • 单个对象内存管理
    • ARC:自动引用计数,不需要程序员管理内存,编译器会在适当的地放自动加上release/retain
      • OC中ARC和Java中的垃圾回收机制,Java中的垃圾回收时系统干的,OC中的ARC是编译器干的
    • MRC:手动引用计数,所有对象的内存都手动管理
    • 将ARC变为MRC:
      • 点击项目
      • 在Building Setting中搜索 Automatic
      • 将YES改为NO
    • 内存管理原则:有加就有减
  • 野指针和空指针
    • 只要一个对象被释放了,我们就称这个对象为僵尸对象
    • 当一个指针指向一个僵尸对象,我们就称这个指针为野指针
    • 只要给野指针发送消息就会报错
    • 在开发时要开启僵尸监听,找错误时添加全局断点
    • 为了避免给野指针发送消息,一般情况下,一个对象被释放后我们会将这个对象的指针设置为空指针
  • 多对象内存管理
    • 只要一个对象想使用另一个对象,就需要对另一个对象的引用计数器+1
    • 只要一个对象不想使用另一个对象,就需要对另一个对象的引用计数器-1
    • 当A对象想使用B对象一定要对B对象进行一次retain,这样才能保证A对象存在B对象就存在,也就是说这样才能保住无论什么时候A对象都可以使用B对象
    • 当A对象释放的时候,一定要对B对象进行一次release,这样才能保住A对象释放了,B对应也会随之释放,避免内存泄漏
  • property修饰符
    • retain 会自动帮我们生成setter/getter方法内存管理的代码
    • assign 不会帮我们生成setter/getter方法内存管理的代码,只会生成普通的setter/getter方法,默认什么都不写就是这个修饰符
    • atomic:性能低(单线程)
    • noatomic 性能高(多线程) 大部分使用
  • 在手动管理内存时,在dealloc中使用self.xxx= nil;即可,相当于调用了set方法,内部进行了release和retain方法
  • @class:可以简单的引用一个类,不会导入头文件,可以提升编译效率
    • 仅仅高偶素编译器 xxx是一个类,并不包含xxx这个类的所有内容
    • 具体使用
      • 在.h文件中使用@class引用一个类
      • 在.m文件中使用#import包含这个类的.h文件
      • 由于import是一个预编译指令,他会将""中的文件拷贝到import所在的位置,并且只要""中的文件发生了变化,就会重新编译一次
    • 解决两个类的相互引用,因为@class没有拷贝操作
    • 总结:
      • 如果在.h中import,假如A拷贝了B,B拷贝了C,如果C修改了,那么B和A都需要重新拷贝,也就是说,只要有简介关系都会重新拷贝
      • 如果在.h用@class,在.m中import,如果一个文件发生了变化,只有和这个文件由直接关系的那个文件才会重新拷贝,所以提升了效率
      • 如果两个类相互拷贝,如果两个.h都用import,那么A拷贝B,B拷贝A,会造成死循环,如果在.h用class,那么不会做任何拷贝操作,而在.m中用import只会拷贝对应的文件,并不会造成死循环
  • 如果A对象要拥有B对象,B对应又要拥有A对象,此时会形成循环retain
    • 让其中一个不要做retain操作即可
  • autorelease
    • 是一种支持引用技术的内存管理方式,只要给对象发送一条autorelease消息,会将对象放到一个自动释放池中,当自动释放池被销毁时,会对池子离的所有对象进行一次release操作,注:只会进行一次release,并不保证引用计数一定为0
    • 调用autorelease,该对象的引用计数器并不会加一
    • 可以避免代码量过大的情况下,需要时时刻刻关注在哪里release的问题
    • 当IOS程序运行过程中,会创建无数个池子,这些池子都是以栈结构存在
    • 当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池
    • autorelease只是将release延迟
    • 注意事项
      • 一定要在自动释放池中调用autorelease,才会将对象放入自动释放池
      • 在自动释放池中创建了对象,一定要调用autorelease才会将对象放入自动释放池
      • 只要在自动释放池中调用autorelease,就会将对象放入自动释放池,对象的定义可以不在自动释放池
      • 不要在自动释放池中使用比较消耗内存的对象,占用内存比较打的对象
      • 尽量不要在释放池中使用循环,特别是循环的次数非常多,并且还非常占内存
      • 一个程序可以创建N个自动释放池,并且自动释放池还可以嵌套
      • 一个alloc/new就对应一个autorelease/release
    • 应用场景
      • 解决程序员要随时考虑在哪里release的问题
  • Foundation框架的类,但凡是通过类工厂方法创建的对象都是autorelease的
  • ARC自动引用计数
    • ARC是编译时特性,而不是运行时特性,XCode的功能
    • 不同于其他语言的垃圾回收,有着本质区别,垃圾回收机制时定时去查看释放对象,而ARC是XCcode自动写代码执行代码
    • ARC的优点
      • 完全消除了手动管理内存,让程序员专注业务
      • 基本上能避免内存泄漏
      • 有时性能会更加优化,因为编译器还可以进行一些优化
    • ARC的判断原则(忘记引用计数的概念)
      • 只要还有一个强指针变量指向对象,对象就会保持在内存中
    • 强指针 ————strong
      • 默认情况下所有指针都是强指针
    • 弱指针 __weak
    • 在开发中,不要使用一个弱指针保存一个刚刚创建的对象,因为会因为ARC马上被释放
    • 单个对象的内存管理 p=nil;
  • MRC:
    • A对象向拥有B对象,需要对B进行一次retain
    • A对象不用B对象了,需要对B进行一次release
    • property的时候进行retain,delloc的时候进行release
  • ARC:
    • A对象向拥有B对象,那么久需要用一个强指针指向B对象
    • A对象不想拥有B对象了,A对象无需做任何操作
    • 在ARC中定义变量不使用retain,assign,而是使用strong
  • ARC中也存在循环引用
    • 可以一个变量用strong,一个变量用weak,不要用assign(可以使用,但是assign是专保存基本数据类型的)
  • MRC项目转ARC项目
    • 点击Xcode 旁边的Edit,点Convert 选中转ARC,相关过大可能失败