predicateWithFormat非常慢

时间:2013-05-30 02:34:51

标签: ios objective-c cocoa-touch core-data

我在coredata NSManagedObjectContextDidSaveNotification中运行谓词来过滤我感兴趣的相关对象。

  - (void)didSaveNotficiation:(NSNotification*)notification
    {
            NSSet *objects = nil;
            NSMutableSet *combinedSet = nil;
            NSPredicate *predicate = nil;

            NSDictionary *userInfo = [notification userInfo];

            objects = [userInfo objectForKey:NSInsertedObjectsKey];
            combinedSet = [NSMutableSet setWithSet:objects];

            objects = [[notification userInfo] objectForKey:NSUpdatedObjectsKey];
            [combinedSet unionSet:objects];

            objects = [[notification userInfo] objectForKey:NSDeletedObjectsKey];
            [combinedSet unionSet:objects];

//THis is slow
            predicate = [NSPredicate predicateWithFormat:@"entity.name == %@ && %K == %@",
                                                         [XXContact entityName], XXContactRelationship.user,self];
            [combinedSet filterUsingPredicate:predicate];

            if ([combinedSet count] == 0) {
                return;
            }

            [self process];

/* This is much faster
            [combinedSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
                if ([obj isKindOfClass:[XXContact class]]) {
                    XXContact* contact = (XXContact*)obj;
                    if (contact.user == self) {
                        [self process];
                        *stop = YES;
                    }
                }
            }];
*/
}

应用启动时,通知可以被调用超过100次。 当我对应用程序进行概要分析时,似乎函数predicateWithFormat非常慢,占用了20%的cpu。甚至过滤都不是很慢。谓词本身的创建是如此缓慢。 如果我将其更改为使用enumerateObjectsUsingBlock,它会变得更快,但代码的可读性较差。

有没有人有解释?谢谢。

1 个答案:

答案 0 :(得分:1)

由于以下几个原因,您无法使用当前谓词超过枚举过滤所实现的时间:

  1. 您在每次调用didSaveNotficiation :
  2. 时分配,解析和编写谓词
  3. 你在谓词中使用字符串比较,这比'isKindOfClass:'class
  4. 要贵得多
  5. 您的实现中有一个停止条件,这在谓词中是不可能的(combinedSet中的所有对象都必须进行评估)
  6. 您的谓词过滤执行正在改变集合(删除对象)
  7. 我相信您最好的选择是自己实施谓词

    为了改进谓词的实现,我建议:

    //1. change .name property to an Integer/Enum value
    //2. make your predicate static and reduce the compose and parse needs:
    //(If you must use %K in your predicate this would be much harder)
    //NOT TESTED
    static NSPredicate* p = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        p = [NSPredicate predicateWithFormat:@"entity.name == $ENAME AND user == $USEROBJECT"];
    });
    
    NSPredicate* currPredicate = [p predicateWithSubstitutionVariables:@{@"ENAME" : [XXContact entityName], @"USEROBJECT" : [self objectID]}];
    [combinedSet filterUsingPredicate:currPredicate];
    

    如您所见,如果您尝试提高可读性,则会受损。