我从数据存储中取出一些对象,但结果并不是我所期待的。我是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数组中的内容。
答案 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派生的,并且您自己手动填充了数组,则会收到相同的错误。
它的出路是:
使用快速枚举
for (User* aUser in theArray)
{
....
}
如果您需要遍历整个数组
将-objectAtIndex:
的结果投射到正确的类型。
((User*)[theArray objectAtIndex: i]).userId;
使用消息发送语法而不是点符号
[[theArray objectAtIndex: i] setUserId: ...];
就个人而言,我会选择1 和 3。
for (User* aUser in theArray)
{
[aUser setUserId: ...]
}
如果你不确定数组中的对象是User对象,显然上述任何一个都是危险的。您可以使用-respondsToSelector:
确保它可以正常使用。