1

我的应用程序的一部分基于 Apple 的 CoreDataRecipes 示例代码,可在

http://developer.apple.com/library/ios/#samplecode/iPhoneCoreDataRecipes/Introduction/Intro.html

经过一些修改后,我花了好几个小时追踪一个我必须引入的错误,但我通过删除苹果代码中存在的两行代码解决了这个问题。

我在 NSManagedDataObject 配方中添加了一个作者属性,在实现上相同 - 据我所知 - 到配方已经具有的其他字符串属性。我的新属性在进入和离开由 IngredientDetailViewController 控制的模态视图后变成了僵尸。IngredientDetailViewController 的 dealloc 方法是

- (void)dealloc {
    [recipe release];
    [ingredient release];
    [super dealloc];
}

找到错误后,我注释掉了配方和成分(另一个 NSManagedObject)的版本,我的应用程序现在似乎正在运行。我现在发现我的代码在有或没有这些发布调用的情况下都可以工作;该错误必须已通过我所做的另一项更改修复。我现在想知道

  1. 为什么苹果的示例代码最初是这样写的?
  2. NSManagedObject 配方的原始属性是什么,这意味着它们不容易受到 dealloc 调用的僵尸化?

如果以上内容还不足以表明我的无知,我应该指出我是 Objective C 和 iPhone 开发的新手,但我真的很想了解这里发生了什么。

针对评论进行编辑并更新:

我现在无法通过取消注释这些行来复制僵尸创建,显然在错误排除期间的另一个更改起到了作用。我最初询问的一些内容现在无效,但这让我对 NSManagedObjects 的 release 使用更加困惑,因为现在无论是否有这些调用,功能似乎都相同。我现在的主要问题是他们是否应该在那里。保存在成分详细信息视图中时发生崩溃。这是标题:

@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);
}

由于我是新用户,数据模型的截图不能放在这里,所以这里有一个链接:数据模型截图

最后是配方标题:

@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

再次感谢。

4

2 回答 2

2

您释放托管对象的唯一时间是您自己保留了它。看到您的属性定义说它保留配方和成分对象,当您的成分视图控制器被释放时,它需要释放配方和成分对象。

当您执行 myIngredientViewController.ingredient = anIngredient 之类的操作时,就像调用一个看起来像这样的方法:

- (void)setIngredient:(Ingredient *)ing {
  [self willChangeValueForKey:@"ingredient"];
  Ingredient *oldIngredient = ingredient;
  ingredient = [ing retain];
  [oldIngredient release];
  [self didChangeValueForKey:@"ingredient"];
}

因此,在您的保存方法中,当它分配 self.ingredient = ... 时,您自己会保留您的对象 - 您现在对该对象拥有所有权权益,因此您需要在您的 dealloc 中释放它。

如果换一种方式考虑,托管对象上下文已将 1 添加到保留计数,因为它在其中拥有所有权权益,并且您已将 1 添加到保留计数,因为您想保持对它的所有权权益。当您放弃您的所有权权益时,通过在 dealloc 期间释放它,保留计数下降 1,当托管对象上下文释放它时,保留计数将变为零并且将被释放。

这就是普通对象的操作方式,以及在大多数情况下您将如何处理托管对象,但是对于托管对象有一些注意事项 - 正如之前的海报所指出的,托管对象的生命周期由托管对象上下文控制,并且有托管对象可能发生的各种事情可能意味着尽管该对象仍然存在,但它可能在上下文中被删除,或者上下文中的错误,甚至可能与不同的数据一起重用。

您通常不必担心这一点,但是如果您使用自定义托管对象,这些对象具有自己的实例变量,您需要管理内存,或者在创建、获取、变成错误时想要做的其他事情等等,那么您需要查看 awakeFromInsert、awakeFromFetch、willTurnIntoFault、didTurnIntoFault 等。

但所有这些都是高级的东西,在你进入更复杂的场景之前你不需要。

高温高压

于 2011-07-29T04:39:05.643 回答
2

请查看 Core Data 文档,因为 Core Data “拥有”托管对象的生命周期,所以您根本不应该释放它们。

于 2011-05-27T14:43:03.103 回答