objective-c NSString 中 stringWithString 和 initWithString 中的对象所有权

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/318666/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 20:56:07  来源:igfitidea点击:

Object ownership in stringWithString and initWithString in NSString

objective-ccocoa

提问by Sixto Saez

I understand that any init... method initializes a new object and that NSString stringWithString makes a copy of the parameter string as a new object. I also understand that being the objects' owner, I can control the release/deallocation of any objects that I allocate. What I don't understand is when would I use the stringWithString method since any local variable assigned that way would have it's memory "owned" by NSString instead of the local class.

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

The "Programming in Objective C" book by Kochan (1st ed) uses the following code (see pages 342-344) to explain that the initWithString is preferable to stringWithString because the AddressCard class would own the name variable contents. Also, I don't get any errors making repeated calls to the setName version with the stringWithString method. TIA!!

Kochan 的“Objective C 编程”一书(第一版)使用以下代码(参见第 342-344 页)来解释 initWithString 比 stringWithString 更可取,因为 AddressCard 类将拥有名称变量内容。此外,我不会在使用 stringWithString 方法重复调用 setName 版本时遇到任何错误。蒂亚!!

//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

回答by Peter Hosey

What I don't understand is when would I use the stringWithString method since any local variable assigned that way would have it's memory "owned" by NSString instead of the local class.

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

What? No.

什么?不。

The rules are simple:

规则很简单:

  • Any object returned by alloc, copy, copyWithZone, or newhas a retain count of 1.
  • retainincreases the receiving object's retain count.
  • releasedecreases the receiving object's retain count.
  • autoreleasetells the current autorelease pool to send the receiving object the releasemessage “later”.
  • Any factory method that doesn't have “new” or “copy” in the name (e.g., stringWithString:) returns an object that it has autoreleased on your behalf.
  • 任何对象返回的alloccopycopyWithZone,或new具有保留的1计数。
  • retain增加接收对象的保留计数。
  • release减少接收对象的保留计数。
  • autorelease告诉当前的自动释放池release“稍后”向接收对象发送消息。
  • 任何名称中没有“new”或“copy”的工厂方法(例如,stringWithString:)返回一个它代表您自动释放的对象。

Or, digested a bit:

或者,稍微消化一下:

  • Any method whose name contains copy, alloc, retain, or newreturns an object that you own.
  • Any method that doesn't, returns an object that you don't own.
  • To own an object, retain it.
  • 任何名称包含copyallocretainnew返回您拥有的对象的方法。
  • 任何不拥有的方法都会返回一个您不拥有的对象。
  • 要拥有一个对象,请保留它。

The incorrect implementation of setName:that you show is incorrect because it stores an autoreleased object in an instance variable, when you mean to own the object. You should retain it or, in this case, copy it. One way is to simply use allocand initWithString:, as in the correct example you show; the other way would be copy.

setName:您显示的不正确实现是不正确的,因为当您打算拥有该对象时,它将自动释放的对象存储在实例变量中。您应该保留它,或者在这种情况下复制它。一种方法是简单地使用allocand initWithString:,如您展示的正确示例所示;另一种方式是copy

The Memory Management Programming Guide for Cocoa explains everything.Every Cocoa or Cocoa Touch programmer should read or re-read it from time to time.

Cocoa 的内存管理编程指南解释了一切。每个 Cocoa 或 Cocoa Touch 程序员都应该不时阅读或重读它。

回答by Boaz Stuller

Actually, both setters are wrong. The 'incorrect' one is wrong for general memory management reasons (which are well-expounded elsewhere). The 'recommended' one is wrong for 2 reasons:

实际上,两个二传手都是错误的。由于一般内存管理原因(在别处有很好的阐述),“不正确”是错误的。“推荐”的错误有两个原因:

  1. if (theName == name), then you're likely to deallocate your object in the first line, and then attempt to use the deallocated object as a parameter to -initWithString: on the second line, resulting in undefined behavior.
  2. -initWithString: does not handle being passed nil gracefully.
  1. if (theName == name),那么您可能会在第一行释放对象,然后尝试将释放的对象用作 -initWithString: 在第二行的参数,从而导致未定义的行为。
  2. -initWithString: 不处理优雅地传递 nil。

The 'correct' (IMHO) method is:

“正确”(恕我直言)方法是:

-(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
}

For most objects you'll actually want to -retain instead of -copy on that third line, but for strings it's almost always better to copy.

对于大多数对象,您实际上希望在第三行使用 -retain 而不是 -copy,但对于字符串,复制几乎总是更好。

回答by Andy

The difference between initWithString and stringWithString is that stringWithString returns an auto-released pointer. This means that you don't need to release it specifically, since that will be taken care of next time that the auto-release pool cleans up any auto-released pointers.

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

initWithString, on the other hand, returns a pointer with a retain count of 1 - you do need to call release on that pointer, or else it would result in a memory leak.

另一方面,initWithString 返回一个保留计数为 1 的指针 - 您确实需要对该指针调用 release,否则会导致内存泄漏。

See https://stackoverflow.com/questions/193288/what-is-the-cost-of-using-autorelease-in-cocoafor some reasons as why you should use auto-release vs release.

请参阅https://stackoverflow.com/questions/193288/what-is-the-cost-of-using-autorelease-in-cocoa出于某些原因,为什么您应该使用自动发布与发布。

回答by Ben Gottlieb

In the Incorrect code above, the next time name is referenced after setName is called, you'll get an exception error, since the object will have been released. You can use either the "Correct" code, or wrap your stringWithString call in an explicit retain call:

在上面的错误代码中,下次在调用 setName 后引用 name 时,您将收到异常错误,因为该对象已被释放。您可以使用“正确”代码,也可以将 stringWithString 调用包装在显式保留调用中:

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

回答by Matt Gallagher

What I don't understand is when would I use the stringWithString method since any local variable assigned that way would have it's memory "owned" by NSString instead of the local class.

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

A string created with stringWithString:isn't owned by the NSString, it is owned by the NSAutoreleasePool(although multiple places can retainan object, making ownership shared).

一个用 with 创建的字符串stringWithString:不属于NSString,而是属于 的NSAutoreleasePool(虽然多个地方可以retain一个对象,使所有权共享)。

With stringWithString:, the string will become invalid when the autorelease pool is next processed (normally during the application's next event loop) because the NSAutoreleasePool will releaseits pointer. If you have not retained the string before then, any pointer you have to it (namein the case of your class) will be invalid (the variable namewill still exist but it will point to garbage).

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

Autorelease is good, if you don't intend to keep any pointers to the NSStringbut since you do intend to keep a pointer, you'll need to retainthe NSString. initWithString:gives you a retain count of 1 automatically.

Autorelease 很好,如果您不打算保留任何指向 的指针,NSString但由于您确实打算保留一个指针,则需要retainNSString. initWithString:自动为您提供 1 的保留计数。