-
[iOS, Swift] Callback 함수와 ClosureiOS/기본 원리 2023. 8. 30. 10:41반응형
Callback 함수는 특정 작업이나 함수의 완료 이후에 실행되도록 설계된 프로그래밍 패턴을 가리킵니다.
이러한 Callback 함수는 특히 비동기 작업에 많이 활용됩니다. 웹 요청 또는 파일 읽기와 같은 작업이 시작될 때, 이러한 함수에 callback을 전달하면 작업 완료 시 해당 callback 함수가 자동으로 호출되어, 결과를 처리하거나 발생한 오류를 관리합니다.
Swift 언어에서는 이러한 Callback 기능을 구현하기 위해 Closure를 활용합니다.
func fetchData(completion: (String) -> Void) { // 데이터를 비동기적으로 가져오는 가상의 코드 DispatchQueue.global().async { let data = "데이터" // 메인 스레드에서 완료 핸들러를 호출 DispatchQueue.main.async { completion(data) } } } fetchData { result in print("받아온 데이터: \(result)") }
이 코드 예제에서는 completion 매개변수에 클로저를 전달하여, 메인 스레드에서 해당 클로저가 실행 될 수 있도록 합니다.
순환 참조 문제
클로저는 주변의 변수나 상수를 자동으로 캡쳐할 수 있는 특성이 있습니다. 이 때, 클래스 인스턴스를 참조하게 되면 순환 참조가 발생할 위험이 있습니다. 이러한 순환 참조가 발생하면, 관련된 객체들이 메모리에서 올바르게 해제되지 않아 메모리 누수를 유발하게 됩니다.
class TaskManager { var taskName: String var completion: (() -> Void)? init(taskName: String) { self.taskName = taskName } func startTask() { // 클로저 내에서 self를 참조하면서 순환 참조 발생 completion = { print("Task \(self.taskName) is completed!") } } deinit { print("\(taskName) is being deinitialized") } } var myTask: TaskManager? = TaskManager(taskName: "Download") myTask?.startTask() // 이후에 객체에 nil을 할당하여 deinitializer를 호출하려고 시도 myTask = nil
위 코드 예제에서 myTask 객체를 nil을 할당하려고 해도 참조 순환 문제 때문에 deinit 함수를 호출하지 못합니다.
이러한 순환 참조를 피하기 위한 방법은 클로저 내에서 [weak self] 또는 [unowned self] 구문을 사용하는 것입니다. 이렇게 하면 클로저 내부에서 클래스 인스턴스에 대한 약한 참조나 비소유 참조를 생성하여 순환 참조 문제를 방지할 수 있습니다.
[weak self]: self는 옵셔널로 취급됩니다. 인스턴스가 메모리에서 해제되면 self는 nil로 설정됩니다.
[unowned self]: self는 옵셔널이 아닙니다. 인스턴스가 메모리에서 해제된 후에 self를 참조하면 런타임 오류가 발생합니다.
반응형'iOS > 기본 원리' 카테고리의 다른 글
[iOS, Swift] Optional 타입 (0) 2023.08.29 [Swift, 패턴] SOLID에 대한 개념 (0) 2023.08.28