什么时候使用forEach(_:)而不是for in?

问题描述 投票:0回答:4

ArrayDictionary

forEach(_:)
实例方法中所述:

以相同的方式调用sequence中每个元素的给定闭包 作为 for-in 循环进行排序。

尽管如此,改编自序列概述

序列是一个值列表,您可以一次逐步浏览其中一个值 时间。 迭代序列元素的最常见方法 是使用 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
循环有什么好处?从性能角度来看它们会相同吗?

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

swift loops foreach sequence for-in-loop
4个回答
106
投票

forEach
不提供性能优势。事实上,如果你查看源代码
forEach
函数实际上只是执行
for
-
in
。对于发布版本,与简单地使用
for
-
in
相比,此函数的性能开销并不重要,但对于调试版本,它会导致可忽略(但可观察)的性能影响。

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

let objects = array.map { … }
    .filter { … }

for object in objects {
    …
}

您可以继续使用函数式编程模式:

array.map { … }
    .filter { … }
    .forEach { … }

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

FWIW,ArrayDictionarySequence的文档都提醒我们

forEach
引入的限制,即:

  1. 您不能使用

    break
    continue
    语句退出当前 调用
    body
    闭包或跳过后续调用。

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


25
投票

我最近遇到一个用例,其中使用

forEach
for in
更可取。假设您要从图层中删除所有子图层。像下面这样的语句不起作用,因为您需要打开
[CALayer]

for layer in self.videoContainerView.layer.sublayers!

如果子层为零,你将会崩溃。这迫使您首先检查是否存在子层。然而,

forEach
使这变得更加简单,如下所示:

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

20
投票

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

  1. break
    /
    continue
    仅适用于
    for .. in
  2. return
    中的
    forEach
    将退出闭包,但不会停止迭代。

这样做的原因是

for .. in
是语言中的特殊形式(它允许中断并继续按照你的期望工作)。这是你无法使用语言本身以相同的方式实现的。

但是,

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

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

3
投票

除了上述答案之外,区分

for
循环与
forEach
的另一个原因是,使用
for
循环,我们还可以选择使用基于
where
的模式匹配来实现该逻辑,例如

for adBanner in adBanners where !adBanner.isLoading {

上述类型的控制流相关功能使得 for 循环如此强大,但如果我们不需要这种级别的控制,使用对 forEach 的调用可能会给我们带来稍微简单一些的代码。

简而言之,使用

for
循环可以让我们更好地控制迭代

© www.soinside.com 2019 - 2024. All rights reserved.