• objectivec 对于引用计数以及指针的浅析


    作为一个熟悉c  c++的人,一下子去接触objective-c,除了感觉到熟悉以及好奇之外,很多东西也是会让自己“无法理解的”

    在c中,对分为实体变量以及指针变量,比如 int a  跟  int * p,如果对于c c++比较了解,那么这个就不用说了

     

    但是一旦接触了objective-c的话,就比较头痛了,没办法用c或者c++这一套方法去理解了。

    下面的文字是从网上摘录,至少我看了之后可以理解其中的原理了。

     

     基本原理

    Objective-C的内存管理机制与.Net/Java那种全自动的垃圾回收机制是不同的,它本质上还是C语言中的手动管理方式,只不过稍微加了一些自动方法。

    1 Objective-C的对象生成于堆之上,生成之后,需要一个指针来指向它。

    ClassA *obj1 = [[ClassA alloc] init];

    2  Objective-C的对象在使用完成之后不会自动销毁,需要执行dealloc来释放空间(销毁),否则内存泄露。

    [obj1 dealloc];

     这带来了一个问题。下面代码中obj2是否需要调用dealloc

    ClassA *obj1 = [[ClassA alloc] init];

    ClassA *obj2 = obj1;

    [obj1 hello]; //输出hello

    [obj1 dealloc];

    [obj2 hello]; //能够执行这一行和下一行吗?

    [obj2 dealloc];

    不能,因为obj1obj2只是指针,它们指向同一个对象,[obj1 dealloc]已经销毁这个对象了,不能再调用[obj2 hello][obj2 dealloc]obj2实际上是个无效指针。

    如何避免无效指针?请看下一条。

    3    Objective-C采用了引用计数(ref count或者retain count)。对象的内部保存一个数字,表示被引用的次数。例如,某个对象被两个指针所指向(引用)那么它的retain count2。需要销毁对象的时候,不直接调用dealloc,而是调用releaserelease会让retain count1,只有retain count等于0,系统才会调用dealloc真正销毁这个对象。

    ClassA *obj1 = [[ClassA alloc] init]; //对象生成时,retain count = 1

    [obj1 release]; //release使retain count1retain count = 0dealloc自动被调用,对象被销毁

    我们回头看看刚刚那个无效指针的问题,把dealloc改成release解决了吗?

    ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1

    ClassA *obj2 = obj1; //retain count = 1

    [obj1 hello]; //输出hello

    [obj1 release]; //retain count = 0,对象被销毁

    [obj2 hello];

    [obj2 release];

    [obj1 release]之后,obj2依然是个无效指针。问题依然没有解决。解决方法见下一条。

    4           Objective-C指针赋值时,retain count不会自动增加,需要手动retain

    ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1

    ClassA *obj2 = obj1; //retain count = 1

    [obj2 retain]; //retain count = 2

    [obj1 hello]; //输出hello

    [obj1 release]; //retain count = 2 – 1 = 1

    [obj2 hello]; //输出hello

    [obj2 release]; //retain count = 0,对象被销毁

    问题解决!注意,如果没有调用[obj2 release],这个对象的retain count始终为1,不会被销毁,内存泄露。(1-4可以参考附件中的示例程序memman-no-pool.m)

    这样的确不会内存泄露,但似乎有点麻烦,有没有简单点的方法?见下一条。

    5  Objective-C中引入了autorelease pool(自动释放对象池),在遵守一些规则的情况下,可以自动释放对象。(autorelease pool依然不是.Net/Java那种全自动的垃圾回收机制)

    5.1          新生成的对象,只要调用autorelease就行了,无需再调用release

    ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1 但无需调用release

    5.2          对于存在指针赋值的情况,代码与前面类似。

    ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1

    ClassA *obj2 = obj1; //retain count = 1

    [obj2 retain]; //retain count = 2

    [obj1 hello]; //输出hello

    //对于obj1,无需调用(实际上不能调用)release

    [obj2 hello]; //输出hello

    [obj2 release]; //retain count = 2-1 = 1

    细心的读者肯定能发现这个对象没有被销毁,何时销毁呢?谁去销毁它?(可以参考附件中的示例程序memman-with-pool.m)请看下一条。

    6           autorelease pool原理剖析。(其实很简单的,一定要坚持看下去,否则还是不能理解Objective-C的内存管理机制。)

    6.1          autorelease pool不是天生的,需要手动创立。只不过在新建一个iphone项目时,xcode会自动帮你写好。autorelease pool的真名是NSAutoreleasePool

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    6.2          NSAutoreleasePool内部包含一个数组(NSMutableArray),用来保存声明为autorelease的所有对象。如果一个对象声明为autorelease,系统所做的工作就是把这个对象加入到这个数组中去。

    ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1,把此对象加入autorelease pool

    6.3          NSAutoreleasePool自身在销毁的时候,会遍历一遍这个数组,release数组中的每个成员。如果此时数组中成员的retain count1,那么release之后,retain count0,对象正式被销毁。如果此时数组中成员的retain count大于1,那么release之后,retain count大于0,此对象依然没有被销毁,内存泄露。

    6.4          默认只有一个autorelease pool,通常类似于下面这个例子。

    int main (int argc, c*****t char *argv[])

    {

    NSAutoreleasePool *pool;

    pool = [[NSAutoreleasePool alloc] init];

    // do something

    [pool release];

    return (0);

    } // main

    所有标记为autorelease的对象都只有在这个pool销毁时才被销毁。如果你有大量的对象标记为autorelease,这显然不能很好的利用内存,在iphone这种内存受限的程序中是很容易造成内存不足的。例如:

    int main (int argc, c*****t char *argv[])

    {

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    int i, j;

    for (i = 0; i < 100; i++ )

    {

     for (j = 0; j < 100000; j++ )

        [NSString stringWithFormat:@"1234567890"];//产生的对象是autorelease的。

    }

    [pool release];

    return (0);

    } // main

    (可以参考附件中的示例程序memman-many-objs-one-pool.m,运行时通过监控工具可以发现使用的内存在急剧增加,直到pool销毁时才被释放)你需要考虑下一条。

    7           Objective-C程序中可以嵌套创建多个autorelease pool。在需要大量创建局部变量的时候,可以创建内嵌的autorelease pool来及时释放内存。(感谢网友hhyyttneogui的提醒,某些情况下,系统会自动创建autorelease pool, 请参见第四章)

    int main (int argc, c*****t char *argv[])

    {

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    int i, j;

    for (i = 0; i < 100; i++ )

    {

     NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];

     for (j = 0; j < 100000; j++ )

        [NSString stringWithFormat:@"1234567890"];//产生的对象是autorelease的。

     [loopPool release];

    }

    [pool release];

    return (0);

    } // main

  • 相关阅读:
    Oracle基础知识整理
    linux下yum安装redis以及使用
    mybatis 学习四 源码分析 mybatis如何执行的一条sql
    mybatis 学习三 mapper xml 配置信息
    mybatis 学习二 conf xml 配置信息
    mybatis 学习一 总体概述
    oracle sql 语句 示例
    jdbc 新认识
    eclipse tomcat 无法加载导入的web项目,There are no resources that can be added or removed from the server. .
    一些常用算法(持续更新)
  • 原文地址:https://www.cnblogs.com/rollrock/p/2542161.html
Copyright © 2020-2023  润新知