对象发布得太早了?

时间:2012-06-25 17:47:35

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

我已经在墙上撞了两天了,试图弄清楚我的代码到底是什么问题。到目前为止,没有。我发现的唯一事情就是一个对象试图自己调用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 dealloc

人setTheAge(bam崩溃)

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

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

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

1 个答案:

答案 0 :(得分:2)

一些想法。

首先,你有:

Person {
    NSString *name;
    NSNumber *age;
}

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

在您的实施中,您有:

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

Apple不再建议你明确地定义你的ivars,但让你的综合语句来处理它(可能因为如果你拼错了其中一个,你最终会得到一个额外的,非预期的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还是在猜测编译器本身在做什么?您的代码可能会得到改进(您使用的是与您的属性相同的局部变量,这只是令人困惑,您想要进行键值通知等),但我不会解决这个问题,因为您除非你想要完成其他任务,否则最好让编译器自己设置setter。让我知道。

第四,你的-(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你的游泳池,无论你是说它是一个充满人物对象的字典等等。如果你描述你想要在那里完成的事情,我也很乐意给你两分钱。

<强>更新

顺便说一句,如果您正在调试代码,有时添加NSLog语句会很有帮助。您可能希望为description类创建Person方法以便于此方法,以便您可以查看Person对象的内容,例如:

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

这样,如果您有一个名为Person的{​​{1}}对象,比如说aRandomPerson,那么您可以使用以下语句:

NSLog(@"%@", aRandomPerson);

此外,如果您有一个包含Person个对象的词典条目,如果您NSLog该词典,您现在将拥有一个有意义的日志语句。这可以帮助您诊断NSDictionary项中的内容(如果它确实包含Person个对象,而不仅仅是NSStringNSNumber个对象。