NSManagedObject 的发布

发布于 2024-11-09 17:18:49 字数 4558 浏览 0 评论 0原文

我已经将应用程序的一部分基于苹果的 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的使用更加困惑,因为现在无论有没有这些调用,功能似乎都是相同的。我现在的主要问题是他们是否应该在那里。保存在 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);
}

因为我是新用户,所以我无法将数据模型的屏幕截图放在这里,所以这里是它的链接:数据模型屏幕截图

,最后是配方标题:

@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

再次感谢。

I have based a portion of an app on Apple's CoreDataRecipes example code attainable at

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

After some modifications I spent a good few hours tracking down a bug which I must have introduced, but which I solved by removing two lines of code present in apple's code.

I added an author attribute to the NSManagedDataObject recipe, identical in implementation - as far as I could tell - to other string attributes which recipe already had. My new attribute became a zombie after entering and leaving the modal view controlled by IngredientDetailViewController. The dealloc method of IngredientDetailViewController was

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

Having tracked down the bug, I commented out the releases on the recipe and the ingredient (another NSManagedObject) and my app now seems to be functioning. I have now discovered that my code works with or without those release calls; the bug must have been fixed by another change I made. I am now wondering

  1. Why was apple's example code written like this originally?
  2. What was it about the original attributes of the NSManagedObject recipe which meant that they were not susceptible to zombification from the dealloc calls?

If the above hasn't displayed my ignorance enough, I should point out that I am new to Objective C and iPhone development but I would really like to understand what's going on here.

EDITED IN RESPONSE TO COMMENTS AND UPDATED:

I now cannot replicate the zombie creation by uncommenting those lines, obviously another change during bugshooting did the trick. Some of what I originally asked is now invalid but this has left me further confused as to the use of release for NSManagedObjects, since now functionality seems identical with or without those calls. My main question now is just whether or not they should be there. The crash was occuring upon saving in the IngredientDetailView. Here is the header:

@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

and the save method:

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

since I'm a new user I can't put the screenshot of the data model here, so here is a link to it: data model screenshot

and finally the Recipe header:

@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

Thanks again.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一曲琵琶半遮面シ 2024-11-16 17:18:49

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

Please take a look at Core Data documentation, since Core Data “owns” the life-cycle of managed objects you should not be releasing them at all.

与风相奔跑 2024-11-16 17:18:49

唯一一次释放托管对象是在您自己保留它的情况下。鉴于您的属性定义表明它保留了配方和成分对象,因此当您的成分视图控制器被释放时,它需要释放配方和成分对象。

当你做像 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,因为您想维护其中的所有权权益。当您放弃所有权权益时,通过在释放期间释放它,保留计数将减少 1,并且当托管对象上下文释放它时,保留计数将变为零并且将被释放。

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

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

但所有这些都是高级内容,除非您进入更复杂的场景,否则您将不需要它们。

华泰

The only time you would release a managed object would be if you had retained it yourself. Seeing as your property definition says that it is retaining the recipe and ingredient objects, when your ingredientviewcontroller is deallocated, it needs to release the recipe and ingredient objects.

When you do something like myIngredientViewController.ingredient = anIngredient, it's like calling a method which would look something like:

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

So in your save method, when it assigns self.ingredient = ..., that is retaining your object yourself - you now have an ownership interest in that object, so you need to release that in your dealloc.

If you think about it in another way, the managed object context has added 1 to the retain count because it has an ownership interest in it, and you have added 1 to the retain count because you want to maintain an ownership interest in it. When you relinquish your ownership interest, by releasing it during dealloc, the retain count goes down 1 and when the managed object context releases it, the retain count would go to zero and it would be deallocated.

That is how normal objects operate, and how you would treat managed objects in most circumstances, but there are a few caveats for managed objects - as the previous poster indicated, the lifecycle of managed objects is controlled by the managed object context, and there are various things that can happen to a managed object that may mean that although the object still exists, it may be deleted in the context, or a fault in the context, or maybe even reused with different data.

You don't usually have to worry about that, but if you use custom managed objects which have their own instance variables that you need to manage the memory for, or other things you want to do when they are created, fetched, turned into faults etc, then you would need to look at the awakeFromInsert, awakeFromFetch, willTurnIntoFault, didTurnIntoFault etc.

But all that is advanced stuff that you won't need until you get into more complex scenarios.

HTH

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文