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

时间:2009-01-14 15:02:46

标签: cocoa-touch string file-access

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

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加载字符串,一切都可以正常工作意?

任何线索?

2 个答案:

答案 0 :(得分:4)

在Cocoa中,如果您使用allocnewcopyretain创建对象,则只拥有该对象。每当您不拥有对象时,您无法保证在您使用它的本地范围之外的该对象。在您的示例中,由于您没有使用上述任何方法创建新字符串,因此您无法保证在存储它的情况下它将会存在。

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

// 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消息并不意味着您正在销毁它,只是因为您放弃了所有权。

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

有关详细信息,请阅读Memory Management Programming Guide for Cocoa。它真的被认为是Cocoa开发人员必读的内容。此外,它通常需要阅读,试验和阅读几次才能真正掌握,特别是如果您对其他内存管理模型有很多经验。

答案 1 :(得分:1)

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