Отношения NSFetchedResultsController и NSOrderedSet - PullRequest
33 голосов
/ 06 января 2012

У меня проблема (если честно, проблема понимания) с NSFetchedResultsController и новыми отношениями NSOrderedSet, доступными в iOS 5.

У меня есть следующая модель данных (хорошо, моя настоящая модель не выдвижная иsock!На Drawer отношение socks представляет собой упорядоченное отношение ко-многим с Sock.Идея заключается в том, что носки находятся в ящике в определенном порядке.На Sock отношение drawer является инверсией отношения 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;    
}

Когда я запускаю это, я получаю следующую ошибку: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'to-many key not allowed here'

Это имеет смысл для меня, так как отношение представляет собой NSOrderedSet, а не одну сущность для сравнениядля целей сортировки.

Я хочу добиться, чтобы Socks отображался в UITableView в порядке, указанном в отношении socks.Я действительно не хочу иметь порядок сортировки, но NSFetchedResultsController, который является отличным компонентом, настаивает на том, что он должен быть.Как я могу сказать ему использовать порядок носков на сущности Drawer.Я не хочу, чтобы в таблице вообще отображались объекты Drawer.

Примечание: Я использую это только в приложении iOS5, поэтому упорядоченные отношения доступны.

Любой, кто может предложить мне любое направление, будет очень признателен.Спасибо за потраченное время.

Редактировать: Итак, просмотр таблицы, носки дисплея, делают это только для одного ящика.Я просто хочу, чтобы табличное представление соблюдало порядок, который содержит отношение socks.Я не уверен, что установить критерии сортировки, чтобы убедиться, что это происходит.

Ответы [ 6 ]

5 голосов
/ 07 января 2012

Дэмиен

Вы должны просто заставить NSFetchRequest использовать форму массива упорядоченного набора. Это будет работать нормально. Вашему контроллеру нужен атрибут для сортировки. Следовательно, вам нужно будет указать и это.

Andrew

3 голосов
/ 12 сентября 2012

Я нашел эту тему, когда искал ответ на точный вопрос, заданный ОП.

Я не нашел примеров представления таких данных в tableView без добавления дополнительного поля сортировки. Конечно, добавление поля сортировки в значительной степени исключает любые преимущества использования упорядоченных отношений. Поэтому, учитывая, что у меня все получилось, я подумал, что другим людям с таким же вопросом может быть полезно, если я опубликую свой код здесь. Это оказалось довольно просто (гораздо проще, чем использование дополнительного поля сортировки) и с явно хорошей производительностью. То, что некоторые люди, возможно, не поняли (включая меня изначально), это то, что тип (NSOrderedSet) атрибута отношения «упорядочено, ко-многим» имеет метод для получения objectAtIndex, и что NSMUtableOrderedSet имеет методы для вставки и удаления objectAtIndex.

Я избегал использования NSFetchedResultsController, как предлагали некоторые из плакатов. Я не использовал ни массив, ни дополнительный атрибут для сортировки, ни предикат. Мой код имеет дело с tableView, в котором есть один маршрутный объект и много объектов размещения, причем itinerary.places является полем отношения «упорядочено, ко-многим». Я включил редактирование / переупорядочение, но я не удалил ячейки. Метод moveRowAtIndexPath показывает, как я обновил базу данных с помощью переупорядочения, хотя для хорошей инкапсуляции мне, вероятно, следует переместить переупорядочение базы данных в файл моей категории для управляемого объекта. Вот и все 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
3 голосов
/ 09 июня 2012

Вы можете присвоить Sock индексный атрибут и упорядочить носки по ним:

sock01.index = [sock01.drawer.socks indexOfObject:sock01];
2 голосов
/ 06 января 2012

Насколько я понимаю, эта функциональность позволяет заказывать Sock с в каждом Drawer.Как пишет Apple в документации :

Вы должны использовать их только в том случае, если отношение имеет внутреннее упорядочение, критическое для его собственного представления, например, шаги в рецепте.

Это означает, что вы не можете получить все Sock s, используя отсортированное отношение.Сортированные Sock с будут доступны только в каждом Drawer объекте.

1 голос
/ 25 июля 2017

Для большей ясности в ответе от adonoho (спасибо, приятель), который помог мне разобраться в этом - вместо того, чтобы пытаться указать отношение «ко-многим» в качестве любого ключа сортировки, который я тоже не смог получить, укажите принадлежность -в связи в предикате, чтобы выбрать, какие сущности вы хотите в выбранном контроллере результатов, и указать отношение принадлежности к в качестве ключа сортировки.

Это удовлетворяет основной цели получения результатов в 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, и порядок, как определено отношением ко многим.

Глядя на сгенерированный SQL (аннотированный из моего примера с использованием разных имен сущностей) с помощью отладки 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

вы можете увидеть порядок SQL, используя столбец Z_FOK_DRAWER, который Core Data использует для положения этого носка.

1 голос
/ 02 октября 2012

Как я только что ответил здесь , я предпочитаю просто добавить новое свойство в мой NSManagedObject через категорию.

Просто добавьте метод:

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

Тогдав вашем NSFetchedResultsController используйте дескриптор сортировки:

fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"indexInDrawerSocks" ascending:YES]];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...