iOS中引用计数内存管理机制分析总结(NSString引用计数为-1的情况)

在 iOS 中引用计数是内存的管理方式,虽然在 iOS5 版本中,已经支持了自动引用计数管理模式,但理解它的运行方式有助于我们了解程序的运行原理,有助于 debug 程序。
   操作系统的内存管理分成堆和栈。

   在堆中分配的内存,都适用引用计数模式;在栈中则不是。

   NSString 定义的对象是保存在栈中,所以它没有引用计数,不是通过引用计数对内存进行管理的。之前我在一篇博客上看,说常量的引用计数会是一个很大的整数,测试的结果显示它是-1. 对该对象进行 retain 操作,不好改变它的 retainCount 值。

   MutableNSString 定义的对象,需要先分配中的内存空间,再初始化才能使用。它是采用引用计数管理内存的。对该对象做 retainCount 操作则每次增加一个。

   其实,引用计数是对内存区域的空间管理方式,是应从内存块的视角去看的。任何对象都是指向内存块的指针,有多少个指针指向这个内存块,这个内存块就有多少个引用计算。
   如果没有任何指针指向该内存块了,很明显,该内存块就没有对象引用了,引用计算就是 0, 系统会认为该内存区域已经空闲,于是立即清理,也就是更新一下管理堆的链表中某个标示位。

     测试方法如下:

     在 xcode 中建立一个非 arc 的项目,单视图即可。建立一个按钮的操作方法。

     - (IBAction)testRC:(id)sender {

     NSInteger i;
     i=self.i_test;

     if((i%2)==1)
     {
     NSString * str1=@"welcome";
     NSString * str2=@"mlgb";
     NSString * str3;
     NSString * str4=@"welcome";
     NSLog(@"str1 addr is %p",str1); // 你会发现str1与str2两个指针指向了同一个内存
     NSLog(@"str2 addr is %p",str3);
     NSLog(@"str3 addr is %p",str3);
     NSLog(@"str4 addr is %p",str4);

     NSLog(@"str1 retainCount is %i",[str1 retainCount]); // str1与str2是开辟在堆中,因此不能用引用计数来管理内存,这里为-1
     NSLog(@"str2 retainCount is %i",[str2 retainCount]);
     //NSLog(@"str3 retainCount is %i",[str3 retainCount]); 该使用会导致 crash ,因为 str3 没有指向任何内存区域。


     str3=[str1 retain];
     NSLog(@"str3=[str1 retain];");
     NSLog(@"str1 retainCount is %i",[str1 retainCount]);
     NSLog(@"str3 retainCount is %i",[str3 retainCount]);
     str3=[str2 retain];
     NSLog(@"str3=[str2 retain];");
     NSLog(@"str2 retainCount is %i",[str1 retainCount]);
     NSLog(@"str3 retainCount is %i",[str2 retainCount]);

     /*
     结果如下:
     2014-10-09 17:12:57.887 mutableCopy)[4402:60b] str1 addr is 0x3598
  2014-10-09 17:12:57.888 mutableCopy)[4402:60b] str2 addr is 0x35a8
  2014-10-09 17:12:57.889 mutableCopy)[4402:60b] str3 addr is 0x1
  2014-10-09 17:12:57.889 mutableCopy)[4402:60b] str4 addr is 0x3598

     在栈中,内容相同的对象 str1 和 str4 ,都分配在一个内存区域中,这点是 c 编译器的功能,有利于内存使用的效率。

  2014-10-09 17:20:57.132 mutableCopy)[4504:60b] str1 retainCount is -1
  2014-10-09 17:20:57.132 mutableCopy)[4504:60b] str2 retainCount is -1
  2014-10-09 17:20:57.132 mutableCopy)[4504:60b] str3=[str1 retain];
  2014-10-09 17:20:57.133 mutableCopy)[4504:60b] str1 retainCount is -1
  2014-10-09 17:20:57.134 mutableCopy)[4504:60b] str3 retainCount is -1
  2014-10-09 17:20:57.134 mutableCopy)[4504:60b] str3=[str2 retain];
  2014-10-09 17:20:57.135 mutableCopy)[4504:60b] str2 retainCount is -1
  2014-10-09 17:20:57.135 mutableCopy)[4504:60b] str3 retainCount is -1

     */
}
else
{


    NSMutableString * mstr1=[[NSMutableString alloc] initWithString: @"welcome" ];
    NSMutableString * mstr2=[[ NSMutableString alloc ] initWithString : @"mlgb" ];
    NSMutableString * mstr3;
    NSMutableString * mstr4=[[ NSMutableString alloc ] initWithString : @"welcome" ];

    NSLog( @"mstr1 addr is %p" ,mstr1);
    NSLog( @"mstr2 addr is %p" ,mstr2);
    NSLog( @"mstr3 addr is %p" ,mstr3);
    NSLog( @"mstr4 addr is %p" ,mstr4);

    NSLog( @"mstr1 retainCount is %i" ,[mstr1 retainCount]);
    NSLog( @"mstr2 retainCount is %i" ,[mstr2 retainCount]);
    //NSLog(@"mstr3 retainCount is %i",[mstr3 retainCount]);该使用会导致 crash ,因为 str3 没有指向任何内存区域。

    mstr3=[mstr1 retain];
    NSLog( @"mstr3=[mstr1 retain];" );

    NSLog( @"mstr1 retainCount is %i" ,[mstr1 retainCount]);
    NSLog( @"mstr3 retainCount is %i" ,[mstr3 retainCount]);
    NSLog( @"mstr3 addr is %p" ,mstr3);

    mstr3=[mstr2 retain];
    NSLog( @"mstr3=[mstr2 retain];" );
    NSLog( @"mstr2 retainCount is %i" ,[mstr1 retainCount]);
    NSLog( @"mstr3 retainCount is %i" ,[mstr2 retainCount]);
    NSLog( @"mstr3 addr is %p" ,mstr3);

    /*

     2012-07-14 11:07:36.652 testMem[878:f803] mstr1 addr is 0x68706b0
     2012-07-14 11:07:36.655 testMem[878:f803] mstr2 addr is 0x6876040
     2012-07-14 11:07:36.656 testMem[878:f803] mstr3 addr is 0x2a35
     2012-07-14 11:07:36.657 testMem[878:f803] mstr4 addr is 0x686fbf0

  在堆中,内容相同的对象 str1 和 str4 ,都分配在不同内存区域中。

  2014-10-09 17:32:49.783 mutableCopy)[4621:60b] mstr1 retainCount is 1
  2014-10-09 17:32:49.783 mutableCopy)[4621:60b] mstr2 retainCount is 1
  2014-10-09 17:32:49.784 mutableCopy)[4621:60b] mstr3=[mstr1 retain];
  2014-10-09 17:32:49.784 mutableCopy)[4621:60b] mstr1 retainCount is 2
  2014-10-09 17:32:49.785 mutableCopy)[4621:60b] mstr3 retainCount is 2
  2014-10-09 17:32:49.785 mutableCopy)[4621:60b] mstr3 addr is 0x8e0e000
  2014-10-09 17:32:49.785 mutableCopy)[4621:60b] mstr3=[mstr2 retain];
  2014-10-09 17:32:49.785 mutableCopy)[4621:60b] mstr2 retainCount is 2
  2014-10-09 17:32:49.785 mutableCopy)[4621:60b] mstr3 retainCount is 2
  2014-10-09 17:32:49.786 mutableCopy)[4621:60b] mstr3 addr is 0x8e0daa0


     */


}

self .i_test= self .i_test+ 1 ;

}


简而言之,引用计数实际上是指向其内存区域的指针数,从内存块的角度去理解,就很容易理解了。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。