【问题标题】:JSON into Core Data to be displayed via UITableVewControllerJSON 转换为核心数据,通过 UITableViewController 显示
【发布时间】:2011-02-26 11:38:29
【问题描述】:

NSManagedObjectModel 由以下部分组成:

#import <CoreData/CoreData.h>


@interface Event :  NSManagedObject  
{
}

@property (nonatomic, retain) NSString * summary;
@property (nonatomic, retain) NSString * content;
@property (nonatomic, retain) NSDate * updated;
@property (nonatomic, retain) NSString * title;
@property (nonatomic, retain) NSDate * created;
@property (nonatomic, retain) NSString * ID;

@end

我正在尝试将解析后的 JSON 插入到上述核心数据属性中。

在下面,newNote 是我给 NSManagedObject 起的名字。基本上我想要完成的是将 JSON 插入核心数据,以便我可以在 UITableView 中显示它。以下函数用于解析 JSON 并将其插入 Core Data。

-(void)pullNotes {

    UIApplication *app = [UIApplication alloc];
    app.networkActivityIndicatorVisible = YES;

    NSDictionary *params = [NSDictionary dictionaryWithObject:api_key forKey:@"api_key"];   

    [[LRResty client] get:@"http://notacio.us/api/note" parameters:params withBlock:^(LRRestyResponse *response){

        if(response.status == 200) {
            NSLog(@"Pulling the users notes \n%@", [response asString]);

            // Create SBJSON object to parse JSON
            SBJSON *parser = [[SBJSON alloc] init];

            // parse the JSON string into an object - assuming [response asString] is a NSString of JSON data
            NSDictionary *object = [parser objectWithString:[response asString] error:nil];

            NSArray *notes = [object valueForKey:@"result"];
            for (NSDictionary *singleNote in notes){

                // newNote.created = [singleNote objectForKey:@"note created"]; Need to work on parsing these properly...
                // newNote.updated = [singleNote objectForKey:@"note updated"]; Need to work on parsing these properly...

                NSString *notetitle = [singleNote objectForKey:@"note title"];
                NSString *notesummary = [singleNote objectForKey:@"note summary"];
                NSString *noteid = [singleNote objectForKey:@"note id"];
                NSString *notecontent = [singleNote objectForKey:@"note content"];

                NSLog(@"Note Title: %@",notetitle);
                NSLog(@"Note Summary: %@",notesummary);
                NSLog(@"Note ID: %@",noteid);
                NSLog(@"Note Content: %@",notecontent);

                [newNote setValue:notecontent forKey:@"content"];

                NSError *error = nil;
                if (![newNote.managedObjectContext save:&error]) {
                    /*
                     Replace this implementation with code to handle the error appropriately.

                     abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
                     */
                    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
                    abort();
                }

                NSLog(@"Added Core Data Value: %@",[newNote valueForKey:@"content"]);

                // NSDate *createdDate = 
                // NSDate *updatedDate =

                [tableView reloadData];

                app.networkActivityIndicatorVisible = NO;

            }
        }

        if (response.status == 404) {
            NSLog(@"FAIL\n%@", [response asString]);
            app.networkActivityIndicatorVisible = NO;
        }

    }];

}

字符串的 NSLogs 正在返回正确的东西,所以我知道它实际上应该将它们插入到核心数据中......

所以,我的问题。

当该函数被调用时,我得到以下错误。

2011-02-26 21:37:30.797 Notacious[22570:207] Unresolved error (null), (null)

我不明白发生了什么,希望有人能够阐明我的问题。

编辑

玩了几个小时后,我的功能有点儿用了:

-(void)pullNotes {

    UIApplication *app = [UIApplication alloc];
    app.networkActivityIndicatorVisible = YES;

    NSDictionary *params = [NSDictionary dictionaryWithObject:api_key forKey:@"api_key"];   

    [[LRResty client] get:@"http://notacio.us/api/note" parameters:params withBlock:^(LRRestyResponse *response){

        if(response.status == 200) {
            NSLog(@"Pulling the users notes \n%@", [response asString]);

            // Create SBJSON object to parse JSON
            SBJSON *parser = [[SBJSON alloc] init];

            // parse the JSON string into an object - assuming [response asString] is a NSString of JSON data
            NSDictionary *object = [parser objectWithString:[response asString] error:nil];


            appDelegate =(NotaciousAppDelegate *) [[UIApplication sharedApplication] delegate];
            NSManagedObject *newNote;

            NSArray *notes = [object valueForKey:@"result"];
            for (NSDictionary *singleNote in notes){

                // newNote.created = [singleNote objectForKey:@"note created"]; Need to work on parsing these properly...
                // newNote.updated = [singleNote objectForKey:@"note updated"]; Need to work on parsing these properly...

                NSString *notetitle = [singleNote objectForKey:@"note title"];
                NSString *notesummary = [singleNote objectForKey:@"note summary"];
                NSString *noteid = [singleNote objectForKey:@"note id"];
                NSString *notecontent = [singleNote objectForKey:@"note content"];

                NSLog(@"Note Title: %@",notetitle);
                NSLog(@"Note Summary: %@",notesummary);
                NSLog(@"Note ID: %@",noteid);
                NSLog(@"Note Content: %@",notecontent);


                newNote = [NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:appDelegate.managedObjectContext];
                [newNote setValue:notecontent forKey:@"content"];
                [newNote setValue:notesummary forKey:@"summary"];
                [newNote setValue:notetitle forKey:@"title"];
                [newNote setValue:noteid forKey:@"ID"];



                NSLog(@"Added Core Data Value: %@",[newNote valueForKey:@"content"]);

                // NSDate *createdDate = 
                // NSDate *updatedDate =

                app.networkActivityIndicatorVisible = NO;

            }

            NSError *error = nil;
            if (![appDelegate.managedObjectContext save:&error]) {
                NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
                abort();
            }

        }

        if (response.status == 404) {
            NSLog(@"FAIL\n%@", [response asString]);
            app.networkActivityIndicatorVisible = NO;
        }

    }];

}

但是,无论实体之前是否存在,它都会添加实体。我想要它做的是发现如果一个笔记已经存在,更新它。如果没有,请创建一个新的。

编辑

目前这个函数看起来是这样的:

-(void)pullNotes {

    UIApplication *app = [UIApplication alloc];
    app.networkActivityIndicatorVisible = YES;

    NSDictionary *params = [NSDictionary dictionaryWithObject:api_key forKey:@"api_key"];   

    [[LRResty client] get:@"http://notacio.us/api/note" parameters:params withBlock:^(LRRestyResponse *response){

        if(response.status == 200) {
            NSLog(@"Pulling the users notes \n%@", [response asString]);

            // Create SBJSON object to parse JSON
            SBJSON *parser = [[SBJSON alloc] init];

            // parse the JSON string into an object - assuming [response asString] is a NSString of JSON data
            NSDictionary *object = [parser objectWithString:[response asString] error:nil];


            appDelegate =(NotaciousAppDelegate *) [[UIApplication sharedApplication] delegate];
            NSManagedObject *newNote;

            NSEntityDescription *noteEntity=[NSEntityDescription entityForName:@"Event" inManagedObjectContext:appDelegate.managedObjectContext];
            NSFetchRequest *noteFetch;
            NSArray *fetchedNotes;

            NSArray *notes = [object valueForKey:@"result"];
            for (NSDictionary *singleNote in notes){

                noteFetch=[[NSFetchRequest alloc] init];
                [noteFetch setEntity:noteEntity];
                NSPredicate *pred=[NSPredicate predicateWithFormat:@"noteID==%@",[singleNote objectForKey:@"note id"]];
                [noteFetch setPredicate:pred];

                NSError *fetchError=nil;
                fetchedNotes=[appDelegate.managedObjectContext performFetch:&fetchError];

                if (fetchError!=nil) {
                    NSLog(@"pullNotes fetchError=%@,details=%@",fetchError,fetchError.userInfo);
                }
                if ([fetchedNotes count]==0) {

                    // newNote.created = [singleNote objectForKey:@"note created"]; Need to work on parsing these properly...
                    // newNote.updated = [singleNote objectForKey:@"note updated"]; Need to work on parsing these properly...

                    NSString *notetitle = [singleNote objectForKey:@"note title"];
                    NSString *notesummary = [singleNote objectForKey:@"note summary"];
                    NSString *noteid = [singleNote objectForKey:@"note id"];
                    NSString *notecontent = [singleNote objectForKey:@"note content"];

                    NSLog(@"Note Title: %@",notetitle);
                    NSLog(@"Note Summary: %@",notesummary);
                    NSLog(@"Note ID: %@",noteid);
                    NSLog(@"Note Content: %@",notecontent);


                    newNote = [NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:appDelegate.managedObjectContext];
                    [newNote setValue:notecontent forKey:@"content"];
                    [newNote setValue:notesummary forKey:@"summary"];
                    [newNote setValue:notetitle forKey:@"title"];
                    [newNote setValue:noteid forKey:@"ID"];

                    // NSDate *createdDate = 
                    // NSDate *updatedDate =

                    NSError *error = nil;
                    if (![appDelegate.managedObjectContext save:&error]) {
                        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
                        abort();
                    }


                }

                [noteFetch release];


                app.networkActivityIndicatorVisible = NO;

            }

        }

        if (response.status == 404) {
            NSLog(@"FAIL\n%@", [response asString]);
            app.networkActivityIndicatorVisible = NO;
        }

    }];

}

然而,我在调用appDelegate.managedObjectContext 时似乎遇到了一个错误,它只是错误地说出以下内容:

2011-03-03 12:11:01.616 Notacious[182:307] -[NSManagedObjectContext performFetch:]: unrecognized selector sent to instance 0x1474e0
2011-03-03 12:11:01.781 Notacious[182:307] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSManagedObjectContext performFetch:]: unrecognized selector sent to instance 0x1474e0'

我已经尝试将其设置为appDelegate.managedObjectContext_,但它似乎返回了一个错误,指出在 appDelegate 中找不到它并且它不是一个 getter 对象。不过这里明确分配了:http://cl.ly/4xdX

编辑

我想我最好编辑这个以添加到我当前的工作中,并在原始标题下添加另一个问题。以下代码是我构建的并且运行良好(除了编辑后的字符串没有被保存,进而导致我的以下问题here)。

-(void)syncNotes {

UIApplication *app = [UIApplication alloc];
app.networkActivityIndicatorVisible = YES;

NSDictionary *params = [NSDictionary dictionaryWithObject:authToken forKey:@"api_key"]; 

[[LRResty client] get:@"http://notacio.us/api/note" parameters:params withBlock:^(LRRestyResponse *response){

    if(response.status == 200) {
        // NSLog(@"Successful Connection \n%@", [response asString]);

        // Create SBJSON object to parse JSON
        SBJSON *parser = [[SBJSON alloc] init];

        // parse the JSON string into an object - assuming [response asString] is a NSString of JSON data
        NSDictionary *object = [parser objectWithString:[response asString] error:nil];
        [parser release];

        NSFetchRequest *noteFetch;
        NSManagedObject *newNote;

        appDelegate =(NotaciousAppDelegate *) [[UIApplication sharedApplication] delegate];

        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Note" inManagedObjectContext:appDelegate.managedObjectContext];
        NSArray *fetchedNotes;

        NSArray *notes = [object valueForKey:@"result"];
        for (NSDictionary *singleNote in notes){

            noteFetch=[[NSFetchRequest alloc] init];
            [noteFetch setEntity:entity];
            NSPredicate *pred=[NSPredicate predicateWithFormat:@"ID==%@",[singleNote objectForKey:@"note id"]];
            [noteFetch setPredicate:pred];

            NSError *fetchError=nil;
            fetchedNotes=[appDelegate.managedObjectContext executeFetchRequest:noteFetch error:&fetchError];

            if (fetchError!=nil) {
                NSLog(@"syncNotes fetchError=%@,details=%@",fetchError,fetchError.userInfo);
            }
            if ([fetchedNotes count]==0) {


                NSString *notelocked = [singleNote objectForKey:@"note locked"];

                NSString *notecreated = [singleNote objectForKey:@"note created"];
                NSString *noteupdated = [singleNote objectForKey:@"note updated"];
                NSString *notetitle = [singleNote objectForKey:@"note title"];
                NSString *notesummary = [singleNote objectForKey:@"note summary"];
                NSString *noteid = [singleNote objectForKey:@"note id"];
                NSString *notecontent = [singleNote objectForKey:@"note content"];

                NSString *formattedTitle = [NSString decodeShit:notetitle];
                NSString *formattedSummary = [NSString decodeShit:notesummary];
                NSString *formattedContent = [NSString decodeShit:notecontent];


                NSLog(@"Note Title: %@",formattedTitle);
                NSLog(@"Note Summary: %@",formattedSummary);
                NSLog(@"Note ID: %@",noteid);
                NSLog(@"Note Content: %@",formattedContent);
                NSLog(@"Note Created: %@",notecreated);
                NSLog(@"Note Updated: %@",noteupdated);
                NSLog(@"Note Locked: %@",notelocked);


                newNote = [NSEntityDescription insertNewObjectForEntityForName:@"Note" inManagedObjectContext:appDelegate.managedObjectContext];
                [newNote setValue:formattedContent forKey:@"content"];
                [newNote setValue:formattedSummary forKey:@"summary"];
                [newNote setValue:formattedTitle forKey:@"title"];
                [newNote setValue:noteid forKey:@"ID"];
                [newNote setValue:notecreated forKey:@"created"];
                [newNote setValue:noteupdated forKey:@"updated"];
                [newNote setValue:notelocked forKey:@"locked"];

                NSError *error = nil;
                if (![appDelegate.managedObjectContext save:&error]) {
                    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
                    abort();
                }


            }

            [noteFetch release];

        }

        app.networkActivityIndicatorVisible = NO;

    }

    if (response.status == 404) {
        NSLog(@"FAIL\n%@", [response asString]);
        app.networkActivityIndicatorVisible = NO;
    }

}];

}

现在,确定这可行,但是当内容字符串不同时,我想更新正确的注释。这里有没有人愿意详细说明如何做到这一点?

我知道我一直在这里问问题,但希望一切都完美,似乎我对此类简单任务的了解有些不存在。任何意见将不胜感激!

编辑

啊,另一个请求。如果当前存储的笔记没有 noteID,那么从核心数据中删除它?这可能吗?

【问题讨论】:

  • 如何分配newNote
  • 我是这样做的 -> cl.ly/4rz2
  • 附注:您忘记将 Event 类重命名为其他名称,例如 Note ;)

标签: iphone objective-c cocoa-touch core-data nsmanagedobject


【解决方案1】:

您的错误的直接来源是您将save:error: 消息发送到不存在的托管对象上下文。反过来,托管对象上下文不存在,因为您从未创建或插入托管对象newNote 到上下文中。

我的印象是您还没有完全理解 Core Data 是围绕对象构建的,而不是 SQL 表、列、行等。Core Data 中的每条数据都存储在一个或多个对象或它们之间的关系中。所有这些对象和关系都创建了对象图,而 Core Data 真正管理的是该图。

在您的情况下,每条 JSON 消息都会分解为多个字典,每个字典都包含一个注释的数据。您需要一个配置了Event 实体的托管对象来存储每个注释。

如果您在 Xcode 中使用模板从核心数据之一开始,您将看到您获得托管对象上下文、托管对象模型、持久存储协调器和持久存储所有设置并分配给应用程序委托的属性.在下面的代码中,我将使用模板中的属性和名称。

你需要这样的东西(我没有编译这个,所以注意错别字):

-(void)pullNotes{
    //first, get the application delegate that manages the Core Data stack
    // since the app itself is a singleton, we just ask it for its delegate
    MyAppDelegateClass *appDelegate=(MyAppDelegateClass *) [[UIApplication sharedApplication] delegate];

    // ...all the JSON pulling code

    NSManagedObject *newNote; 
    NSArray *notes = [object valueForKey:@"result"];
    for (NSDictionary *singleNote in notes){
        // create another concrete instance of NSManagedObject 
        // for each logical note from JSON and insert it into
        // the appDelegate's managedObjectContext attribute
        newNote=[NSEntityDescription insertNewObjectForEntityForName:@"Event" 
                                              inManagedObjectContext:appDelegate.managedObjectContext_];
        // Assign data from the JSON note to the attributes of this specific 
        // NSManagedObject instance. 
        [newNote setValue:[singleNote objectForKey:@"note content"] forKey:@"noteContent"];
        // ... repeat for all other attributes you wish to have in the data 
    } 

    // only save after you've created all the notes. It's to slow to 
    // save after every note.   
    NSError *error = nil;
    if (![appDelegate.managedObjectContext_ save:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }
}

关键点:

  1. 每条建模数据都应该有自己的 NSManagedObject 实例。
  2. 所有托管对象实例都必须配置一个实体并插入到托管对象上下文中以进行管理或保存。
  3. Core Data 堆栈的上下文和其他部分通常驻留在应用程序委托中,并且您通常一次只有一个堆栈(对于大多数应用程序)。使用 UIApplication 单例从应用程序中的任何位置访问应用程序委托。

更新:

但是,它无论如何都会添加实体 他们是否在那里 前。我想要它做的是 发现如果一个便条已经存在, 更新它。如果没有,请创建一个新的。

要检查预先存在的笔记,您需要尝试获取与新数据匹配的现有笔记。这是简单的实现(未编译):

// call this outside the loop so you 
// only have to call it once.
NSEntityDescription *noteEntity=[NSEntityDescription entityForName:@"Event" inManagedObjectContext:appDelegate.managedObjectContext];
NSFetchRequest *noteFetch;
NSArray *fetchedNotes;

// inside the for loop for each new note
noteFetch=[[NSFetchRequest alloc] init];
[noteFetch setEntity:noteEntity];
NSPredicate *pred=[NSPredicate predicateWithFormat:@"noteID==%@",[singleNote objectForKey:@"note id"]];
[noteFetch setPredicate:pred];

NSError *fetchError=nil;
fetchedNotes=[appDelegate.managedObjectContext_ performFetch:&fetchError];
if (fetchError!=nil) {
    NSLog(@"pullNotes fetchError=%@,details=%@",fetchError,fetchError.userInfo);
}
if ([fetchedNotes count]==0) {
    // create new note
}
[noteFetch release];
// end loop

您可以使用IN 测试形成一个谓词,该测试将针对所有新的``noteID 值的数组进行测试,并且只进行一次提取,但这有点复杂。除非您一次处理数百个音符,否则从用户的角度来看,这种技术看起来同样快。

【讨论】:

  • 更新问题,你能帮我看看吗?
  • 目前在编译时得到这个,似乎 performFetch: is not a function of appDelegate -> cl.ly/4sEC
  • performFetch 是 managedObjectContext 的一个方法,它作为 appDelegate 的属性保留。回顾代码,实际的属性名称应该是managedObjectContext_。我会编辑。
  • 刚刚测试过,似乎问题不在于,事实上,将其作为managedObjectContext_ 发送回使用错误getter 方法的错误。有什么想法吗?
  • 您是否使用模板应用程序委托。我的示例假设您是并使用我的 iOS 4.2 版本在模板中创建的属性名称。如果您不使用模板或使用不同版本的操作系统,则可能有不同的属性名称。名称当然必须准确。
【解决方案2】:

您只是在调用 insertNewObjectForEntityForName ,它总是会创建一个新对象。使用唯一值作为键,为该键创建一个获取请求,如果它没有返回任何内容,则创建一个新对象。

 + (Note*)NoteWithKey: (NSString*)key
{
    NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:key,@"KEY",nil];

    NSManagedObjectContext *context = appDelegate.managedObjectContext;
    NSManagedObjectModel *model = appDelegate.managedObjectModel;
    NSFetchRequest *fetchRequest = [model fetchRequestFromTemplateWithName:@"GetKey" substitutionVariables:query];

    NSError *error;
    NSArray *results = [context executeFetchRequest:fetchRequest error:&error];
    if (results && ([results count] > 0)) {
        return [results objectAtIndex:0];
    }
    else {
        Note *newNote = [NSEntityDescription insertNewObjectForEntityForName:@"Note" inManagedObjectContext:context];
        if (nil != newNote) {
            [newNote setValue:key forKey:@"Key"];
        }

        return newNote;
    }
}

【讨论】:

  • 如何将其直接添加到上述函数中?关键是什么意思?
  • 关键是笔记 ID。我看到 TechZen 也有同样的想法。我使用了一个获取请求模板:[d.pr/5Gg5]
【解决方案3】:

您的头文件 (http://cl.ly/4rz2) 有一个 Event 对象的引用,称为 newNote。但是您永远不会创建要存储在该引用中的对象。我将删除您的头文件中的引用,并将其保留在您的 JSON 解析方法中。为您在 json 中遇到的每个 singleNote 创建一个事件:


[..]
Event *newNote = nil;

for (NSDictionary *singleNote in notes) {
    // create a new autoreleased object
    newNote = [[[Event alloc] initWithEntity:aDescription insertIntoManagedObjectContext:aContext] autorelease];
    newNote.content = [singleNote objectForKey:@"note content"];
}

// save managedobjectcontext
[..]

无关

[UIApplication alloc] 不是您通常想做的事情。你可能想使用类似的东西:[UIApplication sharedApplication].n networkActivityIndicatorVisible = YES;

【讨论】:

  • -1 对向下检查感到抱歉,但您永远不会将retainreleaseautorelease 发送到托管对象。托管对象的保留应仅由托管对象上下文处理。
  • @TechZen 谁告诉你的?给我看文档。
  • 好吧,我想可能有一些狭窄的情况需要保留,但在绝大多数情况下,上下文将处理保留。没有什么比从上下文断开的 managedObject 更危险的了。这不是我们想要鼓励的做法。您当然不需要在这里自动释放,因为insertIntoManagedObjectContext 会导致上下文强烈保留对象。一般来说,自动释放应该只在当前对象完成释放对象时发送,因为它可能会在复杂的内存情况下导致意外释放。
  • 好吧,在这种情况下,当前对象实际上是在设置内容后立即使用新创建的事件完成的。 Event 对象由 [[Event alloc] init...] 暗示的用户保留。所以需要发布。 insertIntoManagedObjectContext 引起的上下文对对象的保留确保托管对象上下文保留句柄,但这并不意味着我们不应该释放我们自己持有的任何引用。
  • 你的声誉分数让我想知道为什么我在这里解释保留计数。但我相当确定保留计数也适用于 NSManagedObject 并且不释放分配的对象似乎很奇怪。这就是为什么我问你从哪里得到这些“知识”,因为我找不到关于你的第一条评论所指内容的单一文档参考。无论如何,我在几乎所有的软件中都使用 Core Data,而不是在普通对象和 nsmanagedobjects 中释放导致内存泄漏的结果。
猜你喜欢
  • 2011-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-30
  • 1970-01-01
  • 2018-03-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多