Что на самом деле возвращает мой запрос на выборку CoreData? - PullRequest
3 голосов
/ 17 августа 2010

Я извлекаю некоторые объекты из хранилища данных, но результаты не соответствуют ожиданиям. Я новичок в CoreData, но я уверен, что это должно работать. Что мне не хватает?

Обратите внимание, что пользователь является допустимым управляемым объектом, и я включаю его заголовочный файл в этот код, а этот идентификатор пользователя является допустимым свойством этого класса.

NSFetchRequest *requestLocal = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"User" inManagedObjectContext:messageManagedObjectContext];
[requestLocal setEntity:entity];
// Set the predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY UserID IN %@", userList];
[requestLocal setPredicate:predicate];
// Set the sorting
... sorting details removed but exist and are fine ...
// Request the data
NSArray *fetchResults = [messageManagedObjectContext executeFetchRequest:requestLocal error:&error];
[requestLocal release];

for (int i; i < [fetchResults count]; i++) {
    [fetchResults objectAtIndex:i].UserID = ...<----HERE
}

Разве fetchResults не является массивом пользовательских объектов? Разве [fetchResults objectAtIndex: i] не будет объектом пользователя? Почему я получаю сообщение об ошибке при создании этого " запроса на член 'UserID' в чем-то, что не является структурой или объединением "?

Извините, если это основная ошибка, мне явно не хватает какой-то базовой концепции. Я сделал кучу поиска, и кажется, что это должно быть правильно. (Я также попробовал быстрое перечисление, но пожаловался, что элементы fetchResults не были действительными объектами Objective C, по-моему, это та же ошибка.)


Обновление:

(из комментария ниже)

Моя цель - обновить объект, вызвав saveAction после его изменения. Метод KVC все еще ссылается на реальный объект? Я пробовал быстрое перечисление с:

for (User thisUser in fetchResults) {

... но это не понравилось.

Я использовал более общую версию:

(id thisUser in fetchResults)

... но это не позволит мне установить

[thisUser valueForKey:@"FirstName"] = anything

... настаивая на том, что нет Lvalue.

Будет:

[[thisUser valueForKey:@"FirstName"] stringWithString:@"Bob"]

... сделать трюк или есть лучший способ? Извините, я знаю, что это почти новый вопрос, но я все еще не понимаю, что находится в массиве fetchResults.

Ответы [ 3 ]

8 голосов
/ 17 августа 2010

Ваша переменная fetchedResults содержит объект NSArray.Однако NSArray может содержать любую произвольную группу объектов.В отличие от стандартного массива C, не требуется, чтобы все объекты NSArray были одного класса.

Точечная нотация, которую вы здесь используете:

[fetchResults objectAtIndex:i].UserID =

... хотя допустимый синтаксис, тем не менее, приводит в замешательство компилятор, потому что компилятор не знает, какой класс объекта возвращается [fetchResults objectAtIndex:i].Не зная класса, он понятия не имеет, что за хрень UserID.Отсюда и ошибка "request for member 'UserID' in something not a structure or union".По крайней мере, вы должны привести приведение [fetchResults objectAtIndex:i] к какому-либо классу, чтобы у компилятора была подсказка, что такое «UserID».

Однако вы не должны использовать эту конструкцию, даже если она законна, потому что она опасна.См. Ниже форму наилучшей практики.

Понимание NSManagedObject и его подклассов может быть сложным, поскольку сам NSManagedObject использует трюк с именем associative storage, который позволяет любым универсальным экземплярам NSManagedObject сохранять любое свойство любого объекта, определенного в любой модели.Это может сбить с толку новичков, поскольку существует несколько способов ссылаться на одни и те же объекты, экземпляры и свойства.Иногда в примерах используются общие NSMangedObjects и setValue:forKey: / valueForKey:, а в других случаях они используют objectInstance.propertyName.

Ассоциативное хранилище работает как словарь, прикрепленный к каждому экземпляру класса NSManagedObject.Когда вы вставляете универсальный NSManagedObject следующим образом:

NSManagedObject *mo=[NSEntityDescription insertNewObjectForEntityForName:@"User" 
                                                  inManagedObjectContext:self.managedObjectContext];

... вы получаете экземпляр класса NSManageObject, для ассоциативных ключей хранения которого установлены свойства сущности User, как определено в вашей модели данных,Затем вы можете установить и получить значения, используя кодирование значения ключа (которое имеет тот же синтаксис, что и словари), таким образом:

[mo setValue:@"userid0001" forKey:@"UserID"];
NSString *aUserID=[mo valueForKey:@"UserID"];

Ассоциативное хранилище позволяет вам представлять любую сложную модель данных в коде без необходимости написания каких-либо пользовательскихNSManagedObject подклассы.(В Какао он позволяет вам использовать привязки, которые позволяют создавать целые программы без написания какого-либо кода управления данными.)

Однако общий класс NSManagedObject немного лучше, чем прославленный словарь, чье сохранение и чтениеобрабатывается автоматически.Если вам нужны объекты данных с настраиваемым поведением, вам нужно явно определить подкласс NSManagedObject.Если вы позволите Xcode сгенерировать класс из сущности в модели данных, то в итоге вы получите исходный файл, например:

User.h
@interface User :  NSManagedObject  
{
}

@property (nonatomic, retain) NSString * firstName;
@property (nonatomic, retain) NSString * userID;
@property (nonatomic, retain) NSString * lastName;

@end

User.m
#import "User.h"


@implementation User 

@dynamic firstName;
@dynamic userID;
@dynamic lastName;

@end

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

User *aUser=[NSEntityDescription insertNewObjectForEntityForName:@"User" 
                                                  inManagedObjectContext:self.managedObjectContext];
aUser.userID=@"userID0001";
NSString *aUserID=aUser.userID;

С учетом всего этого становятся понятными правильные формы ссылки на массив fetchedResults.Предположим, вы хотите установить для всех свойств userID одно значение по умолчанию.Если вы используете универсальный класс NSManagedObject, который вы используете:

for (NSManagedObject *aMO in fetchedResults) {
    [aMO setValue:@"userid0001" forKey:@"UserID"];
    NSString *aUserID=[aMO valueForKey:@"UserID"];
}

Если вы используете выделенный подкласс, вы должны использовать:

for (User *aUserin fetchedResults) {
    aUser.userID=@"userID0001";
    NSString *aUserID=aUser.userID;
}

(Примечание: вы всегда можете использовать универсальную форму для всехNSManagedObject подклассы также.)

2 голосов
/ 17 августа 2010

Ваша основная проблема заключается в том, что -objectAtIndex: возвращает объект типа id.Для типа id методы доступа не определены, поэтому при использовании точечной нотации с объектом, возвращаемым -objectAtIndex:, компилятор предполагает, что вы имеете в виду доступ к члену структуры C.id является типом указателя, а не структурным типом, поэтому вы получаете ошибку.

Весь материал данных ядра - красная сельдь в отношении этой проблемы.Вы получите ту же ошибку, если пользователь был получен из NSObject и вы вручную заполнили массив.

Выход из этого:

  1. Использовать быстрое перечисление

    for (User* aUser in theArray)
    {
        ....
    }
    

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

  2. Привести результат -objectAtIndex: к правильному типу.

    ((User*)[theArray objectAtIndex: i]).userId;
    
  3. Используйте синтаксис отправки сообщений вместо точечной нотации

      [[theArray objectAtIndex: i] setUserId: ...];
    

Лично я бы выбрал 1 и 3.

for (User* aUser in theArray)
{
   [aUser setUserId: ...]
}

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

2 голосов
/ 17 августа 2010

Доступ к вашим атрибутам CoreData по свойству Accessors (точечная запись) будет работать только в том случае, если вы определили пользовательский подкласс NSManagedObject в вашей Модели и определили свойства этого класса. Реализация должна быть @dynamic. Затем вам придется привести объект к соответствующему классу:

//Assume this exists:

@interface User : NSManagesObject 
{
}
@property (nonatomic, retain) NSString* UserID;

@end

@implementation User

@dynamic UserID

@end

// You could do:

for (int i; i < [fetchResults count]; i++) {
    ((User*)[fetchResults objectAtIndex:i]).UserID = ... // This works
}

Или вы можете использовать KVC для доступа к свойствам вашей модели, как это (без использования класса):

for (int i; i < [fetchResults count]; i++) {
    [[fetchResults objectAtIndex:i] valueForKey:@"UserID"] = ... // This too
}

Вы должны установить значение с помощью [object setValue:newValue forKey:@"UserID"], обратите внимание, что newValue должен быть объектом в целом и одним из NSString, NSNumber, NSDate, NSSet для CoreData.

Две дополнительные мысли:

Вы можете и должны использовать быстрое перечисление в массиве результатов:

for (id object in fetchResults) {
    [object valueForKey:@"UserID"] = ...
}

Я не понимаю ЛЮБОГО ключевого слова в вашем предикате. «Идентификатор пользователя IN% @» также должен подойти.

...