1

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

.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

4 回答 4

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,因为它由属性保留并被myArray属性保留self.tempArray
  • 在 (3) 中,您将属性影响到与以前完全相同的对象。这参考计数根本没有改变。你可以理解,好像你self.tempArray用另一个值替换了 的值,所以属性的设置器释放旧值(减少它的引用计数),然后保留新值(从而增加它的引用计数)。在您的情况下,旧值和新值是同一个对象,您将减少数组的引用计数,然后再次重新增加它。在实践中,引用计数甚至根本不会改变(而不是再次递减+递增)以避免对象的任何潜在释放,因为属性设置器的默认实现如下:

    -(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) 中,您再次替换了 property 的值tempArray,但这次是用一个全新的对象。因此,该物业将释放其旧价值并保留新价值。因此,您在 (1) 中创建的第一个数组的引用计数为 2(由myArray和 by保留self.tempArray)将其引用计数减少到 1(因为该属性将不再保留它),并且您创建的新实例[NSArray arrayWithObject:@"SomeString"]由属性,所以它的引用计数是 +1。


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

于 2012-10-14T15:58:40.193 回答
4

实际上,retain count仅在 new、alloc、retain 和 copy 条件下增加,但如果我们通过 this 为对象提供所有权,retain count则除了增加之外,没有增加的可能性retain count

于 2013-07-02T06:58:34.717 回答
2

首先,不要尝试依赖retainCount。

在那之后:你想知道在你列举的那些场景中会发生哪种情况。好吧,一个都没有。

为什么?因为,首先,您直接分配给实例变量 - 这不会改变保留计数。完全没有。除非你使用 ARC,但似乎你没有。

你可能想给对象的属性赋值,也就是说,写

self.tempArray = myArray;

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

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

所以基本上,当您重新分配myArrayself.tempArray属性时,它会丢失并获得引用,因此它的引用计数根本不会改变。

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

于 2012-10-14T15:43:31.253 回答
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
}
于 2012-10-14T15:48:16.033 回答