我有一个iPhone应用程序需要避免插入重复记录,起初我以为我可以只测试我的内存数组中的每个对象,为每个对象创建一个获取请求并测试它是否存在。但这在设备上证明是慢的,非常慢(大约5秒,这是不可接受的)。
我一直在努力拼凑如何创建一些我可以使用的智能谓词,以使其有效地工作但没有太大的成功。
我的对象有一个NSNumber字段,我将其设置为“标识属性”,也是非可选的。此字段称为sampleTime
(同样,这不是日期,而是NSNumber)
这是我的想法(借鉴了其他主题甚至我自己的一些问题):
显然,每个对象(大约380个对象)进行一次获取对性能不起作用,所以我的印象是我可以在内存中完成大部分操作并且会更快。我需要创建一个使用IN子句的谓词,然后遍历该获取结果,测试任何一个对象是否在该结果集内,如果不是则插入它,如果SO则不执行任何操作。
但我的实施不起作用:
NSMutableArray *timeStampArray = [[NSMutableArray alloc] init];
for (id emTmp in myListOfObjects)
[timeStampArray addObject: [NSNumber numberWithInt:[[emTmp sampleTime] timeIntervalSince1970]]];
NSFetchRequest *fetch = [[[NSFetchRequest alloc] init] autorelease];
[fetch setEntity:[NSEntityDescription entityForName:@"ElectricalMeasurementEntity" inManagedObjectContext:context]];
[fetch setPredicate:[NSPredicate predicateWithFormat:@"sampleTime in %@", timeStampArray]];
NSArray *results = [context executeFetchRequest:fetch error:nil];
int resCount = [results count];
但resCount总是为零,我不知道为什么......
BTW,myListOfObjects包含业务对象,这些业务对象也具有作为NSDate类型的sampleTime属性。
修改
好的,更新,我得到了基础知识。我没有得到任何结果的原因是因为创建数组的循环使用id
类型,当我使用它的方式使用它时,并没有正确创建NSNumber对象。
现在我这样做:
for (id emTmp in myListOfObjects)
{
Measurement *t = (Measurement*)emTmp;
NSTimeInterval d = [t.sampleTime timeIntervalSince1970];
[timeStampArray addObject: [NSNumber numberWithInt:[[t sampleTime] timeIntervalSince1970]]];
}
创建了一个很好用的列表。
然而,我继续这样做:
NSMutableArray *itemsToInsert = [[NSMutableArray alloc] init];
for (id em in myListOfObjects)
{
BOOL found = NO;
Measurement *t = (Measurement*)em;
for (id e in results) //results from Fetch Request which is now populated properly
{
MeasurementEntity *entity = (MeasurementEntity*)e;
if ([entity.sampleTime isEqualToNumber:[NSNumber numberWithInt:[[t sampleTime] timeIntervalSince1970]]])
{
found = YES;
break;
}
}
if (!found)
[itemsToInsert addObject: t];
}
这个循环(大约850个对象,在iPhone 3Gs上)需要大约10 - 12秒,我可以看到为什么(当850 * 850 = 722500循环!)。我可以更有效率吗?
由于
答案 0 :(得分:3)
您需要删除提取,以便只检查一个属性。然后进行所有与谓词速度的比较。像这样:
NSArray *newData=[NSArray arrayWithObjects:[NSNumber numberWithInt:1],[NSNumber numberWithInt:6],nil];
NSManagedObject *mo;
for (int i=0; i<5; i++) {
mo=[NSEntityDescription insertNewObjectForEntityForName:@"Test" inManagedObjectContext:self.moc];
[mo setValue:[NSNumber numberWithInt:i] forKey:@"numAttrib" ];
}
[self saveContext];
NSFetchRequest *fetch=[[NSFetchRequest alloc] init];
NSEntityDescription *testEntity=[NSEntityDescription entityForName:@"Test" inManagedObjectContext:self.moc];
[fetch setEntity:testEntity];
// fetch only the one property you need to test
NSDictionary *propDict=[testEntity propertiesByName];
[fetch setPropertiesToFetch:[NSArray arrayWithObject:[propDict valueForKey:@"numAttrib"]]];
// Return as dictionaries so you don't have the overhead of live objects
[fetch setResultType:NSDictionaryResultType];
// fetch only those existing property values that match the new data
NSPredicate *p=[NSPredicate predicateWithFormat:@"numAttrib in %@",newData];
[fetch setPredicate:p];
NSArray *fetchReturn=[self performFetch:fetch];//<-- my custom boilerplate
// extract the existing values from the dictionaries into an array
NSArray *values=[fetchReturn valueForKey:@"numAttrib"];
// filter out all new data values that already exist in Core Data
p=[NSPredicate predicateWithFormat:@"NOT (SELF in %@)",values];
NSArray *unmatchedValues=[newData filteredArrayUsingPredicate:p];
NSLog(@"unmatcheValues=%@",unmatchedValues);
......输出:
unmatcheValues=(
6
)
现在您只需要为返回的值创建新的托管对象。所有其他新值已经存在。
答案 1 :(得分:0)
这似乎是一个明显的建议,但如果[context executeFetchRequest:fetch error:nil]
没有返回任何结果,那么首先要做的就是检查错误而不是忽略它们(通过设置error:nil
)。
类似的东西:
NSError *error = nil;
NSArray *results = [context executeFetchRequest:fetch error:&error];
if (results == nil) { // fetch failed - huh?
NSLog(@"Fetch error %@, %@", error, [error userInfo]);
}