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

发布于 2024-09-14 19:27:36 字数 1675 浏览 3 评论 0原文

我正在从数据存储中获取一些对象,但结果不是我所期望的。我是 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"] = everything

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

会:

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

... 可以解决这个问题还是有更好的方法?抱歉,我知道这几乎是一个新问题,但我仍然不明白 fetchResults 数组中的内容。

I'm fetching some objects out of a data store but the results aren't what I'm expecting. I'm new to CoreData but I'm fairly certain this should work. What am I missing?

Note that User is a valid managed object and that I include its header file in this code, and that UserID is a valid property of that class.

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
}

Isn't fetchResults an array of User objects? Wouldn't [fetchResults objectAtIndex:i] be a User object? Why do I get an error when building that "request for member 'UserID' in something not a structure or union"?

Sorry if this is a basic error, I'm clearly missing some basic concept. I've done a ton of searching and it seems like it should be right. (I also tried fast enumeration but it complained that fetchResults items weren't valid Objective C objects, effectively the same error, I think.)


Update:

(from comment below)

My goal is to update the object, calling saveAction after changing it.
Does the KVC method still refer to the actual object? I tried fast enumeration with:

for (User thisUser in fetchResults) {

... but it didn't like that.

I used the more generic version:

(id thisUser in fetchResults)

...but it won't let me set

[thisUser valueForKey:@"FirstName"] = anything

... insisting that there's no Lvalue.

Will:

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

... do the trick or is there a better way? Sorry, I know it's nearly a new question, but I still don't get what is in the fetchResults array.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

蛮可爱 2024-09-21 19:27:36

您的 fetchedResults 变量包含一个 NSArray 对象。然而,NSArray 可以保存任意对象组。与标准 C 数组不同,不要求 NSArray 对象都属于单个类。

您在此处使用的点表示法:

[fetchResults objectAtIndex:i].UserID =

... 虽然是合法语法,但仍然会让编译器感到困惑,因为编译器不知道 [fetchResults objectAtIndex:i] 返回什么类的对象。如果不知道这个类,它就不知道 UserID 到底是什么。因此,错误“请求非结构或联合中的成员‘UserID’”。至少,您必须将 [fetchResults objectAtIndex:i] 的返回值转换为某个类,以便编译器知道“UserID”是什么。

但是,即使这种结构合法,您也不应该使用它,因为它很危险。请参阅下面的最佳实践表格。

理解 NSManagedObject 及其子类可能很棘手,因为 NSManagedObject 本身使用了一种称为关联存储的技巧,它允许任何通用 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 子类使用通用形式。)

Your fetchedResults variable contains a NSArray object. However, a NSArray can hold any arbitrary group of objects. Unlike a standard C array, there is no requirement that the NSArray objects all be of a single class.

The dot notation you are using here:

[fetchResults objectAtIndex:i].UserID =

... while a legal syntax, nevertheless confuses the compiler because the compiler has no idea what class of object is returned by [fetchResults objectAtIndex:i]. Without knowing the class it has no idea what the heck UserID is. Hence the error "request for member 'UserID' in something not a structure or union". At the very least you have to cast the return of [fetchResults objectAtIndex:i] to some class so that the complier has a clue as to what 'UserID' is.

However, you simply shouldn't use this construction even though it legal because it is dangerous. See below for the best practice form.

Understanding NSManagedObject and its subclasses can be tricky because NSManagedObject itself uses a trick called associative storage which allows any generic NSManagedObject instances to store any property of any entity defined in any model. This can confuse novices because there are multiple ways to refer to the same entities, instances and properties. Sometimes the examples use generic NSMangedObjects and setValue:forKey:/valueForKey: and other times they use objectInstance.propertyName.

Associative storage works like a dictionary attached to every instance of the NSManagedObject class. When you insert a generic NSManagedObject like this:

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

... you get an instance of the NSManageObject class whose associative storage keys are set to the properties of the User entity as defined in your data model. You can then set and retrieve the values using key-value coding (which has the same syntax as dictionaries) thusly:

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

Associative storage allows you represent any complex data model in code without having to write any custom NSManagedObject subclasses. (In Cocoa, it allows you to use bindings which let you create entire programs without writing any data management code at all.)

However, the generic NSManagedObject class is little better than a glorified dictionary whose saving and reading is handled automatically. If you need data objects with customized behaviors you need to explicitly define a NSManagedObject subclass. If you let Xcode generate the class from the entity in the data model you end up with a source file something like:

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

Now, you are no longer limited by to the key-value syntax of associative storage. You can use the dot syntax because the complier has a class to refer to:

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

With all this in mind, the proper forms of reference to the fetchedResults array become clear. Suppose you want to set all userID properties to a single default value. If you use the generic NSManagedObject class you use:

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

If you use a dedicated subclass you would use:

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

(Note: you can always use the generic form for all NSManagedObject subclasses as well.)

云醉月微眠 2024-09-21 19:27:36

仅当您在模型中定义了自定义 NSManagedObject 子类并在该类上定义了属性时,通过属性访问器(点表示法)访问 CoreData 属性才有效。实现应该是@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 通常需要是一个对象,对于 CoreData,它需要是 NSString、NSNumber、NSDate、NSSet 之一。

另外两个想法:

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

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

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

Accessing your CoreData attributes by property Accessors (dot notation) will only work if you have defined a custom NSManagedObject subclass in your Model and defined properties on that class. The implementation should be @dynamic. You'll then have to cast the object to the proper class:

//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
}

Or you may use KVC to access your models properties like this (without needing a class):

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

You would set the value using [object setValue:newValue forKey:@"UserID"] please note, that newValue needs to be an object in general and one of NSString, NSNumber, NSDate, NSSet for CoreData.

Two additional thoughts:

Your could and should use fast Enumeration on the results array:

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

I do not understand the ANY keyword in your predicate. "UserID IN %@" should do as well.

方觉久 2024-09-21 19:27:36

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

关于这个问题,整个核心数据都是一个转移注意力的东西。如果 User 派生自 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: ...]
}

如果您不确定对象是否存在,显然上述任何一种都是危险的数组中是 User 对象。您可以使用 -respondsToSelector: 来确保它能够正常工作(如果您愿意)。

Your basic problem is that -objectAtIndex: returtns an object of type id. No accessors are defined for type id so when you use dot notation with the object returned by -objectAtIndex: the compiler assumes you mean to access a C structure member. id is a pointer type, not a structure type, hence the error you are getting.

The whole core data stuff is a red herring with regard to this issue. You'd get the same error if User was derived from NSObject and you had populated the array yourself manually.

The ways out of it are:

  1. Use fast enumeration

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

    which is the preferred idiom if you need to iterate through the whole array

  2. Cast the result of -objectAtIndex: to the correct type.

    ((User*)[theArray objectAtIndex: i]).userId;
    
  3. Use the message sending syntax instead of dot notation

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

Personally, I'd go with 1 and 3.

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

Clearly any of the above are dangerous if you are not certain that the objects in the array are User objects. You can use -respondsToSelector: to make sure it will work if you like.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文