我的CoreData获取请求实际返回的是什么?

时间:2010-08-17 07:09:48

标签: iphone objective-c core-data nsarray

我从数据存储中取出一些对象,但结果并不是我所期待的。我是CoreData的新手,但我相当肯定这应该有用。我错过了什么?

请注意,User是一个有效的托管对象,我将此头文件包含在此代码中,并且该UserID是该类的有效属性。

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不是User对象的数组吗?不会 [fetchResults objectAtIndex:i] 成为User对象吗?为什么在构建“请求成员'UserID'而非结构或联合”时出现错误?

很抱歉,如果这是一个基本错误,我显然错过了一些基本概念。我做了大量的搜索,看起来应该是正确的。 (我也试过快速枚举,但它抱怨fetchResults项目不是有效的Objective C对象,我认为实际上是同样的错误。)


更新

(来自下面的评论)

我的目标是更新对象,更改后调用saveAction。 KVC方法是否仍然引用实际对象?我尝试使用以下快速枚举:

for (User thisUser in fetchResults) {

......但它不喜欢那样。

我使用了更通用的版本:

(id thisUser in fetchResults)

...但它不会让我设置

[thisUser valueForKey:@"FirstName"] = anything

......坚持没有左值。

威尔:

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

......诀窍还是有更好的方法?对不起,我知道这几乎是一个新问题,但我仍然没有得到fetchResults数组中的内容。

3 个答案:

答案 0 :(得分:8)

您的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子类。 (在Cocoa中,它允许您使用绑定来创建整个程序,而无需编写任何数据管理代码。)

但是,通用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子类使用通用表单。)

答案 1 :(得分:2)

按属性访问CoreData属性只有在模型中定义了自定义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,NSData for CoreData。

另外两个想法:

你可以而且应该在结果数组上使用快速枚举:

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

我不理解谓词中的ANY关键字。 “UserID IN%@”也应该这样做。

答案 2 :(得分:2)

您的基本问题是-objectAtIndex:重新绑定id类型的对象。没有为类型id定义访问器,因此当您使用带有-objectAtIndex:返回的对象的点表示法时,编译器会假定您要访问C结构成员。 id是指针类型,而不是结构类型,因此会出现错误。

关于这个问题,整个核心数据是一个红色的鲱鱼。如果User是从NSObject派生的,并且您自己手动填充了数组,则会收到相同的错误。

它的出路是:

  1. 使用快速枚举

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

    如果您需要遍历整个数组

  2. ,这是首选的习惯用法
  3. -objectAtIndex:的结果投射到正确的类型。

    ((User*)[theArray objectAtIndex: i]).userId;
    
  4. 使用消息发送语法而不是点符号

      [[theArray objectAtIndex: i] setUserId: ...];
    
  5. 就个人而言,我会选择1 3。

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

    如果你不确定数组中的对象是User对象,显然上述任何一个都是危险的。您可以使用-respondsToSelector:确保它可以正常使用。