本文会继续深入学习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方法)

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

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

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

      id obj = [[NSObject alloc] init];
      [obj release];
      [obj release]; // 再次释放

Objective-C内存管理之-引用计数的更多相关文章

  1. iOS的内存管理和引用计数规则、Block的用法以及三种形式(stack、malloc、global)

    学习内容 iOS的内存管理和引用计数规则 内存管理的思考方式 自己生成的对象自己持有 非自己生成的对象自己也能持有 自己持有的对象不需要时释放 非自己持有的对象不能释放 ARC有效时,id类型和对象类 ...

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

    初学者在学习Objective-c的时候,很容易在内存管理这一部分陷入混乱状态,很大一部分原因是没有弄清楚引用计数的原理,搞不明白对象的引用数量,这样就当然无法彻底释放对象的内存了,苹果官方文档在内存 ...

  3. Swift基础语法-内存管理, 自动引用计数

    1. 工作机制 Swift和OC一样,采用自动引用计数来管理内存 当有一个强引用指向某一个对象时,该对象的引用计数会自动+1 当该强引用消失时,引用计数会自动-1 当引用计数为0时,该对象会被销毁 2 ...

  4. Python内存管理及引用计数

    作为一门动态语言,python很重要的一个概念就是动态类型,即对象的类型和内存占用都是运行时确定的.(Why?)运行时,解释器会根据语法和右操作数来决定新对象的类型.动态类型的实现,是通过引用和对象的 ...

  5. Objective C内存管理之理解autorelease------面试题

    Objective C内存管理之理解autorelease   Autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当前的A ...

  6. Object-C内存管理-对象引用计数的特例

    看到OC中内存管理这块,其中的引用计数部分,部分10.5上的EBOOK示例已经在10.9上不能运行正确了,比如下面的代码: NSString * str1 = @"string 1" ...

  7. Objective C 内存管理[转]

    1  配对原则 alloc – release new – release retain - release copy – release 2  new和alloc-init的区别 (1)区别只在于a ...

  8. objective C 内存管理及属性方法具体解释

    oc为每一个对象提供一个内部计数器.这个计数器跟踪对象的引用计数,当对象被创建或拷贝时.引用计数为1.每次保持对象时,调用retain接口.引用计数加1.假设不需要这个对象时调用release,引用计 ...

  9. OC语法6——内存管理之引用计数器(retain,release)

    OC内存管理: 一.引用计数器: Java有垃圾回收机制(Garbage Collection,GC).也就是说当我们创建对象后,不需要考虑回收内存的事,Java的垃圾回收机制会自动销毁该对象,回收它 ...

随机推荐

  1. IOS开发之简单音频播放器

    今天第一次接触IOS开发的UI部分,之前学OC的时候一直在模拟的使用Target-Action回调模式,今天算是真正的用了一次.为了熟悉一下基本控件的使用方法,和UI部分的回调,下面开发了一个特别简易 ...

  2. Android APK如何签名

    Android项目以它的包名作为唯一标识,如果在同一设备上安装两个相同的应用,后面安装的应用就会覆盖前面安装的应用.为了避免这种情况的发生,我们需要对作为产品发布的应用进行签名. 签名其实有两个作用: ...

  3. grunt任务之seajs模块打包

    grunt与seajs grunt是前端流行的自定义任务的脚手架工具,我们可以使用grunt来为我们做一些重复度很高的事情,如压缩,合并,js语法检查等.通过定义grunt的配置文件Gruntfile ...

  4. RAC 10.2.0.5,客户端登陆间断遭遇ORA-12545

    实验环境: 服务端:OEL 5.7 + Oracle 10.2.0.5 RAC  客户端:Windows 7 + Oracle 11.2.0.1 Client   1.客户端登陆间断遭遇ORA-125 ...

  5. 使用AutoMapper进行对象间映射

    在开发过程中,难免遇到下面这种情况:两个(或多个)对象所拥有的大多数属性是重复的,我们需要在对象间进行映射(即将一个对象的属性值赋给另一个对象.通常我们可以进行如下操作: A a=new A(); a ...

  6. [WCF编程]10.操作:请求/应答操作

    一.调用操作概述 WCF除了支持经典的服务端-客户端的请求/应答操作外,还提供了对其他操作类型的内建支持,包括:即发即弃的单向调用:允许服务将调用返回给客户端的双向回调:允许客户端或服务器处理大量负荷 ...

  7. PetaPoco4.0的事务为什么不会回滚

    using (var srop=DbHelper.CurrentDb.GetTransaction()) { ID = bp.AddModel(model).ToStr(); #region 参与楼盘 ...

  8. header('Location:'.C('VIP_HX').'/CmdId/'.$CmdId.'/user_id/'.$user_id.'/Token/'.$Token);

    利用header函数做跳转,跳转至C('VIP_HX')配置文件中VIP_HX的地址下,携带参数CmdId 值为 $CmdIduser_id 值为 $user_idToken 值为 $Token th ...

  9. EC笔记,第二部分:6.若不想使用编译器默认生成的函数,就该明确拒绝

    6.若不想使用编译器默认生成的函数,就该明确拒绝 1.有的时候不希望对象被复制和赋值,那么就把复制构造函数与赋值运算符放在private:中,但是这两个函数是否需要实现呢?假设实现了,那么你的类成员方 ...

  10. qam 64的设计

    module qam64(x,out,clk,clk1,rst);input x,clk,clk1,rst;output [18:0] out;reg [2:0] count;reg [5:0] re ...