프로그래밍iOS 개발자

스위프트의 lazy sequence란 무엇인가요? 어떻게 작동하며, 언제 사용하는 것이 좋고, 어떤 함정이 있을까요?

Hintsage AI 어시스턴트로 면접 통과

답변.

Lazy sequence는 컬렉션에 대한 특별한 래퍼로, 요소에 직접 접근할 때까지 계산 수행을 지연시킬 수 있게 해줍니다 (filter, map 등). 즉, lazy sequence에 대한 연산은 결과에 접근할 때만 계산됩니다 (예: .forEach, .first, 배열로 변환 시 등).

사용할 때:

  • 큰 시퀀스를 다룰 때, 모든 요소를 즉시 처리할 필요가 없는 경우에.
  • 연속적인 변환 중에 중간 할당을 피하고 싶을 때 (map -> filter -> ...).

예:

let numbers = Array(1...1_000_000) let lazyNumbers = numbers.lazy.map { $0 * 2 }.filter { $0 % 3 == 0 } let first = lazyNumbers.first // 첫 적합 요소에 대한 연산 체인이 이번에 계산됩니다!

함정:

  • .lazy sequence를 반복할 때마다 계산이 매번 새로 이루어집니다.
  • 호출 순서에 따라 다릅니다: 여러 번 데이터에 대해 다른 조건으로 반복해야 하는 경우 — 데이터가 다시 계산됩니다.
  • 지연된 작업은 내부 클로저에 부작용이 있는 경우 예기치 않은 부작용을 초래할 수 있습니다.

함정 질문.

배열에서 .map { ... }.lazy.map { ... }의 차이는 무엇인가요?

답변:

  • .map { ... }는 클로저를 각 요소에 적용하고 즉시 새로운 배열을 반환하므로 모든 요소가 처리되어 메모리에 저장됩니다.
  • .lazy.map { ... }는 배열이 아니라 lazy-sequence(래퍼)를 반환하며, 이는 요소에 접근할 때까지 처리를 즉시 수행하지 않습니다.

예:

let a = Array(1...10) let eagers = a.map { $0 * 2 } // 10개의 요소를 가진 배열 let laziers = a.lazy.map { $0 * 2 } // 결과가 즉시 포함되지 않는 LazySequence

주제에 대한 이해 부족으로 인한 실제 오류 사례.


이야기

큰 프로젝트에서 개발자는 .lazy 없이 거대한 데이터 배열에 map, filter, reduce의 여러 호출 체인을 적용했습니다. 이로 인해 각 중간 단계에서 큰 배열이 일시적으로 할당되어 메모리 소비가 거의 두 배로 증가했으며, 일부 낮은 RAM 장치에서 크래시를 유발했습니다.


이야기

코드 블록에서 내부 클로저에 부작용이 있는 lazy sequence가 사용되었습니다 (예: map/filter 내에서 이벤트 전송 또는 출력). 개발자는 이 작업이 즉시 수행될 것이라고 기대했지만, lazy sequence의 요소에 한 번도 접근하지 않아 이벤트가 전혀 발생하지 않았습니다. 결과적으로 로그와 메트릭이 부정확했습니다.


이야기

거대한 데이터베이스에서 데이터를 기반으로 통계를 수집할 때 여러 번의 반복과 함께 lazy sequence를 사용했습니다 (예: 두 번 first를 찾고, 그 다음 count를 계산했습니다). lazy sequence를 반복할 때마다 연산의 전체 재계산이 이루어져 두 배의 지연과 불필요한 시스템 부하를 초래했습니다. 일반 배열로 바꾸자 문제가 사라졌습니다.