0

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

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];

我更愿意依赖方法#1,但在以下上下文中使用时它的行为很奇怪:

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

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

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

有什么线索吗?

4

2 回答 2

4

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

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

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

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

选项 2 没问题,因为您通过将retain. 在您的情况下,这可能不是最佳选择,因为 alloc/init已经有一个可用的选项并且通常更易于阅读。

当您完成一个对象并且想要放弃该对象的所有权时,您向该对象发送release消息。重要的是要记住,在 Cocoa 中,向对象发送release消息并不意味着您要销毁它,而只是放弃所有权。

Cocoa 内存管理是围绕对象所有权的思想设计的。虽然一开始可能会有点混乱,但是一旦你开始理解它,它就会使在环境中编程变得更加容易,而不会引入内存错误、泄漏或其他错误。便利方法(如stringWithString)真正设计用于当您想要创建一个您实际上只在短时间内使用并且在单个函数或程序块范围内的对象时使用。如果您打算将对象保留在该范围之外,则首选使用alloc/initornew方法来构造对象。

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

于 2009-01-14T15:44:33.347 回答
1

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

于 2009-01-14T15:12:42.160 回答