我已将Apple的CoreDataRecipes示例代码中的应用程序的一部分基于
http://developer.apple.com/library/ios/#samplecode/iPhoneCoreDataRecipes/Introduction/Intro.html
经过一些修改后,我花了几个小时来追踪我必须介绍的一个bug,但是我通过删除Apple代码中的两行代码解决了这个问题。
我在NSManagedDataObject配方中添加了一个author属性,在实现方面完全相同 - 据我所知 - 与其他配方已有的字符串属性相同。进入和离开由IngredientDetailViewController控制的模态视图后,我的新属性变为僵尸。 IngredientDetailViewController的dealloc方法是
- (void)dealloc {
[recipe release];
[ingredient release];
[super dealloc];
}
跟踪了这个bug后,我评论了配方和配料(另一个NSManagedObject)上的版本,我的应用程序现在似乎正在运行。我现在发现我的代码可以使用或不使用那些释放调用;我必须通过另一项改变来解决这个问题。我现在想知道
如果上述内容没有充分显示我的无知,我应该指出我是Objective C和iPhone开发的新手,但我真的很想了解这里发生了什么。
对评论和更新的回应编辑:
我现在无法通过取消注释这些线来复制僵尸创建,显然在错误检测期间的另一个变化就是诀窍。我最初提出的一些问题现在已经无效了,但这让我对NSManagedObjects的发布使用更加困惑,因为现在功能似乎与这些调用相同或不相同。我现在的主要问题是他们是否应该在那里。保存在IngredientDetailView中时发生了崩溃。这是标题:
@class Recipe, Ingredient, EditingTableViewCell;
@interface IngredientDetailViewController : UITableViewController {
@private
Recipe *recipe;
Ingredient *ingredient;
EditingTableViewCell *editingTableViewCell;
}
@property (nonatomic, retain) Recipe *recipe;
@property (nonatomic, retain) Ingredient *ingredient;
@property (nonatomic, assign) IBOutlet EditingTableViewCell *editingTableViewCell;
@end
和保存方法:
- (void)save:(id)sender {
NSManagedObjectContext *context = [recipe managedObjectContext];
/*
If there isn't an ingredient object, create and configure one.
*/
if (!ingredient) {
self.ingredient = [NSEntityDescription insertNewObjectForEntityForName:@"Ingredient"
inManagedObjectContext:context];
[recipe addIngredientsObject:ingredient];
ingredient.displayOrder = [NSNumber numberWithInteger:[recipe.ingredients count]];
}
/*
Update the ingredient from the values in the text fields.
*/
EditingTableViewCell *cell;
cell = (EditingTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
ingredient.name = cell.textField.text;
cell = (EditingTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]];
ingredient.amount = cell.textField.text;
/*
Save the managed object context.
*/
NSError *error = nil;
if (![context save:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate.
You should not use this function in a shipping application, although it may be useful during development.
If it is not possible to recover from the error, display an alert panel that instructs the user to quit the
application by pressing the Home button.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
[self.navigationController popViewControllerAnimated:YES];
NSLog(@"in ingredient detail save after ingredient pop; - recipe.author is %@", recipe.author);
}
因为我是新用户,所以我不能在这里放置数据模型的屏幕截图,所以这里有一个指向它的链接:data model screenshot
最后是Recipe标题:
@interface ImageToDataTransformer : NSValueTransformer {
}
@end
@interface Recipe : NSManagedObject {
}
@property (nonatomic, retain) NSString *instructions;
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSString *overview;
@property (nonatomic, retain) NSString *prepTime;
@property (nonatomic, retain) NSSet *ingredients;
@property (nonatomic, retain) UIImage *thumbnailImage;
@property (nonatomic, retain) NSString *author;
@property (nonatomic) BOOL *isDownloaded;
@property (nonatomic) BOOL *isSubmitted;
@property (nonatomic, retain) NSString *uniqueID;
@property (nonatomic) float averageRating;
@property (nonatomic) float numberOfRatings;
@property (nonatomic, retain) NSManagedObject *image;
@property (nonatomic, retain) NSManagedObject *type;
@end
@interface Recipe (CoreDataGeneratedAccessors)
- (void)addIngredientsObject:(NSManagedObject *)value;
- (void)removeIngredientsObject:(NSManagedObject *)value;
- (void)addIngredients:(NSSet *)value;
- (void)removeIngredients:(NSSet *)value;
@end
再次感谢。
答案 0 :(得分:2)
请查看Core Data文档,因为Core Data“拥有”托管对象的生命周期,您根本不应该发布它们。
答案 1 :(得分:2)
如果你自己保留托管对象,那么你唯一一次会释放托管对象。看到你的属性定义说它保留了配方和成分对象,当你的ingredientviewcontroller被释放时,它需要释放配方和成分对象。
当你执行像myIngredientViewController.ingredient = anIngredient这样的事情时,就像调用一个看起来像这样的方法:
- (void)setIngredient:(Ingredient *)ing {
[self willChangeValueForKey:@"ingredient"];
Ingredient *oldIngredient = ingredient;
ingredient = [ing retain];
[oldIngredient release];
[self didChangeValueForKey:@"ingredient"];
}
所以在你的save方法中,当它指定self.ingredient = ...时,你自己保留了你的对象 - 你现在对该对象拥有所有权,所以你需要在你的dealloc中释放它。
如果以另一种方式考虑它,托管对象上下文已将保留计数添加1,因为它对其具有所有权权益,并且您已将保留计数添加1,因为您希望保留所有权权益它。当您放弃所有权利益时,通过在dealloc期间释放它,保留计数下降1,当托管对象上下文释放它时,保留计数将归零并且将被解除分配。
这就是普通对象的操作方式,以及在大多数情况下如何处理托管对象,但是托管对象有一些注意事项 - 如前面的海报所示,托管对象的生命周期由托管对象上下文控制,并且管理对象可能发生各种各样的事情,这可能意味着虽然对象仍然存在,但它可能在上下文中被删除,或者在上下文中被删除,或者甚至可能与不同的数据一起被重用。
您通常不必担心这一点,但是如果您使用自定义托管对象,这些对象具有您需要管理内存的自己的实例变量,或者在创建,提取时要执行的其他操作,变成了故障等,然后你需要看看awakeFromInsert,awakeFromFetch,willTurnIntoFault,didTurnIntoFault等。
但是所有这些都是您在进入更复杂的场景之前不需要的高级内容。
HTH