ExecuteFetchRequest периодически вызывает исключение с теми же параметрами. "не соответствует значению ключа кодирования для ключа" - PullRequest
2 голосов
/ 06 ноября 2010

РЕДАКТИРОВАТЬ Благодаря посту Мэтта я теперь понимаю, что не должен пытаться получить доступ к «запущенному» как к массиву. Однако, если это так, я хотел бы знать, почему этот код работает в других местах. Мне все еще кажется, что это должно быть "один или другой". Это должно работать или не должно.

ORIGINAL Я использую один и тот же запрос на выборку в различных частях моего кода, чтобы найти самую последнюю игру:

Game *lastGame = [[[CoreDataAccess managedObjectContext] fetchObjectsForEntityName:@"Game" withPredicate:@"started == started.@max"] anyObject];

'Игра' - это NSManagedObject, а 'запущено' - атрибут даты. 'start' устанавливается ровно один раз для каждого объекта в awakeFromInsert. Это никогда не меняется после этого. Игра никогда не создается напрямую, но имеет три подкласса. Я пытался сделать игру как абстрактной, так и конкретной, но ни одна из них не влияет на эту проблему.

Я использую категорию NSManagedObjectContext для выполнения выборки, как показано на какао с любовью здесь http://cocoawithlove.com/2008/03/core-data-one-line-fetch.html.

Я получаю следующую ошибку:

Serious application error.  An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:.  [<__NSDate 0xebb1130> valueForUndefinedKey:]: this class is not key value coding-compliant for the key @max. with userInfo {
    NSTargetObjectUserInfoKey = "2010-11-06 11:16:53 GMT";
    NSUnknownUserInfoKey = "@max";
}

Мне кажется, что предикат может пытаться применить @max к одной NSDate вместо всех атрибутов «start» во всех играх. Я не уверен, хотя. Я не очень хорош с предикатами, и мне понадобилось много проб и ошибок, чтобы сделать это. Я не понимаю, как одна и та же выборка может иметь ошибки в разных местах.

Выборка не является частью NSFetchedResultsController, но я использую fetchedResultsController в классе, где я получаю ошибку. Например:

- (void)configureCell:(UITableViewCell*)cell atIndexPath:(NSIndexPath*)indexPath{
Game *game = [self.frc objectAtIndexPath:indexPath];
Game *lastGame = [[[CoreDataAccess managedObjectContext] fetchObjectsForEntityName:@"Game" withPredicate:@"started == started.@max"] anyObject]; // Sometimes we get past this line, sometimes we don't...

NSDateFormatter *format = [[NSDateFormatter alloc] init];
[format setDateFormat:@"EEE, MMM d, yyyy h:mm a"];

if (game != lastGame)
    cell.detailTextLabel.text = [format stringFromDate:game.started];
else
    cell.detailTextLabel.text = @"In Progress";
[format release];
...
}

а также здесь:

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
    Game *lastGame = [[[CoreDataAccess managedObjectContext] fetchObjectsForEntityName:@"Game" withPredicate:@"started == started.@max"] anyObject];
    if (lastGame == [frc objectAtIndexPath:indexPath])
        return NO;
    return YES;
}

Эта точная выборка выполняется несколько раз в нескольких местах, например, при запуске, но происходит сбой только в одном классе. Как я уже сказал, это периодически, но, кажется, это происходит через некоторое время после создания нового объекта Game. Игра создается на одной вкладке, а приведенный выше код взят со второй вкладки, которая отображает историю.

Я видел похожую ошибку здесь . В этом случае проблема была решена путем перезагрузки компьютера, что позволило XCode понять, что атрибут был удален из модели. Я попробовал это, и я все еще испытываю проблему. Я также попытался удалить и воссоздать атрибут «старт» в модели. Я также прочитал Руководство по устранению неполадок с основными данными, но не смог найти там никакой помощи.

1 Ответ

1 голос
/ 09 ноября 2010

Предикаты применяются к одному исходному объекту за раз.Если ваш исходный объект не имеет свойства массива, вы не можете использовать оператор массива.

В вашем случае предикат говорит:

Посмотрите на данную «Игру»,Если его собственное свойство «launch» равно собственному свойству «start» с примененным оператором массива @max KVC, то этот предикат будет истинным.

Это не то, что вам нужно.Единственная ситуация, когда вы используете оператор массива KVC в предикате, - это когда свойство объекта является массивом.например,

Выбор каждого «Сезона», в котором «игры. @ max.homeTeamScore> 50» (т. е. сезонов, когда домашняя команда набрала более 50 очков в игре).Это будет работать, потому что свойство «games» в «Season» будет массивом, поэтому games.homeTeamScore также будет массивом.

Однако свойство «launch» в одной игре не является массивом,Массив, с которым вы хотите работать, на самом деле является массивом всех игр, который не является свойством игры.

Единственный извилистый способ получить доступ к массиву всех игр - это получить массив всехсначала в играх, затем примените оператор массива вне предиката, а затем внутри предиката примените только тест на равенство.

т.е. сначала выберите все игры, а затем повторно:

fetchObjectsForEntityName:@"Game" withPredicate:@"started == %@", [allGames valueForKey:@"@max.started"]

Но это тоже не самая разумная вещь.

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

Вам нужно будет создать вариант метода, который позволяет вам устанавливать setSortDescriptors: по запросу выборки (сортировка по «запущенному», по убыванию), а затем setFetchLimit: 1 в запросе выборки, чтобы получить толькоПервый результат.

...