iOS:引用计数

时间:2012-10-14 15:26:35

标签: ios

我正在做一些关于引用计数增加的研究。请帮忙找到它。 下面是示例代码和研究我正在做下面每行的引用计数。

.h文件

NSArray *tempArray;
@property (nonatomic, retain) NSArray *tempArray;

.m文件

@synthesize tempArray;

    -(void) sampleFunction
{
    NSArray *myArray = [[NSArray alloc] init]; // Thinking reference count increases to "1"
    tempArray = myArray;// reference count increases and tempArray gets retain count "1" now.
    tempArray = myArray;// reference count increases and tempArray gets retain count "2" now.
    tempArray = [NSArray arrayWithObject:@"SomeString"]; // retain count = ?

}

我知道这段代码可能不适合运行,但这只是为了研究这些场景的引用计数会发生什么。我试过打印retainCount,但它没有显示正确的结果。请告诉我参考计数如何在每一行上起作用?

4 个答案:

答案 0 :(得分:4)

在第2,3和4行中,您将实例变量tempArray影响到与myArray相同的对象。但是如果你这样写,你会尝试影响一个实例变量。事实上,如果您没有在代码中编写任何@synthesize tempArray@synthesize tempArray = tempArray,默认情况下,自动生成的用于存储属性值的实例变量与属性名称相同,但是以下划线为前缀。因此,当属性名称为tempArray时,实例变量名为_tempArray。实例变量tempArray本身不存在,您的代码行无效。

所以,如果我们假设你写了:

-(void) sampleFunction
{
  NSArray *myArray = [[NSArray alloc] init]; // (1)
  self.tempArray = myArray; // (2)
  self.tempArray = myArray; // (3)
  self.tempArray = [NSArray arrayWithObject:@"SomeString"]; // (4)
}
  • 在(1)中,您正在创建一个全新的NSArray实例。 “alloc”始终使用引用计数1
  • 初始化新实例
  • 在(2)中,您编写self.tempArray = myArray(相当于[self setTempArray:myArray];并因此调用属性设置器),因此您将属性设置为指向您在(1)中创建的相同数组。因此,该属性保留此数组,并且其retainCount增加1,因为它由myArrayself.tempArray属性保留。
  • 在(3)中,您将属性影响到与以前完全相同的对象。这个引用计数根本不会改变。你可以理解,好像你用另一个值替换self.tempArray的值,所以属性的setter释放旧值(递减其引用计数),然后保留新值(从而增加其引用计数) 。在您的情况下,旧值和新值是同一个对象,您将减少数组的引用计数,然后再次重新递增它。在实践中,引用计数甚至根本不会改变(而不是再次递减+递增)以避免对象的任何潜在的dealloc,因为属性setter的默认实现如下:

    -(void)setTempArray:(NSArray*)newValue
    {
      // check if old and new value are different. Only do sthg if they are different
      if (newValue != _tempArray)
      {
        [_tempArray release];  // release old value
        [newValue retain];     // retain new value
        _tempArray = newValue; // store new value in the backing variable associated with the property
      }
    }
    
  • 在(4)中,您再次替换属性tempArray的值,但这次使用了一个全新的对象。因此,该物业将释放其旧价值并保留新价值。因此,您在(1)中创建的第一个数组,其引用数量为2(由myArrayself.tempArray保留)将其引用计数减少为1(因为该属性将不再保留它),并且您创建的新实例[NSArray arrayWithObject:@"SomeString"]由属性保留,因此其引用计数为+1。


如果您直接使用实例变量替换了self.tempArray = ...(因此使用了属性),则使用实例变量不会保留它们受影响的对象(除非您使用ARC但它好像你没有),所以在(2),(3)和(4)中对象的引用计数根本不会改变。

答案 1 :(得分:4)

实际上,retain count仅在新条件,分配条件,保留条件和复制条件下增加,但如果我们通过此方式为对象提供所有权retain count将增加,除此之外不可能增加retain count {1}}。

答案 2 :(得分:2)

首先,don't even try to rely upon retainCount.

之后:你想知道在你列举的那些场景中发生了什么情况。好吧,都不是。

为什么呢?因为,在第一个地方,您直接分配实例变量 - 这不会改变保留计数。完全没有。除非您使用ARC,否则您似乎没有。

您可能想要将内容分配给对象的属性,即写入

self.tempArray = myArray;

等。现在因为属性本身(以及其支持ivar!)被声明为retain,相应的访问器方法将增加的引用计数对象被分配给属性。但是,为了不泄漏内存,通常通过在分配时释放先前分配的对象并因此保留新对象来实现访问器方法,即。即

- (void)setTempArray:(NSArray *)tmp
{
    [tmp retain];
    [tempArray release];
    tempArray = tmp;
}

所以基本上,当你将myArray重新分配给self.tempArray属性时,它会丢失并获得一个引用,因此它的引用计数根本不会被引用。

当您为该属性分配另一个新数组时,再次myArray丢失一个引用计数,当它被释放为0时,将保留使用+ [NSArray arrayWithObject:]创建的新数组。它之后的确切引用计数应该是1,因为它是使用alloc - init - autorelease创建的(这是方法的实现方式),并且它已被属性保留。但是,- retainCount返回的值仍然(并且从不)依赖。

答案 3 :(得分:0)

在您的特定示例中,您将直接分配给tempArray而不是self.tempArray,因此retainCount将始终保持为1。但是,如果你做了我认为你的意思,那么让我们来看看会发生什么。

在objective-c中,合成的保留属性将具有与此功能相同的setter:

-(void) setTempArray:(NSArray *value)
{
    if(tempArray != value) {
        [tempArray release];
        tempArray = [value retain];
    }
}

这会在为其分配新对象时增加保留计数,在将其设置为同一对象时基本上不执行任何操作,并在为其分配其他内容时释放它。所以你的例子中的保留计数就像这样:

-(void) sampleFunction
{
    NSArray *myArray = [[NSArray alloc] init]; // Retain count of 1
    self.tempArray = myArray; // 2
    self.tempArray = myArray; // still 2
    self.tempArray = [NSArray arrayWithObject:@"SomeString"]; 
    // myArray.retainCount is 1,
    // tempArray.retainCount is 2 but with 1 autorelease
    // myArray leaks
}