美文网首页
Objective-C总结----5.内存管理

Objective-C总结----5.内存管理

作者: 鋼鉄侠 | 来源:发表于2016-10-09 14:43 被阅读28次

    可执行程序构成

    • Objective-C可执行程序是由(可执行)代码、初始化和未初始化的程序数据、链接信息、重定位信息、局部数据和动态数据构成的。
      • 可执行程序、程序数据以及链接在重定位信息会以静态方式分配内存,并在程序的生命周期一直存在。
      • 局部数据在语句块中声明并且仅在该语句块中有效,当该语句块执行后局部数据不在继续存在,存在栈内存中,系统自动管理。
      • "动态数据"->Objective-C将创建的对象存储在动态分配的内存即“堆”内存中,我们需要进行唯一的内存管理。
    • 程序数据包包括以静态方式声明的变量和程序变量(即在程序编译时在代码中设置的常数)。

    内存管理不当引起的问题

    • 内存泄漏
      如果程序没有释放不再使用的对象,就会导致内存泄漏,造成内存浪费,容易耗尽系统内存。
    • 悬挂指针
      如果程序释放了仍在使用的对象,就会导致该问题,为野指针或僵尸指针,向悬挂指针发送消息会导致程序奔溃。

    Objective-C内存管理方式

    Objective-C内存管理是通过引用计数实现的,引用计数是一种通过对象的唯一引用,确定对象是否正在被使用。如果对象的引用计数为零时,对象就被视为不再使用,系统将释放这个对象的内存。

    苹果公司提供两种方式管理内存:

    • 手动管理MRR/MRC
    • 自动管理ARC->苹果推荐使用ARC,已经很成熟的计数

    1.手动管理

    手动管理是建立在“对象所有权”概念上的内存管理机制,只要对象的所有者还存在,对象就不会被Objective-C运行时环境释放。

    • 对象引用&对象所有权
      Objective-C对象是通过指向Objective-C对象内存地址的变量,以间接方式访问,这种变量也称为指针。
      如 KNZPerson *person = [[KNZPerson alloc]init];
      • person为一个指针变量,保存着对象的内存地址
      • [[KNZPerson alloc]init]为创建一个对象并初始化,返回对象的地址</br>

    再创建一个指针变量person2,将person赋值给person2,那么person2拥有指向该对象的内存地址。

     -  KNZPerson *person2  = person;
    
    如果person2没有获得该对象的所有权(通过赋值语句无法获得所有权)的话,当person被释放(person不再指向该对象,该对象被释放),那么person2保存的对象地址指向的内存空间不再存在,person2就是悬挂指针,因此我们需要以手动的方式管理对象的生命周期(保留和释放),在编写代码时***(面向类Class设计时添加内存管理代码)***必须遵守一些内存管理规则。
    
    • 手动内存管理规则
      • 为创建的所有对象设置所有权
        • 创建:alloc、new、copy或者mutableCopy,计数器+1

        注意:Foundation里面的类用alloc创建时也需要进行内存管理,但是使用一些便利方法创建的不需要:
        例如:NSString *str = [[NSString alloc]initWithFormat:@"A1"];
        需要[str release];
        NSString *str2 = [NSString stringWithFarmat:@"A2"];
        不需要进行内存管理
        - 应使用retain方法获取对象的所有权
        - retain:计数器+1
        - 当不再使用某个对象时,必须放弃其所有权
        - release:计数器-1
        - 当计数器=0对象自动释放,释放时自动调用dealloc方法
        - 不能放弃不归你所有的对象所有权

    例如:
    KNZPerson *person = [[KNZPerson alloc]init];
    alloc创建一个对象并且赋予person,计数器+1,当前值为1;
    KNZPerson *person2 = [person retain];
    person指向的对象发送消息retain,计数器+1,当前值为2;
    当person不再指向该对象的话[person release];
    计数器-1,当前值为1;
    还有指针变量person2指向该对象,该对象不会被释放;
    [person2 release];person2也不再指向该对象,计数器-1,当前值为0,
    系统认为该对象不再被使用,系统自动调用dealloc方法释放掉该对象。

    MRR管理内存情况下如何设计一个类
    • 设计一个类拥有其他对象时,初始化时被创建的其他对象引用计数器+1(记得在对象释放调用dealloc方法中,释放掉这些其他对象)
    • 创建这个类的实例对象时,这个对象的引用计数器自动+1,就可以拥有这个对象了,不用管对象内部所拥有的其他对象。
    • 当这个对象的引用计数器=0时,系统自动调用对象的dealloc方法释放此对象,当对象拥有其他对象时,需要在dealloc方法里面释放掉所拥有的对象,最后调用[super dealloc]方法,彻底释放对象。
    @autorelease{自动释放代码块}
    • 在自动释放池里面创建一个实例对象并对这个对象发送autorelease消息,无需再对对象进行内存管理,对象会在释放池最后释放掉。
      如 KNZPerson *person = [[[KNZPerson alloc]init]autorelease];

    • 用于创建iOS和macOS应用的苹果UI框架、尤其是AppKit和UIKit,能够自动提供自动释放代码块。

    • 需要在自动释放池中手动编写自动释放代码块:

      • 你编写的程序不是以苹果UI框架为基础的,如命令行工具
      • 你实现的逻辑中含有创建许多临时对象的循环。
      • 你编写的应用派生了一个或多个辅助线程。

    自动管理ARC

    自动引用计数是一种功能强大的内存管理工具。与MRR相同,也是通过对象引用计数器来管理对象保留及释放,编译程序时由编译器分析源代码,在编译代码必要位置自动插入retain和release消息。
    苹果公司推荐使用ARC进行内存管理。

    ARC使用规则和约定

    • 不能手动编写发送retain、retainCount、release、autorelease、dealloc消息
    • 不能直接进行id和(void *)类型的互换
    • 需要使用自动释放池代码块执行由ARC管理的自动释放操作
    • 不能使用C结构体重的对象指针。
    • 不能使用内存去NSZone
    • 为了与非ARC代码协作,不能创建以“copy”开头的方法和自动声明属性
    • 默认情况下,ARC并非异常安全。

    ARC的生命周期限定符

    用来声明常规变量和属性的生命周期

    • 常规变量生命周期
      • _strong,默认配置
      • _weak,表面对象随时被释放,当对象被释放,指针变量会被设置为nil
      • _unsafe_unretained,与_weak类似,但指针变量不会设置为nil而是悬挂
      • _autorelease,与autorelease方法无关,用于通过引用传递对象。
    • 属性生命周期
      • strong,默认配置,等同于retain
      • weak,类似于assign特性,如果引用对象被释放了,其实例变量会被设置为nil。

    循环引用解决方法weak

    • 设计类时,类A拥有类B的实例变量,类A强stong指向类B的实例变量;当类B也拥有类A的实例变量,这个时候用类B也用strong指向类A的实例变量话,那么就无法释放A和B的实例对象了,这个时候需要有一方weak弱指向对方,当对象A释放后,对象B弱weak指向对象A,对象B指针被设置为nil,对象B也会被释放的。

    • 一般iOS开发界面UI控件都用weak,因为添加到window的控件都会被Window强引用着,没有必要再使用一个指针强引用着UI控件。

    • delegate属性一般weak特性,因为带有代理设计的tableview或者textField之类的UIView控件都会让控制器成为它的代理,控制器已经有强指针指着UIView根视图,根视图再指向带有代理设计的tableview或者textField之类的UIView控件,这个时候使用delegate再strong指向控制器的话就会造成循环引用。

    相关文章

      网友评论

          本文标题:Objective-C总结----5.内存管理

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