代码之家  ›  专栏  ›  技术社区  ›  Mark

核心数据性能问题

  •  1
  • Mark  · 技术社区  · 15 年前

    我有一个iPhone应用程序,它需要避免插入重复的记录,起初我以为我可以测试内存数组中的每个对象,为每个对象创建一个提取请求,并测试它是否存在。但这在设备上证明很慢,非常慢(大约5秒,这是不可接受的)。

    我一直在尝试将如何创建一些智能谓词拼凑在一起,我可以使用这些谓词来有效地工作,但不会取得太大的成功。

    我的对象有一个nsnumber字段,我已将其设置为“标识属性”,而且也是非可选的。此字段称为 sampleTime (同样,这不是日期,而是一个nsnumber)

    以下是我的想法(借用其他线索,甚至我自己的一些问题):

    显然,为每个对象(大约380个对象)执行一次获取操作并不能提高性能,所以我觉得我可以在内存中完成大部分操作,而且速度会更快。我需要创建一些使用IN子句的谓词,然后遍历该提取结果,测试是否有任何一个对象在该结果集中,如果没有,则插入它,如果有,则不做任何操作。

    但是我的实现没有起作用:

    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];
    

    但重新计数总是零,我不知道为什么…

    顺便说一句,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个对象,在iPhone3GS上)大约需要10-12秒,我可以理解为什么(当850*850=722500个循环!)我能提高效率吗?

    谢谢

    2 回复  |  直到 15 年前
        1
  •  3
  •   TechZen    15 年前

    您需要剥离提取,以便它只检查一个属性。然后用谓词比较速度。像这样:

    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
    )
    

    现在您只需要为返回的值创建新的托管对象。所有其他新值都已存在。

        2
  •  0
  •   David Gelhar    15 年前

    这似乎是一个明显的建议,但如果 [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]);
    }