나는 Claude에게 작업에서 Memory Leak을 만들라고 요청했지만 실패했다
Source: Dev.to
Background
지난 주에 나는 재미 삼아 Claude에게 Swift의 Task API에서 메모리 누수를 만들도록 요청했습니다. 생성된 코드는 Task가 self를 강하게 캡처해 retain cycle을 형성하고, [weak self]를 사용하라고 제안했습니다. 하지만 나는 뭔가 이상하다고 느꼈습니다—Task는 기본적으로 retain cycle을 만들지 않으며, 이것이 바로 설계상의 장점 중 하나이기 때문입니다.
Investigation
주장의 진위를 확인하기 위해 Swift 컴파일러의 중간 표현인 SIL (Swift Intermediate Language)을 살펴보았습니다. SIL은 코드가 머신 코드가 되기 전 컴파일러가 수행하는 작업을 정확히 보여주며, 일종의 “컴파일러 메모”와 같습니다.
Relevant SIL excerpt
%14 = function_ref @(extension in Swift):Swift.Task.init(priority: Swift.TaskPriority?, operation: __owned @isolated(any) () async -> A) -> Swift.Task : $@convention(method) (@in Optional, @sil_sending @owned @isolated(any) @async @callee_guaranteed @substituted () -> @out τ_0_0 for , @thin Task.Type) -> @owned Task // user: %15
%15 = apply %14(%3, %13, %2) : $@convention(method) (@in Optional, @sil_sending @owned @isolated(any) @async @callee_guaranteed @substituted () -> @out τ_0_0 for , @thin Task.Type) -> @owned Task // user: %17
dealloc_stack %3 : $*Optional // id: %16
release_value %15 : $Task // id: %17
%18 = tuple () // user: %19
return %18 : $() // id: %19
} // end sil function 'MemoryLeak.LeakyViewController.startLeakyTask() -> ()'
핵심 라인은 다음과 같습니다:
release_value %15 : $Task
이 명령은 Task 핸들이 생성 직후 바로 해제된다는 것을 보여줍니다:
- Task 생성
- Task 핸들 즉시 해제
- Task가 저장되지 않음 → 뷰 컨트롤러로부터 강한 참조가 없음
- retain cycle 없음 → 메모리 누수 없음
release_value 명령에 대한 자세한 내용은 Swift 문서에서 확인할 수 있습니다.
Conclusion
SIL 분석 결과 Task가 뷰 컨트롤러를 retain 하지 않으며, 뷰 컨트롤러의 deinit이 예상대로 호출됩니다. 메모리 누수는 존재하지 않습니다.
때때로 어떤 것이 어떻게 동작하는지를 이해하는 가장 좋은 방법은 가정에 의존하기보다 컴파일러가 실제로 수행하는 일을 직접 보는 것입니다. 특히 AI가 생성한 코드를 다룰 때는 “믿되, 검증하라”는 자세가 필요합니다.