프로그래밍iOS 개발자

Swift에서 클로저(closures)의 작동 원리에 대해 설명하십시오: 함수와의 차이점, 변수 캡처의 특성, 잘못된 사용 시 발생할 수 있는 문제.

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

답변

Swift에서 클로저(closures)는 주변 문맥의 변수와 상수에 대한 참조를 캡처하고 저장할 수 있는 독립적인 코드 블록입니다. 클로저는 콜백(callback), 비동기 처리 및 코드 실행을 변수 값으로 저장할 수 있게 해줍니다.

함수와의 차이점

  • 클로저는 외부 범위의 변수를 캡처할 수 있습니다.
  • 더 간결한 문법을 가지고 있습니다.
  • 값으로 전달될 수 있습니다.

클로저 예제:

var counter = 0 let incrementer: () -> Void = { counter += 1 } incrementer() print(counter) // 1

캡처의 특징

  • 기본적으로 변수는 strong으로(강력한 참조) 캡처됩니다.
  • 캡처 목록을 통해 캡처 규칙을 명시적으로 정의할 수 있습니다 ([weak self], [unowned self]).

문제들

  • 주요 문제는 클로저 내에서 self를 캡처할 때 발생할 수 있는 retain cycle입니다.
  • 다중 스레드 코드에서는 오래된 변수 값을 캡처할 수 있습니다.

함정 질문

strong-capture와 [weak self] 또는 [unowned self]를 캡처 목록에서 사용하는 것의 차이는 무엇입니까? 어떤 결과를 초래할 수 있습니까?

답변: [weak self] 또는 [unowned self]를 지정하지 않으면 클로저는 기본적으로 self를 강한 참조로 캡처하게 되어, 클로저와 self가 서로를 참조하게 되면 retain cycle이 발생할 수 있습니다. [weak self]는 self를 약한 참조(옵셔널)로 캡처하고, [unowned self]는 비컨트롤 가능한 참조(비옵셔널)로 캡처합니다. 캡처 목록의 잘못된 선택이나 부재는 메모리 누수 또는 크래시를 초래할 수 있습니다.

class Test { var closure: (() -> Void)? func setup() { closure = { [weak self] in self?.doWork() } } func doWork() { ... } }

주제에 대한 미숙지로 인한 실제 오류 사례


이야기

프로그래머가 UIViewController 내의 클로저에서 캡처 목록을 사용하지 않아 비동기 데이터 로드를 시작했습니다. 결과적으로 controller와 그의 view가 해제되지 않아 dismiss 후 메모리 누수가 발생했습니다. 인프라 분석 결과 수백 개의 "고립된" 메모리에서 컨트롤러가 발견되었습니다.


이야기

클로저가 [unowned self]를 사용하여 self를 캡처했지만, self의 생명 주기가 클로저 이전에 종료되었습니다. 이미 해제된 객체에 접근하려 할 때 크래시가 발생하여 클로저 내에서 self로 작업할 때 애플리케이션이 종료되었습니다.


이야기

프로젝트 내에서 중첩된 클로저가 두 변수를 캡처했는데, 하나는 강한 참조로, 다른 하나는 약한 참조로 캡처되었다. 약한 참조가 너무 일찍 nullified 되어 클로저 작업에 필요한 데이터가 손실되었습니다 — 이 버그는 다중 스레드 환경에서 스트레스 테스트 중에만 발생했습니다.