什么会影响stringWithContentsOfFile返回的NSString的生命周期“?

时间:2021-11-20 13:29:23

Consider the 2 following methods of reading a string from a file:

考虑以下两种从文件中读取字符串的方法:

NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
NSString *string = [NSString stringWithContentsOfFile:path encoding:NSASCIIStringEncoding error:NULL];

NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path];
NSData *data = [file readDataToEndOfFile];
NSString *string = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
[file closeFile];

I would prefer to rely on method #1, but it behaves strangely when used in the following context:

我宁愿依赖方法#1,但在下面的上下文中使用时它表现得很奇怪:

NSString *string; // CLASS VARIABLE
(void) setupView
{
  string = ...; // LOADING THE STRING
}
(void) drawView
{
 ...;  // USING THE STRING
}

In short, it's an OpenGL-ES drawing-loop based on a NSTimer. The problem is that the string is accessible only at the first frame. At the next frame, the IPhone simulator (2.2) is crashing when trying to access the string.

简而言之,它是基于NSTimer的OpenGL-ES绘图循环。问题是字符串只能在第一帧访问。在下一帧,IPhone模拟器(2.2)在尝试访问字符串时崩溃。

Probably "something in my code, or in the OpenGL-ES code I'm using" one would say... But how to explain the strange fact that if I use method #2 to load the string, everything works as intended?

可能是“我的代码中的某些东西,或者我正在使用的OpenGL-ES代码中的东西”,人们会说......但是如何解释一个奇怪的事实:如果我使用方法#2加载字符串,一切都按预期工作?

Any clues?

2 个解决方案

#1


In Cocoa, you only own an object if you create it by using alloc, new, copy or retain. Whenever you don't own an object, you have no guarantees about that object outside the local scope in which you use it. In your example, since you didn't create the new string with any of the above mentioned methods, you have no guarantee that it will be around later if you store it.

在Cocoa中,如果使用alloc,new,copy或retain创建对象,则只拥有该对象。只要您不拥有对象,就无法保证在您使用它的本地范围之外的该对象。在您的示例中,由于您没有使用上述任何方法创建新字符串,因此无法保证在存储它时它将在以后出现。

In order to take ownership of an object (i.e., that you want it to stick around so that you can use it later) that you don't already own (i.e., you didn't create it using one of the previously mentioned methods), you must send that object a retain message. For example, if you rewrite your code as shown below, you will own the string and you won't have to worry about using it later:

为了获取一个对象的所有权(即,你希望它可以保留,以便以后可以使用它),你还没有拥有它(即,你没有使用前面提到的方法之一创建它) ,您必须向该对象发送保留消息。例如,如果您重写代码如下所示,您将拥有该字符串,您不必担心以后使用它:

// option 1
NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
NSString *string = [[NSString alloc] initWithContentsOfFile:path encoding:NSASCIIStringEncoding error:NULL];

// option 2
NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
NSString *string = [NSString stringWithContentsOfFile:path encoding:NSASCIIStringEncoding error:NULL];
[string retain];

Option 1 is okay because you specifically create the string with the alloc message. You now own the string object until you relinquish ownership at some other time. This is probably the best option for you, given your particular circumstances (i.e., you don't really want to use the convenience constructor because you want the string object to hang around to be used later).

选项1没问题,因为您使用alloc消息专门创建了字符串。您现在拥有字符串对象,直到您在其他时间放弃所有权。考虑到你的特殊情况,这可能是你的最佳选择(也就是说,你真的不想使用便利构造函数,因为你希望字符串对象可以随意使用以便稍后使用)。

Option 2 is okay because you specifically take ownership of the string by sending it the retain. In your case, this probably isn't the best option because there is an alloc/init option available already and that's usually easier to read.

选项2是可以的,因为您通过发送保留来专门获取字符串的所有权。在您的情况下,这可能不是最好的选择,因为已经有一个alloc / init选项,并且通常更容易阅读。

When you are done with an object and you want to relinquish ownership of the object, you send that object the release message. It's important to remember that in Cocoa, sending an object a release message doesn't mean that you're destroying it, just that you're relinquishing ownership.

完成对象并希望放弃对象的所有权后,您将向该对象发送释放消息。重要的是要记住,在Cocoa中,向对象发送释放消息并不意味着您正在摧毁它,只是因为您放弃了所有权。

Cocoa memory management is designed around the idea of ownership of objects. Although it may be a little confusing at first, once you wrap your head around it, it makes it a lot easier to program in the environment without introducing memory errors, leaks or other bugs. The conveniences methods (like stringWithString) are really designed to be used when you want to create an object that you're really only using for a brief period of time and within the scope of a single function or program block. If you plan to keep an object around past that scope, using the alloc/init or new methods to construct the object is preferred.

可可内存管理是围绕对象所有权的概念设计的。虽然一开始可能有点令人困惑,但是一旦你绕过它,就可以在环境中编程而不会引入内存错误,泄漏或其他错误。便捷方法(如stringWithString)实际上是为了在您想要创建一个您实际上仅在短时间内使用并且在单个函数或程序块范围内的对象时使用的。如果计划将对象保留在该范围之外,则首选使用alloc / init或new方法构造对象。

For more information, please read the Memory Management Programming Guide for Cocoa. It really is considered required reading for Cocoa developers. Also, it usually takes reading it, experimenting, and reading it a few more times to really grasp, especially if you have a lot of experience with other models of memory management.

有关更多信息,请阅读Cocoa的内存管理编程指南。对于Cocoa开发人员来说,它确实被认为是必读的。此外,它通常需要阅读,试验和阅读它几次才能真正掌握,特别是如果你有很多其他内存管理模型的经验。

#2


When you create a String with a Class method generally it is added to the top most autorelease pool. At the end of the event loop the pool sends a release message to all objects it holds. In this case the newly created string has a retain count equal to 1 and at the end of the loop it reaches 0 and is deallocated. If you want to te keep the string send it a retain message to keep the retain count positive when the event loop ends.

使用Class方法创建String时,通常会将其添加到最顶端的自动释放池中。在事件循环结束时,池会向其保留的所有对象发送释放消息。在这种情况下,新创建的字符串的保留计数等于1,并且在循环结束时它达到0并被解除分配。如果你想保留字符串,则发送一条保留消息,以便在事件循环结束时保持保留计数为正。

#1


In Cocoa, you only own an object if you create it by using alloc, new, copy or retain. Whenever you don't own an object, you have no guarantees about that object outside the local scope in which you use it. In your example, since you didn't create the new string with any of the above mentioned methods, you have no guarantee that it will be around later if you store it.

在Cocoa中,如果使用alloc,new,copy或retain创建对象,则只拥有该对象。只要您不拥有对象,就无法保证在您使用它的本地范围之外的该对象。在您的示例中,由于您没有使用上述任何方法创建新字符串,因此无法保证在存储它时它将在以后出现。

In order to take ownership of an object (i.e., that you want it to stick around so that you can use it later) that you don't already own (i.e., you didn't create it using one of the previously mentioned methods), you must send that object a retain message. For example, if you rewrite your code as shown below, you will own the string and you won't have to worry about using it later:

为了获取一个对象的所有权(即,你希望它可以保留,以便以后可以使用它),你还没有拥有它(即,你没有使用前面提到的方法之一创建它) ,您必须向该对象发送保留消息。例如,如果您重写代码如下所示,您将拥有该字符串,您不必担心以后使用它:

// option 1
NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
NSString *string = [[NSString alloc] initWithContentsOfFile:path encoding:NSASCIIStringEncoding error:NULL];

// option 2
NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
NSString *string = [NSString stringWithContentsOfFile:path encoding:NSASCIIStringEncoding error:NULL];
[string retain];

Option 1 is okay because you specifically create the string with the alloc message. You now own the string object until you relinquish ownership at some other time. This is probably the best option for you, given your particular circumstances (i.e., you don't really want to use the convenience constructor because you want the string object to hang around to be used later).

选项1没问题,因为您使用alloc消息专门创建了字符串。您现在拥有字符串对象,直到您在其他时间放弃所有权。考虑到你的特殊情况,这可能是你的最佳选择(也就是说,你真的不想使用便利构造函数,因为你希望字符串对象可以随意使用以便稍后使用)。

Option 2 is okay because you specifically take ownership of the string by sending it the retain. In your case, this probably isn't the best option because there is an alloc/init option available already and that's usually easier to read.

选项2是可以的,因为您通过发送保留来专门获取字符串的所有权。在您的情况下,这可能不是最好的选择,因为已经有一个alloc / init选项,并且通常更容易阅读。

When you are done with an object and you want to relinquish ownership of the object, you send that object the release message. It's important to remember that in Cocoa, sending an object a release message doesn't mean that you're destroying it, just that you're relinquishing ownership.

完成对象并希望放弃对象的所有权后,您将向该对象发送释放消息。重要的是要记住,在Cocoa中,向对象发送释放消息并不意味着您正在摧毁它,只是因为您放弃了所有权。

Cocoa memory management is designed around the idea of ownership of objects. Although it may be a little confusing at first, once you wrap your head around it, it makes it a lot easier to program in the environment without introducing memory errors, leaks or other bugs. The conveniences methods (like stringWithString) are really designed to be used when you want to create an object that you're really only using for a brief period of time and within the scope of a single function or program block. If you plan to keep an object around past that scope, using the alloc/init or new methods to construct the object is preferred.

可可内存管理是围绕对象所有权的概念设计的。虽然一开始可能有点令人困惑,但是一旦你绕过它,就可以在环境中编程而不会引入内存错误,泄漏或其他错误。便捷方法(如stringWithString)实际上是为了在您想要创建一个您实际上仅在短时间内使用并且在单个函数或程序块范围内的对象时使用的。如果计划将对象保留在该范围之外,则首选使用alloc / init或new方法构造对象。

For more information, please read the Memory Management Programming Guide for Cocoa. It really is considered required reading for Cocoa developers. Also, it usually takes reading it, experimenting, and reading it a few more times to really grasp, especially if you have a lot of experience with other models of memory management.

有关更多信息,请阅读Cocoa的内存管理编程指南。对于Cocoa开发人员来说,它确实被认为是必读的。此外,它通常需要阅读,试验和阅读它几次才能真正掌握,特别是如果你有很多其他内存管理模型的经验。

#2


When you create a String with a Class method generally it is added to the top most autorelease pool. At the end of the event loop the pool sends a release message to all objects it holds. In this case the newly created string has a retain count equal to 1 and at the end of the loop it reaches 0 and is deallocated. If you want to te keep the string send it a retain message to keep the retain count positive when the event loop ends.

使用Class方法创建String时,通常会将其添加到最顶端的自动释放池中。在事件循环结束时,池会向其保留的所有对象发送释放消息。在这种情况下,新创建的字符串的保留计数等于1,并且在循环结束时它达到0并被解除分配。如果你想保留字符串,则发送一条保留消息,以便在事件循环结束时保持保留计数为正。