predicateWithFormat 很慢

Posted

技术标签:

【中文标题】predicateWithFormat 很慢【英文标题】:predicateWithFormat is very slow 【发布时间】:2013-05-30 02:34:50 【问题描述】:

我在 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】:

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

    您在每次调用didSaveNotficiation : 时分配、解析和组合谓词 您在谓词中使用字符串比较,这比 'isKindOfClass:' 类成本高得多 您的实现中有一个停止条件,这在谓词中是不可能的(combinedSet 中的所有对象都必须进行评估) 您的谓词过滤执行正在改变集合(删除对象)

我相信您最好的选择是自己实现谓词

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

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

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

【讨论】:

以上是关于predicateWithFormat 很慢的主要内容,如果未能解决你的问题,请参考以下文章

yeoman新建webapp时总是很慢,卡在pre-build test上是啥原因

NSPredicate - predicateWithFormat 不安全

用于电子邮件问题的 NSPredicate predicateWithFormat

predicateWithFormat 返回错误的核心数据对象

+[NSPredicate predicateWithFormat] 替换保留字

如何使用 predicateWithFormat 查询 NSString 包含在 NSSet 中