NSFetchedResultsController和NSOrderedSet关系

我有一个问题(理解问题是诚实)与NSFetchedResultsController和iOS 5中提供的新的NSOrderedSet关系。

我有下面的数据模型(好吧,我真正的不是抽屉和袜子!)但是这是一个简单的例子:

在这里输入图像说明

抽屉和袜子都是核心数据模型/商店中的NSManagedObjects。 在Drawersocks关系是一个有序的对 socks关系。 这个想法是,袜子在特定的顺序在抽屉里。 在Sockdrawer关系是socks关系的socks

在一个UIViewController中,我绘制了一个基于这些实体的UITableView。 我使用NSFetchedResultsController来提供表格。

 - (NSFetchedResultsController *)fetchedResultsController1 { if (_fetchedResultsController1 != nil) { return _fetchedResultsController1; } NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"Sock" inManagedObjectContext:[NSManagedObjectContext MR_defaultContext]]; [fetchRequest setEntity:entity]; NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"drawer.socks" ascending:YES]; [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]]; self.fetchedResultsController1 = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[NSManagedObjectContext MR_defaultContext] sectionNameKeyPath:nil cacheName:@"SocksCache"]; self.fetchedResultsController1.delegate = self; return _fetchedResultsController1; } 

当我运行这个,我得到以下errror: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'to-many key not allowed here'

这对我来说是有意义的,因为关系是一个NSOrderedSet而不是一个单独的实体进行比较以进行sorting。

我想实现的是Socks按照socks关系中指定的顺序出现在UITableView中。 我真的不想要sorting顺序,但NSFetchedResultsController ,这是一个伟大的组件坚持必须有一个。 我怎样才能告诉它使用抽屉实体上的袜子订单。 我不希望表格显示抽屉实体。

注:我只在iOS5应用程序中使用它,所以有序的关系是可用的。

任何人都可以提供给我任何方向,将不胜感激。 谢谢你的时间。

编辑:所以显示的袜子tableview只为一个抽屉。 我只是想要表视图来遵守袜子关系包含的顺序。 我不确定要设置sorting标准以确保发生这种情况。

达米安,

你应该让你的NSFetchRequest使用有序集合的数组forms。 它会运行良好。 你的控制器需要一个属性来sorting。 因此,你也需要指定。

安德鲁

你可以给袜子索引属性,并由他们订购袜子:

 sock01.index = [sock01.drawer.socks indexOfObject:sock01]; 

据我了解这个function,它允许在每个Drawer订购Sock 。 正如苹果在文档中所写的:

只有当一个关系具有对其自身表示至关重要的内在sorting时(例如配方中的步骤),才应该使用它们。

这意味着你不能使用sorting关系来获取所有的Sock 。 分类Sock只能在每个Drawer对象中使用。

我发现这个线程,同时寻找OP提出的确切问题的答案。

我从来没有发现任何在tableView中显示这些数据的例子,而不需要添加额外的sorting字段。 当然,添加sorting字段几乎可以消除使用有序关系的好处。 所以考虑到我得到了这个工作,我认为如果我在这里发布我的代码,可能会有相同的问题有帮助。 事实certificate,这非常简单(比使用额外的sorting字段简单得多),并且performance出色。 有些人可能没有意识到(最初包括我NSOrderedSet )是“有序,一对多”关系属性的types( NSOrderedSet )有一个获取objectAtIndex的方法, NSMUtableOrderedSet有插入和删除objectAtIndex

我避免使用NSFetchedResultsController ,因为一些海报build议。 我没有使用数组,没有额外的属性进行sorting,并没有谓词。 我的代码处理tableView其中有一个行程实体和许多地方的实体,与itinerary.places是“有序,一对多”的关系领域。 我已启用编辑/重新sorting,但没有细胞删除。 moveRowAtIndexPath方法显示了我如何使用重新sorting来更新数据库,尽pipe对于良好的封装,我可能应该将数据库重新sorting到我的类别文件中以获取地点pipe理对象。 这是整个TableViewController.m

 // // ItineraryTVC.m // Vacations // // Created by Peter Polash on 8/31/12. // Copyright (c) 2012 Peter Polash. All rights reserved. // #import "ItineraryTVC.h" #import "AppDelegate.h" #import "Place+PlaceCat.h" #import "PhotosInVacationPlaceTVC.h" @interface ItineraryTVC () @end @implementation ItineraryTVC #define DBG_ITIN YES @synthesize itinerary ; - (id)initWithStyle:(UITableViewStyle)style { self = [super initWithStyle:style]; if (self) { // Custom initialization } return self; } - (void)viewDidLoad { [super viewDidLoad]; self.navigationItem.rightBarButtonItem = self.editButtonItem ; } - (void) viewWillAppear:(BOOL)animated { [super viewWillAppear:animated] ; UIManagedDocument *doc = UIAppDelegate.vacationDoc; [doc.managedObjectContext performBlock:^ { // do this in the context's thread (should be the same as the main thread, but this made it work) // get the single itinerary for this document self.itinerary = [Itinerary setupItinerary: doc ] ; [self.tableView reloadData] ; }]; } - (void)viewDidUnload { [super viewDidUnload]; // Release any retained subviews of the main view. // eg self.myOutlet = nil; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown ); } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.itinerary.places count ]; } - (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath { static NSString *CellIdentifier = @"Itinerary Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier ]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: CellIdentifier]; } Place *place = [self.itinerary.places objectAtIndex:indexPath.row]; cell.textLabel.text = place.name; cell.detailTextLabel.text = [NSString stringWithFormat:@"%d photos", [place.photos count]]; return cell; } #pragma mark - Table view delegate - (BOOL) tableView: (UITableView *) tableView canMoveRowAtIndexPath:( NSIndexPath *) indexPath { return YES; } -(BOOL) tableView: (UITableView *) tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath { return YES ; } -(void) tableView: (UITableView *) tableView moveRowAtIndexPath: (NSIndexPath *) sourceIndexPath toIndexPath: (NSIndexPath *) destinationIndexPath { UIManagedDocument * doc = UIAppDelegate.vacationDoc ; [doc.managedObjectContext performBlock:^ { // perform in the context's thread // itinerary.places is the "ordered, to-many" relationship attribitute pointing to all places in itinerary NSMutableOrderedSet * places = [ self.itinerary.places mutableCopy ] ; Place *place = [ places objectAtIndex: sourceIndexPath.row] ; [places removeObjectAtIndex: sourceIndexPath.row ] ; [places insertObject: place atIndex: destinationIndexPath.row ] ; self.itinerary.places = places ; [doc saveToURL: doc.fileURL forSaveOperation: UIDocumentSaveForOverwriting completionHandler: ^(BOOL success) { if ( !success ) NSLog(@"Error saving file after reorder, startPos=%d, endPos=%d", sourceIndexPath.row, destinationIndexPath.row) ; }]; }]; } - (UITableViewCellEditingStyle) tableView: (UITableView *) tableView editingStyleForRowAtIndexPath: (NSIndexPath *) indexPath { return ( UITableViewCellEditingStyleNone ) ; } - (void) prepareForSegue:(UIStoryboardSegue *) segue sender: (id) sender { NSIndexPath *indexPath = [self.tableView indexPathForCell: sender] ; PhotosInVacationPlaceTVC * photosInVacationPlaceTVC = segue.destinationViewController ; Place *place = [self.itinerary.places objectAtIndex:indexPath.row ]; photosInVacationPlaceTVC.vacationPlace = place ; photosInVacationPlaceTVC.navigationItem.title = place.name ; UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStylePlain target:nil action:nil]; self.navigationItem.backBarButtonItem = backButton; } @end 

正如我刚才在这里回答,我更喜欢通过一个类别添加一个新的属性到我的NSManagedObject。

只需添加一个方法:

 - (NSUInteger)indexInDrawerSocks { NSUInteger index = [self.drawer.socks indexOfObject:self]; return index; } 

然后在你的NSFetchedResultsController中使用sorting描述符:

 fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"indexInDrawerSocks" ascending:YES]]; 

为了让adonoho(thanks mate)帮助我解决问题的答案更加清晰明了 – 而不是试图将任意sorting关键字指定为任何sorting关键字,我也无法工作,请指定属于关系在谓词中select您要在提取的结果控制器中使用的实体,并指定归属关系作为sorting关键字。

这满足了在NSFetchedResultsController中具有所有这些优点的结果的主要目的,并且尊重了多对多关系中的顺序。

在这个特殊的例子中(通过抽屉取袜子):

 // socks belong-to a single drawer, sort by drawer specified sock order fetchRequest.sortDescriptors = @[[[NSSortDescriptor alloc] initWithKey:@"drawer" ascending:YES]]; // drawer has-many socks, select the socks in the given drawer fetchRequest.predicate = [NSPredicate predicateWithFormat:@"drawer = %@", drawer]; 

基本上,这使用给定的抽屉来指定哪些袜子应该进入NSFetchedResultsController,以及由多对多关系指定的顺序。

通过核心数据SQLdebugging来查看生成的SQL(使用不同的实体名称进行注释):

 CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, ...fields..., t0.ZDRAWER, t0.Z_FOK_DRAWER FROM ZSOCKS t0 WHERE t0.ZDRAWER = ? ORDER BY t0.Z_FOK_DRAWER 

您可以使用Z_FOK_DRAWER列来查看SQLsorting,Core Data使用该列来表示该袜子的位置。