【问题标题】:Faster way to pull specific data from NSMutableArray?从 NSMutableArray 中提取特定数据的更快方法?
【发布时间】:2013-11-30 02:20:37
【问题描述】:

我有一个包含我创建的名为“问题”的 NSObject 的数组。 每个问题的一个属性是它属于哪个级别。 如果用户选择玩 2 级,我想获取所有 .level 属性为 2 的问题。现在我正在循环所有问题以查找匹配项,但这在 iPad 上需要大约 2 秒3 / 新的 iPad 设备。有没有更快的方法来处理这种情况?

int goThrough = 0;    
do {
      Question *currentQuestion = [allQs objectAtIndex:(goThrough)];

      if (currentQuestion.level == levelChosen) {
             [questions addObject:currentQuestion];
      }
      goThrough++;
    } while (goThrough < [allQs count]);

非常感谢您的帮助!

【问题讨论】:

  • 您必须有 一大堆问题 才能在 2 秒内完成循环。我会在别处寻找您的延误。我也可能会缓存过滤后的列表。我还建议熟悉-[NSArray enumerateObjectsUsingBlock:]
  • 你寻找的东西被称为数据库。

标签: ios objective-c arrays sorting


【解决方案1】:

如果您必须定期按级别组织问题,那么为什么不按级别组织所有问题。创建一个数组字典。如果级别和每个数组的每个键是该级别的问题列表。你这样做一次,得到一个级别的问题就变得微不足道了。

【讨论】:

  • 确实,改变您的设计以避免重复过滤对象将比任何优化的过滤方法更快(假设问题列表是静态的。)这似乎比我们的建议更好使用 enumerateObjectsUsingBlock。 (投票)
  • @DuncanC 即使列表不是静态的。添加新问题时,您只需将其添加到适当的子数组中即可。
【解决方案2】:

我目前无法访问 mac,但您可以尝试一下:

[allQs  enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger index, BOOL *stop) {
  Question *currentQuestion = [allQs objectAtIndex:index];
  if (currentQuestion.level == levelChosen) {
    [questions addObject:currentQuestion];
  }
}

这将使用您设备的所有内核,因此速度可以提高一倍

【讨论】:

  • 请注意,将对象添加到可变数组不是线程安全的,因此您必须通过某种同步机制保护[questions addObject:currentQuestion](这会使一切再次变慢)。
  • 阿斯特里,马丁是对的。您不能同时从多个线程将对象添加到可变数组。有关几种不同的解决方案,请参阅我的帖子。我认为最后一个,使用对 indexOfObjectsWithOptions:usingBlock: 的并发调用可能是最快的,因为它避免了从后台线程改变数组。它还有一个优点是它不会一次将一个可变数组变大,这也很慢。这个讨论很有趣,也很有教育意义,但 Maddy 说得对,在开始时重新按级别分解数据确实更好。
【解决方案3】:

您总是可以使用快速枚举(除非您打算改变对象,否则这是枚举集合的最快方法)。像这样的:

for (Question *thisQuestion in allQs) {
    if (thisQuestion.level == levelChosen) 
        [questions addObject:thisQuestion];
    }
}

由于您没有对正在迭代的集合 (allQs) 进行变异,因此它可以正常工作并且比使用 enumerateObjectsUsingBlock 更快。如果您需要正在迭代的数组的索引 (allQs),请使用 enumerateObjectsUsingBlock

【讨论】:

  • for...in 循环是否比单线程 enumerateObjectsUsingBlock 快?我想知道这一点。您是否自己对此进行了计时,或者性能差异是否记录在某处?
  • 我的经验表明,使用 Fast Enum 的结果会稍微快一些,Matt Thompson 的这篇文章支持了这一点。 nshipster.com/enumerators 也就是说,我认为在大多数情况下(甚至在某些情况下)速度优势很小,而 enumerateObjectsUsingBlock 确实提供了免费获取索引的优势。我通常使用 for-in 只是因为它非常简单(除非我出于某种原因需要索引)。
【解决方案4】:

我建议使用 NSArray 方法 enumerateObjectsUsingBlock 或其变体之一。甚至还有一些变体会同时循环遍历数组元素。但是,您可能需要使用锁将元素添加到您的问题数组中,因为我怀疑 NSMutableArray 的 addObject 方法是否是线程安全的。

您可能应该针对具有锁定的并发版本测试非并发版本,以查看哪个更快。哪种方法更快取决于 allQs 数组中有多少对象属于当前级别。如果只有少数人属于,那么断言锁的代码不会经常触发,并发的好处将超过断言锁的时间损失。如果 allQs 数组中的大多数对象都与所选级别匹配,代码最终会花费大量时间来断言锁,而并发线程仍将等待其他线程释放锁。

修改后的代码可能如下所示:

单线程版本:

[allQs enumerateObjectsUsingBlock:
   ^(Question *currentQuestion, NSUInteger index, BOOL *stop)
   {
     if (currentQuestion.level == levelChosen)
       [questions addObject:currentQuestion];
   }
];

并发版本:

[allQs enumerateObjectsWithOptions:
     NSEnumerationConcurrent
   usingBlock:
   ^(Question *currentQuestion, NSUInteger index, BOOL *stop)
   {
     if (currentQuestion.level == levelChosen)
       @synchronized
       {
         [questions addObject:currentQuestion];
       }
   }
];

实际上,现在我考虑了一下,首先使用 indexOfObjectsWithOptions:passingTest 对数组进行并发传递,您可能会获得更快的性能。在那个过程中,您将构建一个包含与当前级别匹配的所有对象的 NSIndexSet。然后在一次通过中,您会将这些元素提取到另一个数组中:

NSIndexSet *questionIndexes = [allQs indexesOfObjectsWithOptions: NSEnumerationConcurrent        
  usingBlock:
  ^(Question *currentQuestion, NSUInteger index, BOOL *stop)
  {
    return (currentQuestion.level == levelChosen)
  }
];
questions = [allQs objectsAtIndexes: questionIndexes];

另一位发帖人指出,最好提前将一系列问题分解开来。如果这适用于您的程序流程,那就更好了,因为根本不过滤您的数组总是比最优化的过滤代码更快。

【讨论】:

    【解决方案5】:

    似乎缺少一个简单的答案。如果您想过滤数组的对象以只剩下某些对象,-filteredArrayUsingPredicate: 就是您想要的。它可以非常简单地完成。

    NSPredicate *p = [NSPredicate predicateWithBlock:^(Question *aQuestion, NSDictionary *bindings){
        return (aQuestion.level==2);
    }];
    NSArray *filteredArray = [originalArray filteredArrayUsingPredicate:p];
    

    【讨论】:

    • 问题不是让它变得简单,而是让它变得更快。使用谓词将比专门构建的数组慢得多。
    • -predicatWithFormat: 创建一个谓词很昂贵。使用 one 与使用枚举遍历整个数组相同。它实际上可能更快,因为框架擅长利用设备硬件进行数组过滤。
    猜你喜欢
    • 2019-12-31
    • 1970-01-01
    • 2018-06-25
    • 1970-01-01
    • 1970-01-01
    • 2016-08-03
    • 1970-01-01
    • 2013-09-03
    • 2012-01-03
    相关资源
    最近更新 更多