处理HTTP Post的正确方法,然后通过HTTP下载,然后保存到核心数据

Posted

技术标签:

【中文标题】处理HTTP Post的正确方法,然后通过HTTP下载,然后保存到核心数据【英文标题】:Correct way to process HTTP Post, then download via HTTP, then save to core data 【发布时间】:2014-09-26 11:58:28 【问题描述】:

我有一个将数据同步到服务器的应用程序。过程是: 1)将本地数据转换为JSON 2) 通过 HTTP Post 将本地数据发送到服务器 3) 处理来自 2) -0 中来自 HTTP Post 的服务器的响应,即它正在处理的服务器的确认表单,并已保存一切正常 4) 从服务器对 JSON 进行 HTTP 请求以进行作业更新 5)处理这个JSON保存到核心数据

我尝试将各个部分放在后台线程上,以便释放 UI。我一直遇到间歇性问题,我认为这是因为我在后台线程上使用核心数据。

这个过程是从一个按钮点击开始的,所以为了保持 UI 正常工作,我至少需要在后台线程中做一些。

目前我将初始方法调用发送到后台线程,并在后台处理它,在主线程上使用进度消息更新 UI。

我可以保持原样,但将核心数据部分强制到主线程上吗?这甚至是一个好的解决方案吗?是否可以将所有内容都保留在背景中并将核心核心数据位放回主线程?

谢谢!

编辑 - 我现在使代码更合理 - VC 现在将处理启动到后台线程,并通过来自 3 个用于发送和下载数据的类的协议调用来更新 UI。

每个类都有自己的托管对象上下文对象,但每个类都使用以下代码进行初始化:

-(NSManagedObjectContext*) managedObjectContext

    if (!_managedObjectContext)
    
        mavisFireChecksAppDelegate *appDelegate = (mavisFireChecksAppDelegate *)    [[UIApplication sharedApplication] delegate];
        _managedObjectContext=appDelegate.managedObjectContext;

    
    return _managedObjectContext;

我应该改变这个吗?这些类是在后台线程上创建的,尽管它们保留在该线程上,但 appdeleegate.managedObjectContext 是在应用程序加载时创建的,因此在主线程上也是如此。我现在脑子里有这个正确的吗?这 3 个类都应该初始化一个新的 MOC,而不是使用 appDelegate 托管对象上下文,因为它们在后台运行,并且应用程序委托是在主线程上创建的?

在没有实际更改托管对象上下文而不是类的情况下,它仍然在每 50-100 次左右的调用中出错一次。在后台对象中使用 appldelegate.managedobject 是问题的可能原因

编辑****

在下面的线程中接受了解决方案,但对于阅读本文的人来说还有一些额外的有用信息:

我只是想补充一点,以防其他人读过它。我确实将所有 CD 调用包装在 perform... 块中,但引入了一个新错误,因为我没有在 2 个单独的 MOC 上正确合并更改。丹尼尔提供的链接 - robots.thoughtbot.com/core-data

,很聪明,向我展示了我的错误。还有一个 CD 堆栈,您可以从文章底部链接的 git hub 中取出。

【问题讨论】:

在导入数据时始终在后台线程上执行核心数据。您是否将所有核心数据调用包装在上下文 perfomBlock 中:? 看看 Magical Record,它们简化了您在核心数据中需要的许多样板,并且它们确实为执行所有 CD 操作提供了有用的 API github.com/magicalpanda/MagicalRecord 感谢您的回复。这个项目极大地改变了范围,虽然我意识到 CD 不是线程安全的,因为我最初没有使用线程,但我并没有过多地研究它。我没有使用过performBlocks。我现在得好好查一下。导入/导出例程有 2 个部分,一个是它自己的类,它有自己的托管对象上下文,另一个是在视图控制器中启动它(我知道糟糕的设计)。正是这一点导致了间歇性错误。我不认为我在主线程上创建 MOC,但我会先检查一下! 一切正常后,我会查找魔法记录。一旦我知道它起作用了,我就可以安全地介绍一些新的东西——否则我不知道是我还是魔法唱片还是两者兼而有之:) 嗨,我已经对 OP 添加了一个编辑。我让 VC 只做 VC 类型的东西 :) 我有 3 个类在做导入/导出。每个都有自己的 MOC 对象,但目前它使用应用程序 delegate.managedObjectContext。课程在后台运行。 appdeleegate 是在主线程上创建的。这样我就明白了——我应该改变类 MOC init 以使用全新的 MOC 吗?这是可能的问题吗?谢谢。 【参考方案1】:

从后台线程调用 Core Data 并不意味着你已经实现了并发。考虑这个创建后台线程上下文的代码:

- (NSManagedObjectContext *)createBackgroundContextWithStoreCoordinator:(NSPersistentStoreCoordinator *)coordinator         
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    [context setPersistentStoreCoordinator:coordinator];
    context.undoManager = nil;//if you don't need an undo manager always do this for performance boosts
    return context;

您将看到上下文的并发类型为 NSPrivateQueueConcurrencyType,而您的 UI 内容通常具有 NSMainQueueConcurrencyType

现在您已经有了背景上下文,您应该知道 performBlock: (异步) 和 performBlockAndWait: (同步) 调用用于与 Core Data 进行交易。因此,您的导入可能如下所示:

- (void)importStuff:(NSArray *)stuffToImport
       usingContext:(NSManagedObjectContext *)context
    andPerformBlock:(void (^)(BOOL madeChanges))completionBlock 
    [context performBlock:^
       /**
         * do some import stuff
        */
        BOOL madeChanges = [context hasChanges];
        [context save:nil];
        if (completionBlock) 
            completionBlock(madeChanges);
        
    ];

您将在其中调用导入方法,如下所示:

NSArray *stuff = //whatever you fetched from server;
NSManagedObjectContext *context = //background context;
[importer importStuff:stuff
         usingContext:context
      andPerformBlock:^(BOOL madeChanges)
      if (madeChanges)
          //reload UI
      
];

请记住,托管对象不是线程安全的。您应始终确保对上下文 performBlock 或 performBlockAndWait 执行任何更新、访问或插入操作。

通常我发现块对于导入比委托方法更有用:)

【讨论】:

谢谢,这一切都有帮助。我也一直在阅读苹果文档。我想我现在明白了。最后一个问题-我获取以查看作业是否已存在,然后更新或插入。作业对象中有很多子对象。你会使用一个大块,还是很多小块?我总是喜欢征求意见:) 顺便说一句 - 核心数据编程指南 - 并发指南 developer.apple.com/library/ios/documentation/Cocoa/Conceptual/… 实际上说他的最佳实践已经改变,这让我很困惑! 我刚看到,很不幸!我猜只是浏览网络,请参阅robots.thoughtbot.com/core-data 和 floriankugler.com/blog/2013/4/2/the-concurrent-core-data-stack 我想补充一点,以防其他人读过它。我确实将所有 CD 调用包装在 perform... 块中,但引入了一个新错误,因为我没有在 2 个单独的 MOC 上正确合并更改。 daniel - robots.thoughtbot.com/core-data 提供的链接非常棒,向我展示了我的错误。还有一个 CD 堆栈,您可以从文章底部链接的 git hub 中取出。 @JamesDawson 好消息!而是将其作为对您原始过去的编辑而不是隐藏在 cmets 中:)

以上是关于处理HTTP Post的正确方法,然后通过HTTP下载,然后保存到核心数据的主要内容,如果未能解决你的问题,请参考以下文章

ESP8266通过http post方法获取网络数据

http方法中get和post方法对比

如何通过 HTTP 安全地发送密码?

django:通用类视图 + POST = HTTP 405(不允许的方法)

接口测试

ESP8266通过http Get方法获取网络数据