何时使用 forEach(_:) 而不是 for in?

Posted

技术标签:

【中文标题】何时使用 forEach(_:) 而不是 for in?【英文标题】:When to use forEach(_:) instead of for in? 【发布时间】:2018-01-02 03:26:51 【问题描述】:

如Array 和Dictionary forEach(_:) 实例方法中所述:

序列中的每个元素上调用给定的闭包 order 作为 for-in 循环。

不过,改编自Sequence Overview:

序列是一个值列表,您可以逐个遍历这些值 时间。 迭代序列元素的最常用方法 是使用 for-in 循环

通过forEach(_:)for in 暗示迭代序列:

let closedRange = 1...3

for element in closedRange  print(element)  // 1 2 3

closedRange.forEach  print($0)  // 1 2 3

或(数组):

let array = [1, 2, 3]

for element in array  print(element)  // 1 2 3

array.forEach  print($0)  // 1 2 3

会给出相同的输出。

为什么forEach(_:) 甚至存在?即使用它而不是 for in 循环有什么好处?从性能的角度来看,它们是否相同?

作为一种假设,它可能是一种语法糖,尤其是在使用函数式编程时。

【问题讨论】:

从性能角度看,forEachfor loopm u c h。但它也可以方便地用作Objective-C的makeObjectsPerformSelector : @vadian - 在调试版本中它要慢得多。在发布版本中不一定正确(显然取决于代码中的其他内容)。例如,我只是在发布版本中通过包含数百万个对象的数组对迭代进行基准测试,性能差异无法区分。 @Rob 感谢您的反馈。事实上,我的“基准测试”是在调试模式下执行的(只需⌘运行项目) 【参考方案1】:

forEach 没有提供性能优势。其实if you look at the source code,forEach函数其实只是简单的执行for-in。对于发布版本,与简单地使用for-in 相比,此函数的性能开销无关紧要,但对于调试版本,它会导致明显的性能影响。

forEach 的主要优势体现在您进行函数式编程时,您可以将其添加到函数式调用链中,而无需将先前的结果保存到使用 @ 时需要的单独变量中987654333@-in 语法。所以,而不是:

let objects = array.map  ... 
    .filter  ... 

for object in objects 
    ...

您可以改为使用函数式编程模式:

array.map  ... 
    .filter  ... 
    .forEach  ... 

结果是功能代码更简洁,语法噪音更少。

FWIW,Array、Dictionary 和Sequence 的文档都提醒我们forEach 引入的限制,即:

    您不能使用breakcontinue 语句退出当前 调用 body 闭包或跳过后续调用。

    body 闭包中使用return 语句将只退出 当前对body 的调用,不是来自任何外部范围,并且不会跳过 后续调用。

【讨论】:

谢谢@Rob。这是否意味着我们应该只使用函数式编程来使用forceah 我不会坚持必须将它与函数式编程结合使用。这是一个简洁的语法,你可以在任何你想要的地方使用。我只是建议它最大的用途是与函数式编程结合使用时,相比之下,for-in 模式确实变得相当麻烦。 @Rob 根据您的建议,我发布了一个与此相关的衍生问题。 ***.com/questions/47743259/… 关于链接的要点。这对我来说并不明显,出于这个原因,我有几个地方可以转换为 .forEach。【参考方案2】:

我最近遇到了一个用例,其中使用forEachfor in 更可取。假设您要从图层中删除所有子图层。如下语句不起作用,因为您需要解开 [CALayer]

for layer in self.videoContainerView.layer.sublayers!

如果 sublayers 为 nil,你会崩溃。这迫使您首先检查是否有子层。但是,forEach 使这更简单,如下所示:

self.videoContainerView.layer.sublayers?.forEach  $0.removeFromSuperlayer() 

【讨论】:

【参考方案3】:

它们或多或少可以互换,但有两个重要的区别。

    break/continue 只能在for .. in 中工作 forEach 中的 return 将退出闭包,但不会停止迭代。

原因是for .. in 是语言中的一种特殊形式(它允许中断并继续按您的预期工作)。这是您无法使用语言本身以相同方式实现的东西。

但是,forEach 不是特殊形式,可以通过将其编写为函数来相同地重新实现。

extension Sequence 
    func myOwnForEach(_ body: (Self.Element) throws -> Void) rethrows 
        let it = Self.makeIterator()
        while let item = it.next() 
            body(item)
        
    

【讨论】:

【参考方案4】:

除了上述答案之外,将for 循环与forEach 区分开来的另一个原因是,使用for 循环,我们还可以选择使用基于where 的模式匹配来实现该逻辑,例如

for adBanner in adBanners where !adBanner.isLoading 

上述与控制流相关的特性使 for 循环如此强大,但如果我们不需要这种级别的控制,使用对 forEach 的调用可能会让我们的代码看起来更简单一些。

简而言之,使用for 循环可以让我们对迭代有更大程度的控制

【讨论】:

以上是关于何时使用 forEach(_:) 而不是 for in?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我应该在循环中使用 foreach 而不是 for (int i=0; i<length; i++) ?

在 .NET 中,使用“foreach”迭代 IEnumerable<ValueType> 的实例会创建一个副本吗?那么我应该更喜欢使用“for”而不是“foreach”吗?

何时使用 Parallel.ForEach,何时使用 PLINQ

ForEach 循环查看而不是图表数据

forEach和map和for方法的区别

教你如何使用零代码开发的Foreach循环功能代替for循环