2012-01-06 15 views
28

Estoy teniendo un problema (problema de la comprensión para ser honesto) con NSFetchedResultsController y las nuevas relaciones NSOrderedSet disponibles en iOS 5.NSFetchedResultsController y NSOrderedSet relaciones

tengo el siguiente modelo de datos (bien, mi verdadero uno no está cajón de calcetines y de) pero esto sirve como un ejemplo sencillo:

enter image description here

cajón del calcetín y son ambos NSManagedObjects en un modelo/almacén de datos básicos. En Drawer la relación socks es una ordenada a muchas relaciones a Sock. La idea es que los calcetines estén en el cajón en un orden específico. En Sock, la relación drawer es la inversa de la relación socks.

En un UIViewController estoy dibujando un UITableView basado en estas entidades. Estoy alimentando la mesa usando un 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;  
} 

Cuando ejecuto esto, me sale el siguiente errror: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'to-many key not allowed here'

Esto tiene sentido para mí, ya que la relación es una NSOrderedSet y no una sola entidad con el que comparar con fines de clasificación.

Lo que quiero lograr es que el Socks aparezca en el UITableView en el orden especificado en la relación socks. Realmente no quiero tener un orden de clasificación sino NSFetchedResultsController, que es un gran componente que insiste en que tiene que haber uno. ¿Cómo puedo decirle que use el orden de los calcetines en la entidad del cajón? No quiero que la tabla muestre entidades de cajones.

Nota: Estoy usando esto solo en una aplicación iOS5, por lo que las relaciones ordenadas están disponibles.

Cualquier persona que pueda ofrecerme cualquier dirección, sería muy apreciada. Gracias por tu tiempo.

Edit: Así que la tabla que muestra los calcetines lo hace para solo un cajón. Solo quiero que la vista de tabla respete el orden que contiene la relación de calcetines. No estoy seguro de qué establecer los criterios de clasificación para garantizar que eso suceda.

+0

Damien, parece que ha resuelto esto. Cuando dice "en el orden especificado en la relación 'calcetines'", ¿cómo hace para especificar el orden de la relación? En el editor, puedo ver una casilla de verificación (ordenada o no), pero nada para controlar los criterios de clasificación. – jhabbott

+0

@jhabbott. Cuando especifica una relación ordenada (usando la casilla de verificación), la relación se representa mediante un NSOrderedSet. A continuación, puede usar los métodos de conjuntos ordenados habituales para controlar el orden. – Damien

+0

Bien, ahora empiezo a usar el método 'insertObject: inSocksAtIndex:' generado, pero recibo una excepción de selector no reconocido. Pensé que el contexto del objeto administrado agregaría esto en tiempo de ejecución? – jhabbott

Respuesta

2

Por lo que entiendo esta funcionalidad, permite ordenar Sock s en cada Drawer. A medida que Apple escribe en documentation:

Se debe utilizar sólo si tiene una relación de orden intrínseco que es fundamental para su propia representación, tales como los pasos de una receta.

Esto significa que no puede recuperar todos Sock s utilizando la relación ordenada. Ordenado Sock s estará disponible solo en cada objeto Drawer.

+0

Hola, gracias por la respuesta. Lo siento, no estaba siendo claro. La mesa representa un cajón, por lo que solo quiero que la mesa muestre los calcetines en ese cajón en particular. Puedo hacer eso, pero lo que no sé es cómo establecer los criterios de clasificación para que muestre los calcetines en el orden correcto según la relación ordenada de los calcetines de la entidad matriz (el cajón). Actualizaré mi pregunta. Gracias de nuevo. – Damien

5

Damien,

sólo debe garantizar que utiliza el NSFetchRequest la forma array del conjunto ordenado. Funcionará bien. Su controlador necesita un atributo para ordenar.Por lo tanto, deberá especificar eso también.

Andrew

+0

Hola Andrew. Muchas gracias por tomarse el tiempo para responder la pregunta. Creo que entiendo lo que estás diciendo y sé que puedo obtener un NSArray de un conjunto ordenado. No estoy seguro de cómo alimentar eso en una NSFetchRequest. Probablemente estoy pensando demasiado en esto. ¿Podrías empujarme en la dirección correcta? – Damien

+7

Damien, un predicado simple es todo lo que necesitas. Por ejemplo: '@" self en% @ ", orderedSet.array'. El descriptor de clasificación debería ser igualmente simple: elija un atributo que no cambie el orden de clasificación. Andrew – adonoho

+1

Hola Andrew, gracias un millón por la asistencia. Eso funciono. Era el tipo de orden de lo que en realidad no cambiaba lo que era el poco pensamiento que me estaba perdiendo. Algo en una entidad que podría ser difícil de encontrar tal atributo, así que estoy pensando que a veces podría tener un atributo ficticio que no cambie el orden de clasificación. Parece que NSFetchedResultsController podría necesitar ponerse al día un poco con las relaciones ordenadas – Damien

3

Usted puede dar un atributo del calcetín índice y ordenar los calcetines por ellos:

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

+1 vea también [Orden de Core Data con UITableView y NSOrderedSet] (http://stackoverflow.com/questions/9875557/core-data-ordering-with-a-uitableview-and-nsorderedset/11188696#11188696) –

+1

Doesn ¿Eso significa buscar todos los calcetines en sock01.drawer, que es exactamente el tipo de cosa que utiliza un NSFetchedResultsController para evitar? –

2

He encontrado este hilo, mientras que en busca de una respuesta a la pregunta exacta que plantea la OP.

Nunca encontré ningún ejemplo de presentación de dichos datos en un tableView sin agregar el campo de clasificación adicional. Por supuesto, agregar el campo de ordenación prácticamente elimina cualquier beneficio al usar la relación ordenada. Entonces, dado que lo hice funcionar, pensé que podría ser útil para otros con la misma pregunta si publicara mi código aquí. Resultó ser bastante simple (mucho más simple que usar un campo de clasificación adicional) y con un rendimiento aparentemente bueno. Lo que algunas personas pueden no haberse dado cuenta (eso me incluye a mí, inicialmente) es que el tipo (NSOrderedSet) del atributo de relación "ordenado, a muchos" tiene un método para obtener objectAtIndex, y que NSMUtableOrderedSet tiene métodos para insertar y eliminar objectAtIndex .

Evité usar el NSFetchedResultsController, como algunos de los carteles sugirieron. No he usado ninguna matriz, ningún atributo adicional para clasificar y ningún predicado. Mi código trata de un tableView en donde hay una entidad de itinerario y muchas entidades de lugar, siendo itinerary.places el campo de relación "ordenado, a muchos". He habilitado la edición/reordenación, pero no eliminaciones de celdas. El método moveRowAtIndexPath muestra cómo he actualizado la base de datos con el reordenamiento, aunque para una buena encapsulación, probablemente debería mover el reordenamiento de la base de datos a mi archivo de categoría para el objeto gestionado por lugar. Aquí está toda la 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. 
    // e.g. 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 
1

Como acabo respondí here, prefiero simplemente añadiendo una nueva propiedad a mi NSManagedObject a través de una categoría.

Sólo tiene que añadir un método:

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

Luego, en su NSFetchedResultsController, descriptor de uso para ordenar:

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

Esto no parece funcionar con configuraciones respaldadas por SQL. Intenté esto y la solicitud de búsqueda arroja una excepción: '*** Aplicación de terminación debido a una excepción no detectada 'NSInvalidArgumentException', razón: 'keypath indexInBookChapters no encontrado en la entidad ' ' – acoward

0

Para añadir un poco más de claridad a la respuesta de adonoho (gracias compañero) que me ayudó trabajo en lugar de tratar de especificar la relación a muchos como una clave de clasificación que tampoco pude obtener, especifique la relación de pertenecer a un predicado para seleccionar qué entidades desea en el controlador de resultados obtenidos y especifique la relación pertenece a la relación como el tipo k ey.

Esto cumple el objetivo principal de tener los resultados en un NSFetchedResultsController con toda esa bondad, y respeta el orden en la relación de muchos.

En este ejemplo en particular (ir a buscar los calcetines por cajón):

// 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]; 

En esencia, este utiliza un cajón determinado para especificar los calcetines deben entrar en el NSFetchedResultsController, y el orden según lo especificado por la relación-a-muchos .

Mirando el SQL generado (anotado de mi ejemplo usando diferentes nombres de entidad) a través de un núcleo de SQL datos de depuración:

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 

se puede ver el orden SQL utilizando la columna Z_FOK_DRAWER, que utiliza la base de datos para el posición de ese calcetín.

Cuestiones relacionadas