iOS/Swift

[Swift] For-in Loop과 forEach Method의 차이점

Dev.Andy 2023. 8. 17. 23:59

머리말

for-in과 forEach는 그 생김새나 쓰임이 매우 비슷하다. 심지어 개발자 공식 문서에서 forEach를 설명할 때 for-in loop를 언급하면서 이를 정의한다.

forEach의 정의

forEach(_:)
Calls the given closure on each element in the sequence in the same order as a for-in loop.
주어진 클로저에 대하여, 시퀀스의 각 요소를 for-in 루프와 같은 순서로 호출한다

forEach(_:) | Apple Developer Documentation

그렇다면 두 개념의 차이는 무엇이고 실제 코드에서는 어떻게 다를까? 자세히 알아 보자.

차이점

둘에 대한 차이점은 바로 루프(loop; 반복문)와 클로저(closure)의 차이점이라 할 수 있다.

사실 위의 forEach에 대한 공식 문서 링크를 들어가면 그 차이점을 설명해 놓아서 이를 명확히 확인할 수 있는데, 이를 실제 코드에 적용하여 알아 보자.

(1) break 문이나 continue 문이 사용 가능한가?

You cannot use a break or continue statement to exit the current call of the body closure or skip subsequent calls.
클로저 안에서 break 문이나 continue 문을 통하여 현재 호출된 클로저로부터 탈출을 하거나 부가적인 호출을 생략할 수 없다.

쉽게 말하면 forEach 안의 클로저에서 break와 continue 키워드가 에러를 발생시킨다는 의미이다.

(a) for-in의 continue와 break → 사용 가능

아래처럼 for-in에서는 continue나 break가 잘 작동한다

import UIKit

let arr = Array(1...10)

for num in arr {
    if num <= 3 {
        continue
    }
    print(num, terminator: " ") // "4 5 6 7 8 9 10 " 출력
}

for num in arr {
    if num >= 3 {
        break
    }
    print(num, terminator: " ") // "1 2 " 출력
}

(b) forEach의 continue와 break → 사용 불가능

반면에 forEach에서는 continue나 break를 사용하려 할 경우, 에러를 발생시켜 실행되지 않는다.

import UIKit

let arr = Array(1...10)

arr.forEach { num in
    if num <= 3 {
        continue // 'continue' is only allowed inside a loop
    }
    print(num, terminator: " ") // 에러 발생으로 실행되지 않음
}

arr.forEach { num in
    if num <= 3 {
        break // Unlabeled 'break' is only allowed inside a loop or switch, a labeled break is required to exit an if or do
    }
    print(num, terminator: " ") // 에러 발생으로 실행되지 않음
}

forEach에서 continue나 break를 사용하려는 경우 에러가 발생하여 실행되지 않는다

(2) return 문이 외부 스코프나 부가적인 호출의 영향을 받는가?

Using the return statement in the body closure will exit only from the current call to body, not from any outer scope, and won't skip subsequent calls.
클로저 안에서 return 문을 사용하는 것은 현재에 대한 호출만을 탈출할 뿐, 외부 스코프으로부터 탈출할 수 없고, 부가적인 호출을 생략할 수 없다.

(a) for-in의 return → 영향 받음

아래처럼 특정 조건(num이 3 이하)을 만나서 return을 했을 경우, for-in 루프의 외부 스코프(forInFunction 함수)를 탈출한다.

func forInFunction() {
    let arr = Array(1...10)
    
    for num in arr {
        if num <= 3 {
            print(num, terminator: " ")
            return
        }
        print("PRINT", terminator: " ")
    }
}

forInFunction() // "1 " 출력

(b) forEach의 return → 영향 안 받음

반면에 forEach는 클로저이기에 return은 클로저에 대한 탈출을 할 뿐, 외부 스코프를 탈출하거나 부가적인 호출을 생략할 수 없다.

func forEachFunction() {
    let arr = Array(1...10)
    
    arr.forEach { num in
        if num <= 3 {
            print(num, terminator: " ")
            return
        }
        print("PRINT", terminator: " ")
    }
}

forEachFunction() // "1 2 3 PRINT PRINT PRINT PRINT PRINT PRINT PRINT " 출력

위의 코드에서 3 이하일 경우에는 return에 의해 클로저가 종료되어 PRINT를 출력하지 못했지만, 외부 스코프(forEachFunction 함수)를 탈출하지 못하고 나머지 조건(3 초과)일 때 PRINT를 출력한다. 만약 10번째 줄 아래에 부가적인 호출이 있었다면 이 역시 생략하지 못한 채 호출되었을 것이다.