[swift] Swift PromiseKit에서의 메모리 관리 방법

PromiseKit은 Swift에서 비동기 작업을 처리하기 위한 라이브러리입니다. 비동기 작업은 메모리 관리에 영향을 미칠 수 있는데, 이 글에서는 Swift PromiseKit에서의 메모리 관리 방법에 대해 알아보겠습니다.

1. 적절한 사용 시점에서 retain cycle 방지

PromiseKit을 사용할 때는 주의해야 할 것이 있습니다. 비동기로 실행되는 클로저나 핸들러는 self를 강한 참조로 캡처할 수 있기 때문에 retain cycle이 발생할 수 있습니다. 이를 방지하기 위해서는 적절한 사용 시점에서 약한 참조를 사용해야 합니다.

class NetworkManager {
    func fetchData() -> Promise<Data> {
        return Promise { seal in
            URLSession.shared.dataTask(with: url) { [weak self] (data, response, error) in
                // 비동기 작업이 완료되면 호출되는 클로저
                guard let self = self else { return }
                // self를 약한 참조로 캡처하여 retain cycle 방지
                if let error = error {
                    seal.reject(error)
                } else if let data = data {
                    seal.fulfill(data)
                }
            }.resume()
        }
    }
}

위의 예제 코드에서는 fetchData() 메소드 내부에서 URLSession의 dataTask(with:completionHandler:) 메소드를 호출하고 있습니다. 클로저 내부에서 self를 약한 참조로 캡처함으로써 retain cycle을 방지해줍니다.

2. 메모리 누수가 발생하는 경우 조치

PromiseKit을 사용하다 보면 메모리 누수가 발생할 수 있는 경우가 있습니다. 그 중에는 콜백 함수에서 처리해야 하는 클로저 등에서 메모리 누수가 발생하는 경우가 있을 수 있습니다.

class ViewController: UIViewController {
    private var promise: Promise<Void>?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        fetchData()
            .done { [weak self] in
                self?.handleData(data)
            }
            .ensure { [weak self] in
                self?.cleanup()
            }
            .catch { [weak self] error in
                self?.showError(error)
            }
    }
    
    private func fetchData() -> Promise<Data> {
        return Promise { seal in
            URLSession.shared.dataTask(with: url) { [weak self] (data, response, error) in
                // 비동기 작업이 완료되면 호출되는 클로저
                guard let self = self else { return }
                if let error = error {
                    seal.reject(error)
                } else if let data = data {
                    seal.fulfill(data)
                }
            }.resume()
        }
    }
    
    private func handleData(_ data: Data) {
        // 데이터 처리 로직
    }
    
    private func cleanup() {
        // 데이터 정리 로직
    }
    
    private func showError(_ error: Error) {
        // 에러 처리 로직
    }
}

위의 예제에서는 UIViewController에서 fetchData() 메소드를 호출하고, 비동기 작업이 완료되면 각각의 콜백 함수에서 데이터를 처리하고 정리하거나 에러를 처리하도록 구현되어 있습니다. 주의해야 할 점은, 클로저 내부에서 [weak self]를 캡처해주어 메모리 누수를 방지해야 합니다.

3. Promise 사용 후 정리 작업 필수

PromiseKit을 사용할 때는 사용 후에 반드시 정리 작업을 해줘야 합니다. 이는 메모리 누수를 방지하고, 자원을 효율적으로 관리하기 위함입니다.

class NetworkManager {
    func fetchData() -> Promise<Data> {
        return Promise { seal in
            let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
                if let error = error {
                    seal.reject(error)
                } else if let data = data {
                    seal.fulfill(data)
                }
            }
            task.resume()
            seal.ensure {
                task.cancel()
            }
        }
    }
}

위의 예제에서는 fetchData() 메소드 내부에서 URLSession의 dataTask(with:completionHandler:) 메소드를 사용하는데, 작업이 완료된 후에는 task를 취소해줌으로써 자원을 효율적으로 관리합니다. 이렇게 Promise 사용 후에 필요한 정리 작업을 해주어야 합니다.

결론

Swift PromiseKit을 사용할 때는 메모리 관리에 유의해야 합니다. 적절한 사용 시점에서 retain cycle을 방지하고, 메모리 누수가 발생하는 경우에는 조치를 취해야 합니다. 또한, Promise 사용 후에는 정리 작업을 해주어 메모리를 효율적으로 관리해야 합니다.

참고 자료