iphone, 以多对多关系保存数据, 核心数据
Posted
技术标签:
【中文标题】iphone, 以多对多关系保存数据, 核心数据【英文标题】:iphone, Saving data in to-many relationship, Core Data 【发布时间】:2010-11-26 03:51:52 【问题描述】:我正在使用 Core Data 开发一个小型 iphone 应用程序,其中我有一个 Person 和一个 Image 实体。 Person 和 Image 之间的关系是对多的。一个人对许多图像。
问题发生在保存方法。在首先保存期间,我需要从 ImagePickerViewController 获得的多个图像数据“添加”到 Person,然后将 Person 实体“保存”到实际数据库。 ((用同样的方法))
if (managedObjectContext == nil)
managedObjectContext = [(CoreDataCombine01AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
//???: how to init _PersonImage?
_PersonImage = (PersonImage *)[NSEntityDescription insertNewObjectForEntityForName:@"PersonImage" inManagedObjectContext:managedObjectContext];
//_PersonImage = [_PersonImage init];
//_PersonImage = [[_PersonImage alloc] init];
_PersonImage.originalImage = imageData;
managedObjectContext = nil;
[managedObjectContext release];
.
if (managedObjectContext == nil)
managedObjectContext = [(CoreDataCombine01AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
person = (Person *)[NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:managedObjectContext];
[person addPersonToPersonImageObject:_PersonImage];//runTime error
[_PersonImage release];
.
NSError *error = nil;
if (![person.managedObjectContext save:&error])
// Handle error
NSLog(@"Unresolved error at save %@, %@", error, [error userInfo]);
exit(-1); // Fail
然后我收到错误消息:
"-[NSConcreteMutableData CGImage]: unrecognized selector sent to instance"
和:
"*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteMutableData CGImage]: unrecognized selector sent to instance".
我猜这个错误来自 从一个 managedObjectContext 中双重使用 NSEntityDescription。所以我只是初始化了 Person 类,它自动从数据模型派生并手动导入,就像注释行一样,而不是使用 managedObjectContext。
它没有给出任何错误,但当我点击保存按钮时会出现运行时错误。
当我使用一对一关系时,保存没有问题,所以我猜对 Person 使用 managedObjectContext 是正确的方法。但是,一旦我使用多对多关系,我需要将图像数据保存到 PersonImage 实体。并且必须以某种方式初始化实体。
我错过了什么?
【问题讨论】:
【参考方案1】:好的。
我查看了您在 GitHub 上发布的代码,发现您的 DetailView 控制器存在一些问题,我将在下面概述。
首先,您没有正确地将托管对象上下文传递给详细视图,这意味着当您尝试保存对象时,没有可以从中保存它们的上下文。将托管对象上下文视为持久存储的“草稿”。您对 NSManagedObject 所做的任何更改都将被跟踪并保留在您的上下文中,直到您使用 [managedObjectContext save]
命令将它们持久化。
为了清楚起见,您是在 AppDelegate 中创建上下文,然后使用 rootViewController.managedObjectContext = self.managedObjectContext
将对它的引用传递给您的 RootViewController
从 RootViewController,您需要使用相同的技术将 managedObjectContext 向下传递给 DetailView。因此,在您的 tableView:didSelectRowAtIndexPath:
方法中,您应该传递对上下文的引用,以便您对该视图所做的任何更改都将被跟踪并最终被持久化:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
DetailView *detailView = [[DetailView alloc] initWithNibName:@"DetailView" bundle:nil];
detailView.event = (Event *)[[self fetchedResultsController] objectAtIndexPath:indexPath];
detailView.managedObjectContext = self.managedObjectContext;
// ...
[self.navigationController pushViewController:detailView animated:YES];
[detailView release];
我还更新了对 managedObjectContext 的所有其他引用,您在其中实例化了一个新的上下文对象,以简单地指向 self.managedObjectContext,即:
JustString *_justString = [NSEntityDescription insertNewObjectForEntityForName:@"JustString" inManagedObjectContext:self.managedObjectContext];
一旦解决了这个问题,只有另一件事阻止您正确保存图像对象,TechZen 在上面谈到了这一点。
在您的 DetailView 控制器中,您有一个 imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
方法,您可以将 UIImage 转换为 NSData,然后将其分配给您的 Image 实体实例。
问题在于您的对象模型(参见 Event.m)中已经有一个名为 -(id)transformedValue:(id)value
的转换器方法。
所以基本上你试图将 UIImage 转换为 NSData,然后将 NSData 传递给实体,这实际上是在期待 UIImage。在这种情况下,我建议您让您的对象模型处理数据转换,因此在您的 DetailView 控制器中,注释掉 UIImage 到 NSData 转换器代码,并将图像直接传递给您的托管对象:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
UIImage *selectedImage = [info objectForKey:@"UIImagePickerControllerOriginalImage"];
// Transform the image to NSData
// ImageToDataTransformer *transformer = [[[ImageToDataTransformer alloc] init] autorelease];
// NSData *imageData = [transformer transformedValue:selectedImage];
Image *_image = [NSEntityDescription insertNewObjectForEntityForName:@"Image" inManagedObjectContext:self.managedObjectContext];
_image.justImage = selectedImage;
[event addEventToImageObject:_image];
[picker dismissModalViewControllerAnimated:YES];
在那之后,你应该好好去尝试一下,如果它解决了你的问题,请告诉我们。
CoreData 的学习曲线可能有点陡峭,但一旦你“掌握”了它,你就会意识到它是 ios 开发中可用的最精美的 API 之一。
祝你好运,让我们知道你的进展! 罗格
【讨论】:
嗨,罗格,在我根据您的建议处理之前有 2 个问题。 Q1) 是 _PersonImage.originalImage = imageData;与 [personImage setValue:imageData forKey:@"originalImage"];? Q2) 我们不使用 add'relationshipName'Object 方法吗?多对多关系免费提供的方法。 您好! Q1 - 正确,前提是您为 PersonImage 对象声明了 @property。我个人的偏好是 setValue:forKey。 Q2 - 不熟悉该方法,但我想它们必须是可以互换的。 我申请了 '[person setValue:personImage forKey:@"nameOfYourPersonToImageRelationShipHere"] ' 并且 Xcode 给我错误提示 "*** Terminating app due to unaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of一对多关系的值:属性 =“PersonToPersonImage”;所需类型 = NSSet;给定类型 = PersonImage;值 =您收到的错误消息是由于您混淆了 UIImage 实例的 NSData 实例。
与 NSString、NSArray 和类似的核心类一样,NSData 是一个类集群而不是单个类。 NSConcreteMutableData 是集群中的类之一。
您的问题出现是因为您试图将消息 CGImage
发送到当然没有 CGImage
方法的 NSData 实例。
由于您必须使用值转换器将图像存储在 Core Data 属性中,我建议您查看值转换器和/或分配图像的方法。
查看您的代码,我怀疑您将 Person.originalImage
set 作为可转换属性,但您尝试为其分配一个 NSData 对象。当值转换器尝试转换 NSData 实例时认为它是 UIImage,它会生成错误。
【讨论】:
感谢 TechZen。你是对的。问题出在滥用变压器方法。以上是关于iphone, 以多对多关系保存数据, 核心数据的主要内容,如果未能解决你的问题,请参考以下文章
通过链接到SQL Server数据库的MS Access以多对多关系插入数据(中间表)
在 MS Access 中,如何以多对多关系列出记录,以使所列出的表中的记录不重复?