Objective C —枚举数组最快且最有效的方法是什么?
编辑
我读了一些关于块、快速枚举和 GCD 等的文章。 @Bbum 撰写了许多关于 GCD 和块主题的文章,他说块枚举方法总是与快速枚举等效方法一样快或更快。您可以在此处阅读他的推理。
虽然这是一次引人入胜、充满智慧的对话,但我同意那些说这实际上取决于手头任务的人的观点。
我有一些任务要完成,我需要快速、廉价且高效地完成它们。对于如何枚举数组,Apple 为我们提供了许多 选择,但我不确定该选择哪个。
快速枚举
for (id obj in array)
{
/* Do something with |obj|. */
}
非并发块枚举
[array enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
/* Do something with |obj|. */
}];
并发块枚举
[array enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
/* Do something with |obj|. */
}];
GCD 应用
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_apply([array count], queue, ^(size_t idx) {
id obj = [array objectAtIndex: idx];
/* Do something with |obj|. */
});
GCD 异步应用
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^(void) {
dispatch_apply([array count], queue, ^(size_t idx) {
id obj = [array objectAtIndex: idx];
/* Do something with |obj|. */
});
});
或者可能带有 NSBlockOperation
或 NSOperationQueue
的东西?
蒂亚,亚历克斯。
Edit
I read through some articles on blocks and fast enumeration and GCD and the like. @Bbum, who's written many articles on the subject of GCD and blocks, says that the block enumeration methods are always as fast or faster than the fast enumeration equivalents. You can read his reasoning here.
While this has been a fascinating, intellectual conversation, I agree with those who said that it really depends on the task at hand.
I have some tasks to accomplish and I need them done fast, cheap, and efficiently. Apple gives us many choices for how we want to enumerate an array, but I'm not sure which to choose.
Fast Enumeration
for (id obj in array)
{
/* Do something with |obj|. */
}
Nonconcurrent Block Enumeration
[array enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
/* Do something with |obj|. */
}];
Concurrent Block Enumeration
[array enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
/* Do something with |obj|. */
}];
GCD Apply
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_apply([array count], queue, ^(size_t idx) {
id obj = [array objectAtIndex: idx];
/* Do something with |obj|. */
});
GCD Async Apply
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^(void) {
dispatch_apply([array count], queue, ^(size_t idx) {
id obj = [array objectAtIndex: idx];
/* Do something with |obj|. */
});
});
Or perhaps something with NSBlockOperation
s or an NSOperationQueue
?
TIA, Alex.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
最快的代码是最先到达市场的代码。
说真的——除非你有一个可衡量的性能问题,否则这个特定的选择应该不会占用你更多的时间来回答这些模式中的哪一个最适合我的项目风格?
注意:解决从串行执行转移到并发执行的性能问题通常会导致两个问题;性能与并发。
The fastest code is the code that reaches the market first.
Seriously -- unless you have a measurable performance problem, this particular choice should occupy no more of your time than it takes to answer which of these patterns fits the most naturally with my project's style?
Note: adressing a performance problem by moving from serial to concurrent execution usually results having two problems; performance & concurrency.
这实际上取决于手头的任务。
一次处理多个迭代需要生成线程。如果迭代中的逻辑是可并行的并且花费的时间比生成线程所需的时间多,则使用线程。另外,如果数组中的项目太多,生成一个线程所需的时间比遍历整个数组要少,请将数组分成几个部分并并行处理它们。
否则,生成线程来迭代数组会产生开销。即使操作系统为您处理了这些问题,它仍然需要生成它们。这需要时间和资源以及运行时的上下文切换(取决于可用 CPU 的数量、负载、调度程序等)。
这一切都取决于生成一个新线程是否比遍历整个数组花费更长的时间。您可以使用分析工具找到这一点。
It really depends on the task at hand.
Processing more than one iteration at a time requires spawning threads. If the logic in the iterations is parallelizable and takes more time than it would take to spawn a thread, then use threads. Also, if you have so many items in the array that it would take less to spawn a thread than to walk through the whole array, divide your array into a few pieces and process them in parallel.
Otherwise, spawning threads to iterate through the array is overhead. Even if the OS takes care of that for you, it still does need to spawn them. That takes time and resources and context switching at runtime (depending on the number of CPUs available, load, scheduler, etc).
It all comes down to whether spawning a new thread takes longer than walking through the whole array. You can find that out using the profiling tools.
如果您观看 WWDC 视频“iOS7 中的隐藏开发宝石”(这个问题比授予的视频更早),您将看到使用 GCD 可以更好地实现简单的 for 循环枚举:
[array enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock: ^ (id obj, NSUInteger idx, BOOL *stop) {
/* 用 |obj| 做一些事情。 */
}];
虽然这在一定程度上取决于手头的任务,并且交付软件是最重要的,但绝对有一些简单的方法可以防止性能问题在未来作为技术债务问题出现。应用程序开始扩展。例如,您可能有一个很大的网络获取结果。根据此结果,您可能必须迭代获取的对象并更新某些模型或一系列模型(不是 Core Data 模型,因为 Core Data 不是线程安全的!必须采取其他措施才能在多个线程上使用 Core Data。这是如果您正在考虑在多线程场景中处理核心数据,那么您将需要研究一个深入的主题)。考虑到您没有对 UI 执行更新(因为这不是您想要在主线程之外执行的操作),您可以使用此方法来充分利用设备的硬件。与 for 循环一次仅使用一个核心不同,它将充分并行地利用可用核心,从而提高性能并可能将执行时间几乎减半。如果不考虑要枚举的数组和在块内执行的任务,这不是您想要做的事情,但如果仔细使用,它对于多种情况都很有用。
If you watch the WWDC video "Hidden Development Gems in iOS7" (this question is older than that video granted), you will see that simple for loop enumerations can be better implemented using GCD's:
[array enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
/* Do something with |obj|. */
}];
Although it does depend on the task at hand to an extent and shipping software is of the most importance, there are definitely easy ways to keep performance issues from cropping up as a tech debt issue in the future when your application starts to scale. For example, you may have a large network fetch result. From this result, you may have to iterate over the fetched objects and update some model or series of models (not Core Data models as Core Data is not thread safe! Additional measures must be taken to work with Core Data on multiple threads. This is a deep topic you will want to research if you are considering working on Core Data from a multi-threading scenario). Considering you are not performing updates to the UI (as that is not something you would want to do off of the main thread), you can use this method to take full advantage of the hardware of your device. Instead of the for loop using only one core at a time, it will fully utilize the available cores in parallel, therefore increasing performance and potentially almost halving the time of execution. This is not something you will want to do without giving any thought to the array you are enumerating over and the tasks you are performing from within the block, but is useful for a wide array of cases if used carefully.