【发布时间】:2017-11-16 09:29:39
【问题描述】:
我正在从 Web 服务获取 700.000 行,我的应用程序在由于内存问题而终止一段时间后崩溃,它在崩溃之前消耗了大约 1 GB 的内存,代码相当简单,我获取 JSON,我放入一个数组,我循环数组并插入核心数据,完成后我保存上下文
代码如下所示
+ (void)fetchTillDataAll:(int)tillId :(int)startAtRow :(int)takeNoOfRows {
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchTillDataAll:tillId = %d, startAtRow = %d, takeNoOfRows = %d", tillId, startAtRow, takeNoOfRows);
}
NSString *finalURL = [NSString stringWithFormat:@"https://host.domain.com:5443/api/till/tilldatav2/%d?StartAtRow=%d&TakeNoOfRows=%d",tillId, startAtRow, takeNoOfRows];
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:finalURL]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error != nil) {
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchTillDataAll:Transport error %@", error);
}
} else {
NSHTTPURLResponse *responseHTTP;
responseHTTP = (NSHTTPURLResponse *) response;
if(responseHTTP.statusCode != 200) {
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchTillDataAll:Server Error %d", (int) responseHTTP.statusCode);
}
} else {
NSArray *tillBasicDataArray = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchTillDataAll:tillBasicDataArray count = %lu", (unsigned long)[tillBasicDataArray count]);
NSLog(@"WebServices:fetchTillDataAll:tillBasicDataArray looks like %@",tillBasicDataArray);
}
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
//NSManagedObjectContext *context =
//appDelegate.persistentContainer.viewContext;
//context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;
NSPersistentContainer *container = appDelegate.persistentContainer;
[container performBackgroundTask:^(NSManagedObjectContext *context ) {
context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;
NSDictionary *tillBasicDataDict = Nil;
//Loop through the array and for each dictionary insert into local DB
// lets work on concurrency here
for (id element in tillBasicDataArray){
tillBasicDataDict = element;
NSString *itemId = [tillBasicDataDict objectForKey:@"itemId"];
NSString *brandId = [tillBasicDataDict objectForKey:@"companyId"];
NSString *languageId = [tillBasicDataDict objectForKey:@"languageCode"];
NSString *colorCode = [NSString stringWithFormat:@"%@", [tillBasicDataDict objectForKey:@"colorCode"]];
NSString *discountable = [tillBasicDataDict objectForKey:@"discountable"];
NSString *exchangeable = [tillBasicDataDict objectForKey:@"exchangeable"];
NSString *noos14 = [tillBasicDataDict objectForKey:@"noos14"];
NSString *sizeCode = [NSString stringWithFormat:@"%@", [tillBasicDataDict objectForKey:@"sizeCode"]];
NSString *taxGroup = [tillBasicDataDict objectForKey:@"taxGroupId"];
NSString *taxRegion = [tillBasicDataDict objectForKey:@"taxRegion"];
NSString *tradeItemDesc = [tillBasicDataDict objectForKey:@"tradeItemDesc"];
NSString *withTax = [tillBasicDataDict objectForKey:@"withTax"];
NSString *status = [tillBasicDataDict objectForKey:@"status"];
// Use Core Data FMD
NSManagedObject *newPimItem = Nil;
newPimItem = [NSEntityDescription
insertNewObjectForEntityForName:@"TillData"
inManagedObjectContext:context];
[newPimItem setValue:itemId forKey:@"itemId"];
[newPimItem setValue:brandId forKey:@"brandId"];
[newPimItem setValue:languageId forKey:@"languageCode"];
[newPimItem setValue:colorCode forKey:@"colorCode"];
[newPimItem setValue:discountable forKey:@"discountable"];
[newPimItem setValue:exchangeable forKey:@"exchangeable"];
[newPimItem setValue:noos14 forKey:@"noos14"];
[newPimItem setValue:sizeCode forKey:@"sizeCode"];
[newPimItem setValue:[NSNumber numberWithInt:[taxGroup intValue]] forKey:@"taxGroup"];
[newPimItem setValue:taxRegion forKey:@"taxRegion"];
[newPimItem setValue:tradeItemDesc forKey:@"tradeItemDesc"];
[newPimItem setValue:[NSNumber numberWithInt:[withTax intValue]] forKey:@"withTax"];
[newPimItem setValue:[NSNumber numberWithInt:[status intValue]] forKey:@"status"];
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchTillDataAll:ItemId in loop = %@", itemId);
NSLog(@"WebServices:fetchTillDataAll:newPimItem = %@", newPimItem);
NSLog(@"WebServices:fetchTillDataAll:CoreData error = %@", error);
}
}
NSError *error = nil;
if (![context save:&error]) {
NSLog(@"Failure to save context: %@\n%@", [error localizedDescription], [error userInfo]);
abort();
} else {
NSUserDefaults *tillUserDefaults = [NSUserDefaults standardUserDefaults];
[tillUserDefaults setInteger:1 forKey:@"hasTillData"];
[tillUserDefaults synchronize];
}
}];
}
}
}] resume];
}
我可以做些什么来最小化占用空间以便我能够下载数据?我绝对必须在本地拥有数据才能在应用中启用离线功能
----- 编辑-----
在实现将 NSArray 拆分为数组数组后,我仍然遇到同样的问题,如果新代码按照建议如下:
拆分方法
+ (NSArray *) splitIntoArraysOfBatchSize:(NSArray *)originalArray :(int)batchSize {
NSMutableArray *arrayOfArrays = [NSMutableArray array];
for(int j = 0; j < [originalArray count]; j += batchSize) {
NSArray *subarray = [originalArray subarrayWithRange:NSMakeRange(j, MIN(batchSize, [originalArray count] - j))];
[arrayOfArrays addObject:subarray];
}
return arrayOfArrays;
}
如下循环遍历数组
+(void)fetchPricelistAll:(int)pricelistId :(int)startAtRow :(int)takeNoOfRows;
{
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchPriceList:priceListId = %d", pricelistId);
}
NSString *finalURL = [NSString stringWithFormat:@"https://host.domain.com:5443/api/till/tillpricelistv2/%d?StartAtRow=%d&TakeNoOfRows=%d",pricelistId, startAtRow, takeNoOfRows];
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:finalURL]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error != nil) {
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchPriceList:Transport error %@", error);
}
} else {
NSHTTPURLResponse *responseHTTP;
responseHTTP = (NSHTTPURLResponse *) response;
if(responseHTTP.statusCode != 200) {
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchPriceList:Server Error %d", (int) responseHTTP.statusCode);
}
} else {
NSArray *priceListObjectArray = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchPriceList:count = %lu", (unsigned long)[priceListObjectArray count]);
NSLog(@"WebServices:fetchPriceList:PricelistObjectArray looks like %@",priceListObjectArray);
}
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
NSPersistentContainer *container = appDelegate.persistentContainer;
NSArray *arrayOfArrays = [NWTillHelper splitIntoArraysOfBatchSize:priceListObjectArray :1000];
for(NSArray *batch in arrayOfArrays) {
[container performBackgroundTask:^(NSManagedObjectContext *context ) {
context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;
NSDictionary *priceListObjectDict;
//Loop through the array and for each dictionary insert into local DB
for (id element in batch) {
priceListObjectDict = element;
NSString *currencyName = [priceListObjectDict objectForKey:@"currencyName"];
NSString *price = [priceListObjectDict objectForKey:@"price"];
NSString *priceIncTax = [priceListObjectDict objectForKey:@"priceIncTAX"];
NSString *validFrom = [priceListObjectDict objectForKey:@"validFromDate"];
NSString *validTo = [priceListObjectDict objectForKey:@"validToDate"];
NSString *itemId = [priceListObjectDict objectForKey:@"itemID"];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:@"YYYY-MM-dd'T'HH:mm:ss"];
NSDate *validToDate = [dateFormat dateFromString:validTo];
NSDate *validFromDate = [dateFormat dateFromString:validFrom];
if([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchPriceList:validToDate: >>>> %@ <<<<", validToDate);
NSLog(@"WebServices:fetchPriceList:validFromDate: >>>> %@ <<<<", validFromDate);
}
if([NWTillHelper isDebug] == 1) {
NSLog(@"PimItemListView:tableView:context = %@", context);
}
NSManagedObject *newPrlItem = Nil;
newPrlItem = [NSEntityDescription
insertNewObjectForEntityForName:@"PriceList"
inManagedObjectContext:context];
[newPrlItem setValue:itemId forKey:@"itemId"];
[newPrlItem setValue:validToDate forKey:@"validTo"];
[newPrlItem setValue:validFromDate forKey:@"validFrom"];
[newPrlItem setValue:price forKey:@"price"];
[newPrlItem setValue:priceIncTax forKey:@"priceIncTax"];
[newPrlItem setValue:currencyName forKey:@"currencyName"];
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchTillData:ItemId in loop = %@", itemId);
NSLog(@"WebServices:fetchTillData:newPrlItem = %@", newPrlItem);
NSLog(@"WebServices:fetchTillData:CoreData error = %@", error);
}
}
NSError *error = nil;
if (![context save:&error]) {
NSLog(@"Failure to save context: %@\n%@", [error localizedDescription], [error userInfo]);
abort();
} else {
NSUserDefaults *tillUserDefaults = [NSUserDefaults standardUserDefaults];
[tillUserDefaults setInteger:1 forKey:@"hasPriceList"];
[tillUserDefaults synchronize];
}
}];
}
}
}
}] resume];
}
它仍然增加到 2 GB 并被终止,当 NSURLSession 完成块达到内存使用量约为 250 时,我假设是在它将整个数据集下载到 NSArray 之后,但在那之后,当我想写入核心时数据全部出错,达到 2 GB 并被终止
为什么会这样?
【问题讨论】:
-
通过分页获取更少的数据。
-
我不能像刚才所说的那样获取更少的数据,请您详细说明
-
添加查询限制。 (偏移量和限制)
-
我已经有了,但在这种情况下,我如何循环并发送多个任务?因为我最终会遇到线程问题,不是吗?你有一些指向任何文档或任何提示的指针吗?
标签: objective-c core-data