将大量数据从iPad转换/上传到Dropbox

我正在通过仪器运行它以及用大量数据强调它来完成我的应用程序。 仪器测试很好,但压力测试是我遇到问题的地方。 在没有深入细节的情况下,我正在为我的应用程序提供越来越多的Core Data事件,它需要在MKMapView实例上推断数据,制作图表和显示位置。 我开始小规模并增加到56000个事件,它处理得很好,没有任何泄漏或内存警告(我为它处理它而感到非常自豪)。

我的应用实现了Dropbox API,允许上传和下载模板和数据以进行同步。 从我的应用程序上传的文件从Core Data转换为NSDictionary ,然后转换为NSData 。 我为数据创建一个临时文件夹,然后将该文件上传到Dropbox,这通常正常工作。 如果我尝试使用56000个事件上传我的数据文件,那么它会崩溃。 我已经记录了它并观察了数据的转换。 它没有任何问题到达最后一个事件,但是当它应该开始上传到Dropbox时,应用程序崩溃了,我不能为我的生活找出原因。 我看到日志中弹出内存警告。 通常,它会变为Level = 1,Level = 2,Level = 1,Level = 2,然后崩溃,这让我感到困惑,因为它永远不会达到Level = 3。

我发现的大部分信息都在我的编辑中。 以下是一些相关代码:

 - (void)uploadSurveys:(NSDictionary *)dict { NSArray *templateArray = [dict objectForKey:@"templates"]; NSArray *dataArray = [dict objectForKey:@"data"]; NSString *filename; NSLog(@"upload called"); if ([templateArray count] || [dataArray count]) { if ([templateArray count]) { // irrelevent code; } if ([dataArray count]) { SurveyData *survey; for (int i = 0; i < [dataArray count]; i++) { BOOL matchExists = NO; // ...... code to make sure no file exists in dropbox folder and creates new version if necessary; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSData *data = [self convertSurvey:survey]; dispatch_async(dispatch_get_main_queue(), ^{ [self uploadData:data withFilename:filename]; NSLog(@"converted and uploading"); }); }); } } } 

[self convertSurvey:survey]只是将我的Core Data对象转换为NSData

 - (void)uploadData:(NSData *)data withFilename:(NSString *)filename { NSFileManager *manager = [NSFileManager defaultManager]; NSString *pathComponent = [NSString stringWithFormat:@"tempData.%@", filename]; NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:pathComponent]; if ([manager createFileAtPath:path contents:data attributes:nil]) { [self.restClient uploadFile:filename toPath:[NSString stringWithFormat:@"/%@", currentSearch] fromPath:path]; NSLog(@"uploading data"); } } 

任何帮助都会有很大帮助,我提前非常感谢你。 我只想弄清楚我是对大文件采取了错误的方法,还是只是不允许。 如果我必须拆分文件,那很好,但我想知道发生了什么阻止我的应用程序在我尝试解决方法之前执行此操作。 再次感谢你。

更新:由于这个问题现在是我的应用程序发布的唯一障碍,我正在为这个问题添加一个赏金,希望能得到一个解决方案或解决方法。 它将持续一周,在此之后,我很可能只是在上传时拆分文件以确保未达到此明显的大小限制。 这种方法并不理想,这就是为什么一个更好的解决方案非常受欢迎的原因,但这是我的备份计划,如果这不能带来更方便的东西。

编辑: NSTemporaryDirectory似乎NSTemporaryDirectory不参与其中。 这是新情况。 正如您在上面的代码中看到的, NSData *data = [self convertSurvey:survey]; 在辅助线程中调用(这不是问题)。 我一直在记录创建的对象,并且知道它们已经到达最后一个,但从未想过检查并查看是否返回了NSData文件。 事实certificate,事实并非如此。 简而言之,我将所有Core Data对象转换为数组并将它们放入字典中(仅用于要转换的相关调查/数据)。 这确实有效,并且创建了字典。 然后我使用NSData *data = [NSKeyedArchiver archivedDataWithRootObject:d];创建一个NSData文件NSData *data = [NSKeyedArchiver archivedDataWithRootObject:d]; 其中d是我的字典。 在此之后,我直接调用return data; 设置NSData *data = [self convertSurvey:survey]; 。 在这种情况下,似乎NSDataNSKeyedArchiver在此处有错。 根据Apple文档:

使用32位Cocoa,数据大小受理论上的2GB限制(实际上,因为内存将被其他对象使用,此限制将更小); 使用64位Cocoa,数据的大小受理论限制约为8EB(实际上,限制不应该是一个因素)。

我已经以小增量检查了文件大小,以查看故障发生的位置。 我已成功获得48.2MB的数据,但不是51.5MB,这让我相信问题发生在50MB左右,远低于NSData的理论极限(除非iOS和OS X在这方面存在差异)。

希望这些新信息有助于解决这个问题

NSData的2 GB限制在iOS上完全是理论上的,即使iPhone 4只有512 MB的RAM而iOS(与Mac OS X不同)也无法交换,所以如果您的物理RAM已满,则会崩溃(或者您的应用程序在之前终止那)。

单独的50 MB NSData对象已经非常大并且它不是内存中唯一的对象 – 假设您将数据从Core Data转换为字典表示然后转换为NSData ,您可能至少消耗两倍的内存(可能更多)。 系统和其他应用程序也需要RAM,因此您可能已达到限制。

尝试在Instruments中运行您的应用程序,以查看您实际消耗的内存量。

要减少峰值内存使用量,您有几个选项在很大程度上取决于您的数据模型:

  • 正如Jason Foreman在他的回答中建议的那样,尽量避免将整个文件放在内存中。 使用NSFileHandle ,您可以将数据块写入文件,而无需一次将整个数据存储在内存中。 当然,这需要您相应地准备数据,以便将其拆分为块。 更高级别的方法可能是将数据序列化为XML格式,您可以将其作为流写出。 如果您的数据格式非常简单,CSV之类的内容也可能有效。

  • 不要使用NSData上传到Dropbox。 将数据写入文件(参见上文)并将Dropbox SDK指向该文件。 Dropbox SDK使这很容易( DBRestClient有一个uploadFile:toPath:fromPath:方法)。

  • 如果您的数据模型难以采用流式处理方法,请尝试将数据划分为更易于管理的部分。 然后,您可以使用旧的序列化字典方法,只需使用多个文件。

  • 小心Core Data的内存使用情况。 尝试使用refreshObject:mergeChanges:重新refreshObject:mergeChanges:对象refreshObject:mergeChanges:如果可能,可以破坏数据中的循环引用(有关详细信息,请参阅“ 核心数据编程指南 ”)。

  • 当您处于长时间运行的循环中时,请避免使用自动释放池,或者创建一个单独的NSAutoreleasePool ,它会在循环的每次迭代中耗尽。

解决此类内存压力的一种方法是使用流构建API,既可以将转换后的数据写入磁盘上的文件,也可以将数据上传到Web服务。

在转换过程中,您可以使用NSOutputStream将数据块写入文件,以避免一次在内存中保留大量数据。 然后, NSMutableURLRequest可以接受主体的NSStream而不是NSData ,因此您应该创建一个NSInputStream以从磁盘读取文件并上传它。

以这种方式使用流将确保您永远不会加载50多MB的数据,并应避免您看到的内存警告。