神奇的记录 将大量数据保存到数据库
Posted
技术标签:
【中文标题】神奇的记录 将大量数据保存到数据库【英文标题】:Magical record Saving big amount of data to DB 【发布时间】:2014-08-03 09:48:39 【问题描述】:我正在使用神奇的记录将大量数据保存到我的数据库中。我想将它保存在后台,但它冻结了 UI。我正在使用
保存它[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext)
completion:^(BOOL success, NSError *error)
];
所以它不应该阻塞用户界面。但我发现它出于某些原因使用主线程来合并更改或类似的东西。如果有人可以提出如何提高效率的解决方案,我将不胜感激。
我正在使用的代码:
- (void)saveRidesForUser:(User *)user fromResponseData:(id)responseData
if (![[[responseData valueForKey:@"rides"] class] isSubclassOfClass:[NSArray class]])
return;
NSArray *rides = [responseData valueForKey:@"rides"];
if (!rides)
return;
NSMutableArray *photoCacheArray = [[NSMutableArray alloc] init];
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext)
User *rideUser = [user MR_inContext:localContext];
for (NSDictionary *rideDictionary in rides)
Ride *ride = [Ride MR_createEntityInContext:localContext];
ride.title = ([rideDictionary objectForKey:@"title"]) ? [rideDictionary valueForKey:@"title"] : @"";
NSNumber *rideTimeStamp =[rideDictionary objectForKey:@"start_date"];
if (rideTimeStamp)
ride.startDate = [NSDate dateWithTimeIntervalSince1970:[rideTimeStamp doubleValue]];
NSNumber *rideEndTimeStamp =[rideDictionary objectForKey:@"end_date"];
if (rideEndTimeStamp)
ride.endDate = [NSDate dateWithTimeIntervalSince1970:[rideEndTimeStamp doubleValue]];
ride.serverID = [NSNumber numberWithInt:[[rideDictionary objectForKey:@"id"] intValue]];
ride.isPopular = [NSNumber numberWithBool:NO];
if ([rideDictionary objectForKey:@"intervals"])
NSArray *intervals = [rideDictionary objectForKey:@"intervals"];
for (NSDictionary *intervalDictionary in intervals)
RideInterval *interval = [RideInterval MR_createEntityInContext:localContext];
interval.startDate = [NSDate dateWithTimeIntervalSince1970:[[intervalDictionary objectForKey:@"start_date"] integerValue]];
interval.endDate = [NSDate dateWithTimeIntervalSince1970:[[intervalDictionary objectForKey:@"end_date"] integerValue]];
interval.ride = ride;
if ([intervalDictionary objectForKey:@"points"] && ![[[intervalDictionary objectForKey:@"points"] class] isSubclassOfClass:[NSString class]])
NSArray *points = [intervalDictionary objectForKey:@"points"];
for (NSDictionary *pointDict in points)
NSDictionary *pointDictionary = [pointDict dictionaryByReplacingNullsWithStrings];
RidePoint *point = [RidePoint MR_createEntityInContext:localContext];
point.timestamp = [NSDate dateWithTimeIntervalSince1970:[[pointDictionary objectForKey:@"timestamp"] integerValue]];
point.latitude = [NSNumber numberWithDouble:[[pointDictionary objectForKey:@"latitude"] doubleValue]];
point.longitude = [NSNumber numberWithDouble:[[pointDictionary objectForKey:@"longitude"] doubleValue]];
point.distanceFromPrevious = [NSNumber numberWithDouble:[[pointDictionary objectForKey:@"distance_from_previous"] doubleValue]];
point.interval = interval;
if ([rideDictionary valueForKey:@"photos"] && [[[rideDictionary valueForKey:@"photos"] class] isSubclassOfClass:[NSArray class]])
NSArray *photos = [rideDictionary objectForKey:@"photos"];
for (NSDictionary *photoDict in photos)
NSDictionary *photoDictionary = [photoDict dictionaryByReplacingNullsWithStrings];
RidePhoto *photo = [RidePhoto MR_createEntityInContext:localContext];
photo.caption = [photoDictionary valueForKey:@"caption"];
NSNumber *timeStamp =[photoDictionary objectForKey:@"timestamp"];
if (timeStamp)
photo.timestamp = [NSDate dateWithTimeIntervalSince1970:[timeStamp doubleValue]];
photo.locationLatitude = [NSNumber numberWithDouble:[[photoDictionary objectForKey:@"location_latitude"] doubleValue]];
photo.locationLongitude = [NSNumber numberWithDouble:[[photoDictionary objectForKey:@"location_longitude"] doubleValue]];
photo.serverPath = [photoDictionary objectForKey:@"url"];
photo.imagePath = [NSString getUUID];
photo.ride = ride;
[photoCacheArray addObject:photo];
ride.user = rideUser;
completion:^(BOOL success, NSError *error)
[[NSNotificationCenter defaultCenter] postNotificationName:kLTRideServise_RoutesDownloadedNotification object:nil userInfo:nil];
];
提前感谢您的帮助!
【问题讨论】:
【参考方案1】:您是否将 MR_defaultContext 用于所有其他主线程任务?
我正在使用 saveWithBlock: 方法进行后台保存, MR_defaultContext 用于主线程操作,它工作正常。据我所知,MagicalRecord 在保存操作结束时将 localContext 与 defaultContext 合并。
示例代码:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.0 * NSEC_PER_SEC),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext)
// your loop here in background thread
completion:^(BOOL success, NSError *error)
dispatch_async(dispatch_get_main_queue(), ^
// completion on main thread
);
];
);
【讨论】:
是的,我就是这样做的。我保存了很多数据,这会是问题吗? 检查 UI 何时开始冻结 - 在“for”循环期间、循环之后或整个保存块之后。 用户界面在 for 循环之后立即冻结。关于如何解决这个问题的任何想法? 我已经为您添加了带有 GCD 的示例代码。这应该可以完全消除您的流程中的冻结。 不幸的是它完全一样。我读过 saveWithBlock 不应该这样使用。好吧,无论如何,我已经尝试过,但没有任何改变。还有其他解决方案吗?【参考方案2】:[[NSNotificationCenter defaultCenter] postNotificationName:kLTRideServise_RoutesDownloadedNotification object:nil userInfo:nil];
==> 完成后检查您正在进行的处理。此通知在主线程上调用,可能是您正在进行大量处理,导致主线程忙,导致 UI 冻结。
还要检查您是否已从任何上下文注册 NSManagedObjectContextDidSaveNotification,并查看您是否正在那里进行任何处理。
【讨论】:
【参考方案3】:我正在使用
[[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreWithCompletion:aCallback];将上下文保存到数据库中。
它是这样记录的
/// \brief 将当前上下文中的更改异步保存到持久存储 /// \param completion 保存完成后调用的完成块。如果发生错误,该块将作为“BOOL”和“NSError”实例传递成功状态。总是在主队列上调用。 /// \discussion 在当前上下文和任何祖先上执行异步保存,直到更改被持久化到分配的持久存储。完成块将始终在主队列上调用。这对我来说很好,没有任何冻结。
【讨论】:
以上是关于神奇的记录 将大量数据保存到数据库的主要内容,如果未能解决你的问题,请参考以下文章
Expo/React Native 可以将大量加密数据保存到 iCloud 吗?