问题: 当插入到Core Data的记录数量无法预测时,如何释放NSManagedObjectContext使用的内存(我猜),这样可以有效地使用内存?
以下是我的理由: 我有一个蓝牙设备,每隔0.00125秒就会连续向iOS设备发送12组整数(最小间隔,最大情况为0.002秒),然后我应该将这些整数存储到带有时间戳的CoreData中。
数据对象和关联:
当进程开始时,创建标题记录(NSManagedObject)作为从蓝牙设备检索批量接收数据的密钥。在整个数据接收期间,对象将作为强属性保留,并在进程结束时从属性中删除(可能设置为nil)。
设计NSManagedObjectContext:
所有三个ManagedObjectContext都是存储在AppDelegate
中的单例对象下面列出了创建ManagedObjectContext的代码:
- (NSManagedObjectContext *)masterManagedObjectContext {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_masterManagedObjectContext != nil) {
return _masterManagedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_masterManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_masterManagedObjectContext setPersistentStoreCoordinator:coordinator];
[_masterManagedObjectContext setUndoManager:nil];
return _masterManagedObjectContext;
}
-(NSManagedObjectContext*) backgroundManagedObjectContext{
if(_backgroundManagedObjectContext != nil){
return _backgroundManagedObjectContext;
}
_backgroundManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
[_backgroundManagedObjectContext setUndoManager:nil];
[_backgroundManagedObjectContext setParentContext:[self masterManagedObjectContext]];
return _backgroundManagedObjectContext;
}
-(NSManagedObjectContext*) mainManagedObjectContext{
if(_mainManagedObjectContext !=nil){
return _mainManagedObjectContext;
}
_mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_mainManagedObjectContext setUndoManager:nil];
[_mainManagedObjectContext setParentContext:[self masterManagedObjectContext]];
return _mainManagedObjectContext;
}
导入在backgroundManagedObjectContext中处理。 使用以下代码创建和存储标题:
_header = [NSEntityDescription insertNewObjectForEntityForName:@"Header" inManagedObjectContext:_backgroundManagedObjectContext];
_header.startTime = [NSDate date];
NSError *error;
BOOL success = [_backgroundManagedObjectContext save:&error];
当蓝牙设备触发方法时,使用以下代码创建和存储接收数据:
@autoreleasepool {
ReceivedData* data = [NSEntityDescription insertNewObjectForEntityForName:@"ReceivedData" inManagedObjectContext:_backgroundManagedObjectContext];
//Data is set here
[_header addFk_header_many_dataObject:data];
currentCount ++;
if(currentCount >=1000){
currentCount = 0;
NSError *error;
BOOL success = [_backgroundManagedObjectContext save:&error];
}
}
接收的数据将被存储到managedObjectContext中,每1000个数据被接收。
如果我停止了这个过程,消耗的内存会翻倍,并持续到我完全终止该应用程序。
下面列出了处理流程结束的代码:
_header.endTime = [NSDate date];
_header = nil;
NSError *error;
BOOL success = [_backgroundManagedObjectContext save:&error];
[_masterManagedObjectContext performBlock:^{
NSError* mastererror;
BOOL mastersuccess = [_masterManagedObjectContext save:&mastererror];
}];
问题: 正如Core Data Performance by Apple所提到的,使用NSManagedObjectContext的reset方法的将删除与上下文关联的所有托管对象,并且"重新开始"好像你刚刚创建它。
根据我的理解,这意味着我只能在整个过程结束时调用此方法。我已经尝试在保存_backgroundManagedObjectContext和_masterManagedObjectContext之后添加重置函数。但是,记忆保持不变。
内存使用情况的说明 对于每0.002秒接收一次数据的情况,每1000条记录增加的0.5MB内存将保存到backgroundManagedObjectContext。因此,应用程序将消耗大约150 MB,持续8分钟的处理时间,当此进程终止时内存将增加到320MB,并将保留大约220MB的内存使用量。
问题: 当插入到Core Data的记录数量无法预测时,如何释放NSManagedObjectContext使用的内存(我猜),这样可以有效地使用内存?
对于一些愚蠢的错误感到抱歉,因为我在iOS上很新。在发布问题之前,我已尽力搜索。
感谢您的帮助。 非常感谢你。
说明 我在不超过10分钟的处理时间内尝试了上述情况。但是,实施应该延长到超过1小时的处理时间。我仍然不知道如何处理这种情况。
编辑1 修改了显示ReceivedData和Header关系的代码 EDIT 2 更新了@flexaddicted
提及的标准代码答案 0 :(得分:0)
只是我的建议。也许有人可能有不同的方法。
在这种情况下,我将消除BackgroundManagedObjectContext,我只留下MasterManagedObjectContext(作为主要的父级)。因为,您需要一个低内存配置文件,您应该切换到允许您控制应用程序的内存占用的机制。所以,我会创建一种开始收集接收数据的缓冲区。当缓冲区达到其限制时,我会将接收数据移动到MasterManagedObjectContext,以便将它们保存到持久存储中。这里应该根据应用程序性能调整缓冲区的限制(结构的向量或对象数组)。通过这种方式,您可以直接控制所创建的对象。所以,每当你完成一堆导入的数据(其中束是该向量/数组的限制)时,你可以扔掉它们。
否则,您可以尝试以下方法。
@autoreleasepool {
NSMutableArray *temporary = [NSMutableArray array];
ReceivedData* data = [NSEntityDescription insertNewObjectForEntityForName:@"ReceivedData" inManagedObjectContext:_backgroundManagedObjectContext];
// Data is set here
// Let temporary to hold a reference of the data object
[temporary addObject:data];
currentCount ++;
if(currentCount >=1000){
currentCount = 0;
NSError *error;
BOOL success = [_backgroundManagedObjectContext save:&error];
for(NSManagedObject *object in temporary) {
[_backgroundManagedObjectContext refreshObject:object mergeChanges:NO];
}
[temporary removeAllObjects];
}
}
更新1
您是否还可以显示您在ReceiveData
和Header
之间设置关系的位置?我之所以这样问,是因为你可以改变设置这两个实体之间关系的时间。
根据您修改的代码。
@autoreleasepool {
receivedData* data = [NSEntityDescription insertNewObjectForEntityForName:@"ReceivedData" inManagedObjectContext:_backgroundManagedObjectContext];
//Data is set here
[_header addFk_header_many_dataObject:data];
currentCount ++;
if(currentCount >=1000){
currentCount = 0;
NSError *error;
BOOL success = [_backgroundManagedObjectContext save:&error];
}
}
如果您能够在主队列上预测此关联(我想您需要将该属性设置为可选属性),您可以执行以下操作:
@autoreleasepool {
ReceivedData* data = [NSEntityDescription insertNewObjectForEntityForName:@"ReceivedData" inManagedObjectContext:_backgroundManagedObjectContext];
// Data is set here
// Move it later
//[_header addFk_header_many_dataObject:data];
currentCount ++;
if(currentCount >=1000){
currentCount = 0;
NSError *error;
BOOL success = [_backgroundManagedObjectContext save:&error];
[_backgroundManagedObjectContext reset];
}
}
P.S。 receiveData *data = ...
应为ReceiveData *data = ...
。换句话说,课程应以大写字母开头。