stringWithString中的对象所有权和NSString中的initWithString

时间:2008-11-25 20:00:21

标签: objective-c cocoa

据我所知,任何init ...方法都会初始化一个新对象,而NSString stringWithString会将参数字符串的副本作为新对象。我也理解作为对象的所有者,我可以控制我分配的任何对象的释放/释放。我不明白的是什么时候我会使用stringWithString方法,因为任何以这种方式分配的局部变量都会让它的内存由NSString而不是本地类“拥有”。

Kochan编写的“Objective C中的编程”一书(第1版)使用以下代码(参见第342-344页)来解释initWithString比stringWithString更可取,因为AddressCard类将拥有名称变量contents。另外,使用stringWithString方法重复调用setName版本时,我没有任何错误。 TIA !!

//header file has appropriate declarations but not included here:
#import "AddressCard.h"

@implementation AddressCard;

-(NSString *) name
{
   return name;
}

//Recommended code:
-(void) setName: (NSString *) theName
{
   [name release]
   name = [[NSString alloc] initWthString: theName];
}

//Incorrect code according to Kochan:
-(void) setName: (NSString *) theName
{
   [name release]
   name = [NSString stringWthString: theName];
}

//rest of class implementation code snipped
@end

5 个答案:

答案 0 :(得分:27)

  

我不明白的是什么时候我会使用stringWithString方法,因为任何以这种方式分配的局部变量都会让它的内存由NSString而不是本地类“拥有”。

什么?否。

规则很简单:

  • alloccopycopyWithZonenew返回的任何对象的保留计数为1.
  • retain会增加接收对象的保留计数。
  • release会减少接收对象的保留计数。
  • autorelease告诉当前的自动释放池,以便稍后发送接收对象release消息。
  • 任何名称中没有“new”或“copy”的工厂方法(例如stringWithString:)都会返回一个代表您自动释放的对象。

或者,消化了一下:

  • 任何名称包含copyallocretainnew的方法都会返回您拥有的对象。
  • 任何不支持的方法都会返回您不拥有的对象。
  • 要拥有一个对象,请保留它。

您显示的setName:的错误实现是不正确的,因为它将一个自动释放的对象存储在实例变量中,当您意味着拥有该对象时。你应该保留它,或者在这种情况下,复制它。一种方法是简单地使用allocinitWithString:,就像您展示的正确示例一样;另一种方式是copy

The Memory Management Programming Guide for Cocoa explains everything.每个Cocoa或Cocoa Touch程序员都应该不时阅读或重读。

答案 1 :(得分:6)

实际上,两位制定者都错了。由于一般的内存管理原因(其他地方已经很好地阐述),“错误的”错误。 “推荐”的错误有两个原因:

  1. if(theName == name),那么你就是 可能会释放你的对象 第一行,然后尝试 使用deallocated对象作为 参数为-initWithString:on 第二行,导致未定义 行为。
  2. -initWithString:不处理优雅地传递nil。
  3. '正确'(恕我直言)方法是:

    -(void) setName: (NSString *) theName
    {
       if (theName == name) return; // if they're equal, no need to do anything further
       [name release];
       name = [theName copy];  // sets name to nil if theName is nil
    }
    

    对于大多数对象,你实际上想要在第三行上获得而不是-copy,但对于字符串,复制几乎总是更好。

答案 2 :(得分:3)

initWithString和stringWithString之间的区别在于stringWithString返回一个自动释放的指针。这意味着您不需要专门发布它,因为下次自动发布池会清除任何自动释放的指针时会注意这一点。

另一方面,

initWithString返回一个保留计数为1的指针 - 你需要在该指针上调用release,否则会导致内存泄漏。

请参阅https://stackoverflow.com/questions/193288/what-is-the-cost-of-using-autorelease-in-cocoa,了解为什么要使用自动发布与发布。

答案 3 :(得分:0)

在上面的错误代码中,在调用setName之后引用下一个时间名称,您将收到异常错误,因为该对象已被释放。您可以使用“更正”代码,或将stringWithString调用包装在显式保留调用中:

name = [[NSString stringWithString: theName] retain];

答案 4 :(得分:0)

  

我不明白的是什么时候我会使用stringWithString方法,因为任何以这种方式分配的局部变量都会让它的内存由NSString而不是本地类“拥有”。

使用stringWithString:创建的字符串不属于NSString,它由NSAutoreleasePool拥有(尽管多个地方可以retain一个对象,使所有权共享)。

对于stringWithString:,当下一次处理自动释放池时(通常在应用程序的下一个事件循环期间),该字符串将变为无效,因为NSAutoreleasePool将release其指针。如果您之前没有retain字符串,那么您所拥有的任何指针(如果是您的类,则为name)都将无效(变量name仍然存在,但它将指向垃圾)。

自动释放很好,如果你不打算保留NSString的任何指针,但由于你打算保留一个指针,你需要retain NSStringinitWithString:会自动为您提供1的保留计数。