保留本地创建的NSMutableDictionary - 是否必要?

时间:2011-04-04 04:07:10

标签: iphone objective-c cocoa-touch ios

嘿所有,我在这里的一段代码上有一些问题,我真的很感激一些反馈。基本上我正在创建一个临时的NSMutableDictionary,以便从plist中提取一些值。然后,我将对其中一个值进行检查,以确定是否要对plist执行任何其他操作。

代码如下,但我的问题如下:

1)主要问题

因为我可能将这个本地创建的Dictionary传递给另一个方法,在那里用作源数据,我是否需要在这里保留字典?如果是这样,那么正确的方法是什么?

我还在学习这门语言,除了不知道这里是否需要保留/释放之外,我在下面实现的方法似乎没有做任何事情。当我在发布之前和之后将字典转储到控制台时,会打印出相同的值(尽管即使发布成功也可能会发生这种情况,因为还没有其他内容覆盖该内存段?)

2)不太重要的问题:

“return”会立即跳出整个“multitaskingResumeCheck”函数,而不执行任何进一步的代码吗?或者它只是跳出“其他”循环?我尝试将“NSMutableDictionary * dict”字典创建放在filemanager中,如果循环和我从“NSNumber * wrappedATZ ...”行收到关于dict未声明的错误。我的错误是什么?

提前感谢您的帮助。我在所有相关的行上加上了“// QUESTION TAG”。

- (void) multitaskingResumeCheck {

NSLog(@"Running multitaskingResumeCheck");

// Get the path to the "Documents" directory

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

// Get the path to our plist ("Documents/foo.plist")

NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@"lastStopwatchState.plist"];

// check if our plist already exists in the Documents directory

NSMutableDictionary *dict; // QUESTION TAG (Why can't I do this in the "filemanager if" below)

NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:plistPath]) {
    // If it does, read it.     
    NSLog(@"dict existed, reading %@", plistPath);
    dict = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];

    [dict retain]; // QUESTION TAG (Necessary? If so, correct?)

} else {
    // If not, we're done here.
    return;
}

// Now check whether the timers were zeroed on exit

NSNumber *wrappedATZ = [dict valueForKey:@"allTimersZeroed"]; // QUESTION TAG (Got undeclared error if dict declaration was in wrong place (see above). Maybe I misunderstand "return"?
BOOL timersWereZeroed = [wrappedATZ boolValue];

// If they were not Zeroed, then we'll load in our data

if (!timersWereZeroed) {
    NSLog(@"Timers were not zeroed. Loading Data.");
    [self loadResumeData:dict];
}

// Dump the contents of the dictionary to the console
NSLog(@"dumping...");
for (id key in dict) {
    NSLog(@"key=%@, value=%@", key, [dict objectForKey:key]);
}   

[dict release]; // QUESTION TAG (Necessary? If so, correct?)

// Dump the contents of the dictionary to the console
NSLog(@"dumping...");
for (id key in dict) {
    NSLog(@"key=%@, value=%@", key, [dict objectForKey:key]);
}   

}

3 个答案:

答案 0 :(得分:1)

Cocoa Memory Management Rules或多或少地完全回答了你的问题,尽管可能并不完全清楚如何。

首先,您应该重构代码,以使大括号结构与实际控制流相匹配。即,

之后的一切
else
{
    return;
}

应该在if部分内部,所以它看起来像这样:

- (void) multitaskingResumeCheck {

    // Some stuff not relevant to the question

    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ([fileManager fileExistsAtPath:plistPath]) {
        // If it does, read it.     
        NSLog(@"dict existed, reading %@", plistPath);
        NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];

        [dict retain]; // QUESTION TAG (Necessary? If so, correct?)

        // Some stuff not relevant to the question

        if (!timersWereZeroed) {
            NSLog(@"Timers were not zeroed. Loading Data.");
            [self loadResumeData:dict];
        }

        // Dump the contents of the dictionary to the console
        NSLog(@"dumping...");
        for (id key in dict) {
            NSLog(@"key=%@, value=%@", key, [dict objectForKey:key]);
        }

        [dict release]; // QUESTION TAG (Necessary? If so, correct?)
    }

我把else部分之后的所有部分都移到了if部分,因为它实际上是完全一样的。由于dict现在不再使用,除了在if部分,我已经在那里移动了它的声明。我也删除了第二个转储因为dict不再存在于同一范围内。除此之外,这在功能上与你的功能相同。

内存管理规则的一个要点是:

  

通常保证收到的对象在接收到的方法中保持有效,并且该方法也可以安全地将对象返回给其调用者。

这回答了你的第一个问题。您从+dictionaryWithContentsOfFile:收到的词典在该方法中仍然有效。这包括此方法调用的任何方法,将dict作为参数传递。因此,在这种情况下,保留和释放不是必需的。之前第二次转储的原因是保留和释放对对字典的所有权没有任何净影响。

在回答第二个问题时,return会立即退出该函数而不执行任何代码。这就是我能够以我的方式重构函数的原因。

当您移动dict声明时出现错误的原因是,在您的代码中,dict被引用到块外部({ ... }中包含的语句称为块),其中声明。 C范围规则阻止了这一点。

最后,要记录字典的内容,你可以这样做:

 NSLog(@"%@", dictionary);

无需循环键盘。

答案 1 :(得分:0)

使用必要的更改检查以下代码&你的评论的答案。

- (void) multitaskingResumeCheck {

NSLog(@"Running multitaskingResumeCheck");

// Get the path to the "Documents" directory

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

// Get the path to our plist ("Documents/foo.plist")

NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@"lastStopwatchState.plist"];

// check if our plist already exists in the Documents directory

NSMutableDictionary *dict = nil; // Assign the initial value as nil if you're going to assign the actual value later in a conditional block.

NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:plistPath]) {
    // If it does, read it.     
    NSLog(@"dict existed, reading %@", plistPath);
    dict = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];

    // It's not required to retain as it's being assigned using a class method which in-turn returns an auto-release object.

}

    if (dict) {  // Put a conditional check here whether dict is nil or not.

// Now check whether the timers were zeroed on exit

NSNumber *wrappedATZ = [dict valueForKey:@"allTimersZeroed"]; // No error should come here.
BOOL timersWereZeroed = [wrappedATZ boolValue];

// If they were not Zeroed, then we'll load in our data

if (!timersWereZeroed) {
    NSLog(@"Timers were not zeroed. Loading Data.");
    [self loadResumeData:dict];
}

// Dump the contents of the dictionary to the console
NSLog(@"dumping...");
for (id key in dict) {
    NSLog(@"key=%@, value=%@", key, [dict objectForKey:key]);
}   

//[dict release]; No need to release an auto-release object.

    /** You were executing the below code after releasing the dict object. It might give garbage values which crashes the app. **/

// Dump the contents of the dictionary to the console
NSLog(@"dumping...");
for (id key in dict) {
    NSLog(@"key=%@, value=%@", key, [dict objectForKey:key]);
}
    }
}

答案 2 :(得分:0)

1。您应该只在您将使用它的位置保留一个对象。在同一个注释中,每次保留某些东西时,你必须确保释放它,这应该在同一范围内处理。您似乎在代码中正确执行此操作,除非您释放dict然后在代码中使用它。您可能不需要保留,因为它将保留到您离开该功能,但如果您保留它,则必须将其释放。

如果您将保留的对象传递到其他地方,则应通过调用[object autorelease]来放弃责任。 [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];在返回字典的自动释放副本时会做类似的事情。

2。 return;告诉代码完全离开函数。您无法在if块中声明dict的原因是因为它只存在于该范围内。一旦你离开了if代码就不再存在了。