【问题标题】:Object being released too early?对象过早释放?
【发布时间】:2012-06-25 17:47:35
【问题描述】:

2 天来,我一直在用头撞墙,试图找出我的代码到底出了什么问题。到目前为止,什么都没有。我发现的唯一一件事是一个对象试图对其自身调用 release 并且它还没有被实例化。 (虽然,由于某种原因,它不会在 100% 的时间发生,只是在它想的时候)

让我解释一下问题(也许我忽略了一些东西,我希望你们能给我的黑暗带来一些启发)

我的模型对象

Person {
  NSString *name;
  NSNumber *age;
}

@property(nonatomic, retain) NSNumber* TheAge;
@property(nonatomic, retain) NSString *TheName;

我的实现

@synthesize TheName = name;
@synthesize TheAge = age;

+(Person*)personFromDictionary:(NSDictionary*)dic {
  Person* newPerson = [[[Person alloc] init]autorelease];
  newPerson.theAge = [dic objectForKey:kAge];
  newPerson.theName = [dic objectForKey:kName];

  return newPerson;
}

-(void)dealloc {
  self.TheAge = nil;
  self.TheName = nil;
}

我有一个“收集器线程”,它从服务器读取 JSON 数组,下载它并将其解析为字典。字典中的每个条目都对应一个人对象 这个线程是一个不同的类,只是使用 Person 模型

线程做这样的事情(在自动释放池中)

NSDictionary *parsedDict = download.returnValue;

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Person *tmpPerson = personFromDictionary

[entryDictionary setObject:tmpPerson forKey:kAge];

[pool release];

[self updateEntries:entryDictionary];

-(void)updateEntries:(NSMutableDictionary*)updatedDict {
   NSArray *keys = [updatedDict allKeys];
   for(NSString *key in allKeys){
        Person *entry = [updatedDict valueForKey:key];
        [entriesLock lock];
        [currentPersonEntries setObject:entry forKey:key];
        [entriesLock unlock];

    }
}

当我遇到崩溃时(由于某种该死的原因随机发生,我得到如下堆栈跟踪

人员释放

person setTheAge (bam crash)

我猜是因为 setter 看起来像这样

-(void)setTheAge:(NSString*)theAge {
  [theAge retain];
  [age release]; // it doesn't exist for some reason???
  age = theAge;
}

我该如何保护这种类型的东西?

【问题讨论】:

  • 您是否尝试在 Person* newPerson = [[[Person alloc] init]autorelease] 中删除自动释放;
  • 顺便说一句,我还没有在上面的代码示例上尝试过,但我敢打赌静态分析器(shift-command-B)可能会生成关于上面的各种警告代码。如果您刚刚进入 Objective C,静态分析器报告的警告可能非常有用。您应该有来自分析器的 zero 警告。我认为我下面的答案是一个更有建设性的答案(而不是通过一堆警告),但我只是想建议你看看这个很棒的小工具。

标签: iphone ios memory memory-management memory-leaks


【解决方案1】:

一些想法。

首先,你有:

Person {
    NSString *name;
    NSNumber *age;
}

@property(nonatomic, retain) NSNumber* TheAge;
@property(nonatomic, retain) NSString *TheName;

在你的实现中,你有:

@synthesize TheName = name;
@synthesize TheAge = age;

Apple 不再建议您明确定义您的 ivar,而是让您的 synthesize 语句处理它(可能是因为如果您拼错其中一个,您最终可能会得到一个额外的、意外的 ivar)。所以你可能只想:

@interface Person : NSObject

@property(nonatomic, retain) NSNumber* theAge;
@property(nonatomic, retain) NSString* theName;

@end

此外,在命名您的 ivars 时,新兴标准是(当然,您可以为所欲为)为 ivars 使用下划线,并以小写开头的变量名...类的大写,变量的小写,例如:

@synthesize theName = _theName;
@synthesize theAge = _theAge;

其次,在您的 dealloc 中,您将这些 ivars 设置为 nil。虽然这是在 ARC 代码中发布的正确方法,但在非 ARC 代码中,您应该只使用您的 ivars,并使用 release 命令:

- (void)dealloc {
    [_theAge release];
    [_theName release];

    [super dealloc];
}

第三,您是在编写 setter setTheAge 还是在猜测编译器自己在做什么?您的代码可能可以改进(您使用的局部变量与您的属性相同,这只是令人困惑,您想要进行键值通知等),但我不会解决这个问题,因为您最好让编译器执行自己的设置器,除非您要完成其他工作。告诉我。

第四,您的-(Person*)personFromDictionary:(NSDictionary*)dic 是一种奇怪的方法。我建议编写自己的 init,例如:

- (Person*)initFromDictionary:(NSDictionary*) dic 
{
    self = [super init]; // I always inherit from NSObject, in which case you'd have this line

    if (self)
    {
        self.theAge  = [dic objectForKey:kAge];
        self.theName = [dic objectForKey:kName];
    }

    return self;
}

这样,您可以像这样创建 Person 对象:

Person *aPerson = [[Person alloc] initWithDictionary:entryDictionary];

您当前的实现假定Person 对象已经存在(因为您在方法名称前加上“-”而不是“+”),然后创建一个新对象。这有点奇怪。你也许可以做这样的事情,但上面的代码模式更常见,并且实现了我认为你想要的。

最后,我不确定你想用updateEntries 做什么,所以我不确定在那里建议什么,但我不太明白你的池之外的updateEntries,是否你的意思是它是一本充满 Person 对象等的字典。如果你描述你在那里想要完成的事情,我也很乐意给你两分钱。

更新:

顺便说一句,如果您正在调试代码,有时添加 NSLog 语句会很有帮助。不过,您可能希望为您的 Person 类创建一个 description 方法来促进这一点,这样您就可以看到您的 Person 对象的内容,例如:

- (NSString *)description
{
    return [NSString stringWithFormat:@"Person (%p) {\n  theName = '%@'\n  theAge = %@\n}", self, self.theName, self.theAge];
}

这样,如果您有一个名为Person 的对象,例如aRandomPerson,那么您可以有如下语句:

NSLog(@"%@", aRandomPerson);

另外,如果你有一个包含Person 对象的字典条目,如果你NSLog 那个字典,你现在将有一个有意义的日志语句。这可以帮助您诊断 NSDictionary 项目中的内容(如果它真的有 Person 对象,而不仅仅是 NSStringNSNumber 对象)。

【讨论】:

  • 非常详细的答案,我的personFromDictionary居然打错了,其实是一个类方法。但是现在我想知道如果该方法被多次调用会发生什么?这会影响行为吗?
  • @MrShoot 不,如果这是一个类方法,那么它可能有效。但是您调用updateEntries 的sn-p 充其量是难以理解的,并且可能有点可疑。也许为了将我们与不相关的细节隔离开来,您省略了一些将其置于上下文中的代码。 personFromDictionary 变量是如何设置的(顺便说一句,对方法和变量使用相同的名称非常令人困惑)?此外,在 entryDictionary 中,您将键 kAge 设置为 Person(但在 personFromDictionary 方法中,您建议此键为 NSNumber)。 ...
  • @MrShoot 在updateEntries方法中,你也传递了一个updatedDict,然后你说每个key里面都有一个Person。它真的是一个字典,其中每个键都包含一个 Person 对象吗?这些只是一些随机问题,但 updateEntries 代码看起来非常奇怪。此外,看起来您创建了一个池,但在该池之前和之后都有代码。有什么理由不让这一切都在游泳池里吗?也许如果您提供该代码的稍微更完整的 sn-p,也许我们可以理解这一点,但我无法确定您正在尝试做什么。
  • @MrShoot 如果有帮助,也许我们可以继续讨论chat.stackoverflow.com/rooms/13016/…
  • @MrShoot BTW,我已经用description 方法更新了我的答案,如果您想在代码中插入NSLog 语句来诊断各种内容,这非常有用PersonNSDictionary 对象。 NSLog 是你的朋友。
猜你喜欢
  • 1970-01-01
  • 2020-12-28
  • 2012-03-12
  • 1970-01-01
  • 2012-03-18
  • 2015-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多