Cocoa:CoreData - ManagedObjectContext 中的多个实体

Posted

技术标签:

【中文标题】Cocoa:CoreData - ManagedObjectContext 中的多个实体【英文标题】:Cocoa:CoreData - Multiple entities in a ManagedObjectContext 【发布时间】:2010-02-11 12:44:32 【问题描述】:

我有两个实体,我也建模为类和核心数据模型实体/这些类如下所示:

类:StateManager

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h> 
#import "StateManager.h"
#import "Constants.h"


@implementation StateManager 

@synthesize deviceID, physicalID, applicationName, applicationVersion;
@synthesize userName, userPassword, server, port, client, language, protocol;

-(bool)checkCompleteness
if (physicalID == nil) 
    return false;

if (applicationName == nil) 
    return false;

if (applicationVersion == nil) 
    return false;

if (userName == nil) 
    return false;

if (userPassword == nil) 
    return false;

if (port == nil) 
    return false;

if (server == nil) 
    return false;

return true;

-(void)preserveState:(NSManagedObjectContext *)managedObjectContext
// Step 1: Create Object
StateManager *inMemoryState = (StateManager *)[NSEntityDescription
                                     insertNewObjectForEntityForName:ENTITY_STATE
                                     inManagedObjectContext:managedObjectContext];
// Step 2: Set Properties
[inMemoryState setDeviceID:self.deviceID];
[inMemoryState setPhysicalID:self.physicalID];
[inMemoryState setApplicationName:self.applicationName];
[inMemoryState setApplicationVersion:self.applicationVersion];
//[inMemoryState setAutoRegister:[self getAutoRegister]];
[inMemoryState setUserName:self.userName];
[inMemoryState setUserPassword:self.userPassword];
[inMemoryState setServer:self.server];
[inMemoryState setPort:self.port];
[inMemoryState setClient:self.client];
[inMemoryState setLanguage:self.language];
[inMemoryState setProtocol:self.protocol];

// Step 3: Save Object
NSError *error;
if (![managedObjectContext save:&error]) 
     NSLog(@"Unresolved Core Data Save error %@, %@", error, [error userInfo]);
     exit(-1);
 




-(StateManager *)loadState:(NSManagedObjectContext *)managedObjectContext

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

NSEntityDescription *entityState = [NSEntityDescription entityForName:ENTITY_STATE 
                                               inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entityState];

NSError *error;

NSArray *items = [managedObjectContext
                  executeFetchRequest:fetchRequest error:&error];

if ([items count] > 0) 
    StateManager *inDatabaseState = (StateManager *)[items objectAtIndex:0];

    deviceID = [inDatabaseState valueForKey:@"deviceID"];
    physicalID = [inDatabaseState valueForKey:@"physicalID"];
    applicationName = [inDatabaseState valueForKey:@"applicationName"];
    applicationVersion = [inDatabaseState valueForKey:@"applicationVersion"];
    //autoRegister = [inDatabaseState valueForKey:@"autoRegister"];
    userName = [inDatabaseState valueForKey:@"userName"];
    userPassword = [inDatabaseState valueForKey:@"userPassword"];
    server = [inDatabaseState valueForKey:@"server"];
    port = [inDatabaseState valueForKey:@"port"];
    client = [inDatabaseState valueForKey:@"client"];
    language = [inDatabaseState valueForKey:@"language"];
    protocol = [inDatabaseState valueForKey:@"protocol"];

    [inDatabaseState release];


[fetchRequest release];
[entityState release]; 
[items release]; 
/*
 deviceID = @"1234";
 physicalID = @"12312";
 applicationName = @"erwerw";
 applicationVersion = @"applicationVersion";
 //autoRegister = [inDatabaseState valueForKey:@"autoRegister"];
 userName = @"userName";
 userPassword = @"userPassword";
 server = @"server";
 port = @"port";
 client = @"client";
 language = @"language";
 protocol = @"protocol";*/
 return self;



-(void)dealloc

[deviceID release];
[physicalID release];
[applicationName release];
[applicationVersion release];
[userName release];
[userPassword release] ;
[server release];
[port release];
[client release];
[language release];
[protocol release];
[super dealloc];


@end

类:InboundQueueState

#import "InQueueState.h"


@implementation InQueueState 

@dynamic top;
@dynamic read;
@dynamic last;

@end

现在我正在尝试将这些对象数据从数据库加载到我的应用程序中的两个不同位置,如下所示:

    如果您查看 StateManager 类,您会看到一个方法 loadState,它尝试使用托管对象上下文读取实体“StateManager”。这个上下文是从主类传递给这个类的。

    下面“InboundQueue”类中显示的另一个方法“readQueue”尝试遵循使用全局托管对象上下文加载实体“InboundState”的相同范例,但失败并出现以下错误:

错误:[NSFetchRequest objectID]:无法识别的选择器发送到 实例 0x3a1a300 由于未捕获的异常而终止应用程序 'NSInvalidArgumentException',原因:'*** -[NSFetchRequest objectID]: 无法识别的选择器发送到实例 0x3a1a300'

类:入站队列

#import "InboundQueue.h"
#import <CoreData/CoreData.h> 
#import "Constants.h"
#import "InQueueItem.h"

@implementation InboundQueue

-(int)last
return [queueState last];


-(int)top
return [queueState top];


-(int)read
return [queueState read];


-(InboundQueue *)initInboundQueue:(NSManagedObjectContext *)managedObjectContext

//load the inbound queue counters and initialize the inbound queue object with the values
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

NSEntityDescription *entityState = [NSEntityDescription entityForName:INQUEUE_STATE 
                                               inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entityState];

NSError *error;
NSArray *msgs = [managedObjectContext
                  executeFetchRequest:fetchRequest error:&error];

if ([msgs count] > 0) 
    queueState = [msgs objectAtIndex:0]; //contains the values of the counters related to inbound queue at any point in time


[fetchRequest, entityState, msgs release];
return self;


-(NSMutableArray *)readQueue:(NSManagedObjectContext*) managedObjectContext
//load the inbound queue counters and initialize the inbound queue object with the values
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

NSEntityDescription *entityState = [NSEntityDescription entityForName:INQUEUE 
                                               inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entityState];

NSError *error;
NSArray *queueContent = [managedObjectContext
                  executeFetchRequest:fetchRequest error:&error];
[queueData setArray:queueContent];

[fetchRequest, entityState, error release];
return queueData;


-(void)confirm:(int)msgID managedObjectContext:(NSManagedObjectContext*) managedObjectContext
if (msgID < [queueState top]) 
    return;

if (msgID > [queueState last]) 
    return;

//remove items from queue which are lower than the confrmed message ID
for (InQueueItem *item in queueData) 
    int currentMsgId = [item MSG_ID]; 
    if ( currentMsgId <= msgID) 
        //delete the item from queue and adjust counters

        [queueData removeObject:item];
    

[queueState setTop:(msgID + 1)];
if ([queueState read] < [queueState top]) 
    [queueState setRead:[queueState top]]; //realign the top counter

//persist the changes from context to database
NSError *error;
if (![managedObjectContext save:&error]) 
    NSLog(@"Unresolved Core Data Save error %@, %@", error, [error userInfo]);
    exit(-1);



-(void)add:(InQueueItem *)item  managedObjectContext:(NSManagedObjectContext*) managedObjectContext
//validate the message id
if (([queueState last] > ([item MSG_ID] - 1))) 
    //ignore this message since we have already received a message with this message id     
else if (([queueState last] < ([item MSG_ID] - 1))) 
    //raise an invalid message ID exception => data loss    
else 
    if ([queueState last] == 0) 
        //need to initialize counters as this is the first entry
        [queueState setTop:[item MSG_ID]];
        [queueState setLast:[item MSG_ID]];

    else 
        //increment last
        [queueState setLast:([queueState last] + 1)];
    
    //now insert the item into queue
    [queueData addObject:item];

//persist the changes from context to database
NSError *error;
if (![managedObjectContext save:&error]) 
    NSLog(@"Unresolved Core Data Save error %@, %@", error, [error userInfo]);
    exit(-1);




-(void)dealloc
[queueData release];
[queueState  release];
[super dealloc];

@end

我的理解是同一个托管对象上下文可以加载数据模型的所有对象并允许操作,但我不确定为什么在我的情况下它只适用于一个对象。

【问题讨论】:

你能告诉我们你获取这两个实体的代码吗? 不,您不需要单独的托管对象上下文;问题一定是别的。请发布更多详细信息。 【参考方案1】:

您不需要为每个实体使用单独的 ManagedObjectContext。 ManagedObjectContext 控制整个模型的整个对象图,无论模型包含多少实体。 (您可以创建多个上下文,但不能处理多个实体。)

如果您的 A 抓取踩到您的 B 抓取,那么您很可能没有正确配置您的 B 抓取。您是否试图重用您为 A 创建的提取?

Fetch 非常独特。每个 fetch 都是单独配置的,您永远不应将它们重用于其他实体或更改它们的 fetch 参数(尽管该类不强制执行此操作。)

您应该为 B 初始化一个完全独立的提取。

编辑01:

根据您发布的代码和错误的详细信息,我认为问题很可能是您没有为您的 InQueueState 类返回正确的实体描述。

我建议记录以下返回的实体:

NSEntityDescription *entityState = [NSEntityDescription entityForName:INQUEUE 
                                               inManagedObjectContext:managedObjectContext]

以确保它不为零。

您还需要(作为标准做法)在使用 NSArray *queueContent 之前检查它是否为 nil 或空。

编辑02:

另外,这一行:

[fetchRequest, entityState, error release];

您不需要释放 entityState 或错误,因为您没有初始化或创建任何一个,而只是持有从其他对象返回的对它们的引用。

【讨论】:

以上是关于Cocoa:CoreData - ManagedObjectContext 中的多个实体的主要内容,如果未能解决你的问题,请参考以下文章

删除 NSManagedObjectContext

核心数据实体未删除

Coredata groupby 属性 iOS

在 EditView 中使用 CoreData 中的值预填充 SwiftUI 表单

如何使用 RKValueTransformer 将 NSString 转换为 Core Data NSManagedObject