• Objective-C内存管理之-引用计数


    本文会继续深入学习OC内存管理,内容主要参考iOS高级编程,Objective-C基础教程,疯狂iOS讲义,是我学习内存管理的笔记

    内存管理

    1 内存管理的基本概念

    1.1 Objective-C中的内存管理

    • 手动内存管理和自动释放池---MRC>(Mannul Reference Counting)
    • 自动内存管理---ARC>(Automatic Reference Count)
    • 自动垃圾回收---GC>(Garbage Collection)
    由于iOS系统不支持垃圾回收,所以我们在iOS开发中只能使用MRC和ARC来进行内存管理,本文不再介绍Objective-C中的垃圾回收机制,但是此处注意Objective-C中是存在垃圾回收机制的
    

    1.2 内存管理中存在的问题

    • 内存泄露:不再需要的对象没有释放

      引起的问题:程序的内存占有量不断增加,最终会被耗尽导致程序崩溃

    • 野指针:没有进行初始化得指针

      引起的问题:浪费内存资源,如果调用程序会出现未知的结果,甚至导致程序崩溃

    • 悬空指针:一个指针指向一个被销毁的对象

      引起的问题:调用悬空指针指向的属性或者方法时,程序会出现未知的结果,甚至导致程序崩溃

    • 僵尸对象:过度释放的对象

      引起的问题:

    2.手动内存管理和自动释放池---MRC>(Mannul Reference Counting)

    2.1 什么是引用计数(Reference Counting)

    引用计数:Objective-C中引入了引用计数这一机制来跟踪并处理对象的生命周期,

    管理方式:每个对象都有一个与之关联的整数,这个整数被称为引用计数,在Objective-C中,通过不同的方法可以对引用计数进行操作,具体的处理如下表:

    对象操作 Objective-C方法 对应的操作结果
    生成并持有对象 alloc, new, copy,mutableCopy等方法 生成对象并设置引用计数 =1
    持有对象 reatain方法 使引用计数 +1
    释放对象 release方法 使引用计数 -1
    废弃对象 dealloc方法---系统自动调用 引用计数 =0 时调用

    关于delloc方法:dealloc方法继承自NSObject,因此所有的对象都具有此方法,当一个对象的引用计数为0时,也就意味着没有任何程序需要此对象,系统会回收该对象所占用的内存,在系统销毁对象之前,会自动调用该对象的dealloc方法来执行一些回收操作,如果该对象还持有其他对象的引用,我们必须重写dealloc方法来释放该对象引用的其他对象(通常就是使用该对象的release方法)

    引用计数机制回收对象的说明:如果一个对象的引用计数为0,则表明程序已经不再需要它,这时系统会自动回收该对象所占内存,相反,如果一个对象的引用计数不为0,系统就不应该回收,也不会回收它所占的内存

    关于retainCount方法:Objective-C提供了retainCount方法来返回一个对象当前的引用计数

    如何重写dealloc方法:

    - (void)dealloc {
    
        // 处理该对象的其他引用(通过release方法)
        
        /** 回调父类的dealloc方法 */
        [super dealloc];
    }
    

    2.2 苹果如何管理引用计数

    • 2.2.1 因为NSObject类的源代码没有公开,我们利用Xcode的调试器(lldb)和iOS大概追溯出其实现过程
      • alloc

        +alloc
        +allocWithZone: 
        class_createInstance 		//此方法可以通过objc4中的runtime/objc-runtime-new.mm确认
        calloc						// 分配内存块
        
      • retainCount

        -retainCount 
        __CFDoExternRefOperation	// 此函数根据retain,retainCount,release操作进行分发,调用__CFBasicHashXXX方法
        CFBasicHashGetCountOfKey
        
      • retain

        -retain
        __CFDoExternRefOperation 
        CFBasicHashAddValue
        
      • release

        -release 
        __CFDoExternRefOperation 
        CFBasicHashRemoveValue		// 当此函数返回0时, -release调用dealloc方法
        
    • 2.2.2 由__CFDoExternRefOperation函数以及此函数的调用关系,我们大概推算苹果大概是使用散列表(引用计数表)来管理引用计数
      • 通过引用计数表来管理引用计数的好处:
        • 对象用内存块的分配无须考虑内存块头部
        • 引用计数表各记录中存有内存块的地址,可从各个记录追溯到各对象的内存块(在进行内存泄露的检查时,此条特性具有举足轻重的作用,即使出现故障导致对象占用的内存块损坏,但是只要引用计数表没有被破坏,我们就可以确定各内存块的位置,这就是设置全局断点可以查出哪里出现内存泄露的原因)
          引用计数表

    2.3 内存管理的思考方式

    1. 自己生成的对象,自己持有

      1.1 使用alloc new copy mutableCopy创建的对象只能自己持有

      id obj1 = [[NSObject alloc] init];
      id obj2 = [NSObject new];
      id obj3 = [NSObject copy];
      id obj4 = [NSObject mutableCopy];
      

      1.2 使用以上名称的开头的方法也意味着自己生成并持有对象

      allocNewObject

      newNewObject

      copyNewObject

      mutableCopyNewObject

    2. 非自己生成的对象,自己也能持有

      2.1 非alloc new copy mutableCopy生成的对象,变量obj本身不持有该对象

      id obj1 = [NSMutableArray array];
      id obj2 = [NSDictionary dictionary];
      
      

      2.2 通过retain方法,非通过alloc new copy mutableCopy生成的对象,可以成为自己持有的对象

      id obj = [NSMutableArray array];
      
      [obj retain];
      
    3. 不再需要自己持有的对象时释放

      • 3.1 释放通过alloc new copy mutableCopy生成的对象,一旦不在需要,务必要使用release方法释放

        id obj = [[NSObject alloc] init];
         
        [obj release];
        
      • 3.2 用retain方法持有的非自己生成的对象,一旦不再需要,也一定要使用release释放

        id obj = [NSMutableArray array];
        [obj retain]; 	// 通过retain方法持有对象
        [obj release];	// 在不需要时也要通过release方法释放对象
        
      • 3.3 用某个方法生成对象,并将其作为方法的返回值,这时我们该如何处理

        • 3.3.1 通过alloc new copy mutableCopy 或其他符合命名规则的方法生成的对象,只需要原封不动的返回就能让调用方也持有该对象

          - (id)allocObject {
          	id obj = [[NSObject alloc] init];
              return obj;
          }
          
          - (id)allocObjectWithObject:(id)obj {
          	id object = [obj allocObject];
          	return object;
          }	    
          
        • 3.3.2 如果持有非自己生成的对象,例如[NSMutableArray array]生成的对象,我们要使用autorelease方法释放

          注:命名规则:用来取得谁都不持有的对象的方法名不能以alloc new copy mutableCopy开头

          - (id)object {  
          	 id obj = [NSMutableArray array];
          	 [obj autorelease];
          	 return obj;
          }
          
        • 3.3.3 autorelease方法:提供了这样的功能,使对象在超出指定的生存范围时自动并正确释放(调用release方法)
          release和autorelease的区别

    4. 非自己持有的对象无法释放---注意以下两点,如果发生这样的情况会导致程序崩溃

      • 4.1 通过alloc new copy mutableCopy方法或者通过retain方法持有的对象,一旦不再需要时,必须进行释放,除此之外其他方法获得的对象绝对不能释放,一旦释放会造成程序崩溃

      • 4.2 自己持有的对象释放后再次释放,造成僵死对象,引起程序崩溃或在访问废弃的对象时崩溃

        id obj = [[NSObject alloc] init];
        [obj release]; 
        [obj release]; 	// 再次释放
        
  • 相关阅读:
    Spring Cloud Hystrix Dashboard的使用 5.1.3
    Spring Cloud Hystrix 服务容错保护 5.1
    Spring Cloud Ribbon 客户端负载均衡 4.3
    Spring Cloud 如何实现服务间的调用 4.2.3
    hadoop3.1集成yarn ha
    hadoop3.1 hdfs的api使用
    hadoop3.1 ha高可用部署
    hadoop3.1 分布式集群部署
    hadoop3.1伪分布式部署
    KVM(八)使用 libvirt 迁移 QEMU/KVM 虚机和 Nova 虚机
  • 原文地址:https://www.cnblogs.com/denz/p/5339038.html
Copyright © 2020-2023  润新知