Swift Closure 단점

John Jeonguk Hur·2023년 3월 27일
0

INDEX

  1. 가독성 저하
  2. 불필요한 복잡성
  3. 메모리 누수 가능성
  4. 클로저 재사용 어려움

1. 가독성이 떨어질 수 있음

클로저가 복잡해질수록 가독성이 떨어지는 경우가 있습니다. 특히 클로저가 여러 줄에 걸쳐 있는 경우나, 여러 개의 중첩된 클로저를 사용하는 경우 등은 코드를 이해하기 어려울 수 있습니다.

func performOperation(completion: (String) -> Void) {
    let result = someOperation()
    completion(result)
}

performOperation { result in
    print(result)
}

이 예시에서는 performOperation 함수 내에서 결과 값을 계산한 뒤, 클로저를 호출하여 결과 값을 반환합니다. 이 경우, 클로저를 호출할 때 전달되는 파라미터를 명시적으로 정의해야 하므로 코드가 다소 복잡해질 수 있습니다. 또한, 클로저를 전달하는 위치가 함수 호출의 매개 변수로 인해 가독성이 저하될 수 있습니다.


2. 불필요하게 복잡해질 수 있음

클로저가 지나치게 복잡하거나 중첩된 경우 코드가 불필요하게 복잡해질 수 있습니다. 이러한 경우 코드를 분리하거나, 함수나 메서드로 분리하는 것이 더 나은 선택일 수 있습니다.

func greet(name: String) -> String {
    return "Hello, \(name)!"
}

func greetWithDelay(name: String, delay: TimeInterval, completion: @escaping (String) -> Void) {
    DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
        let greeting = greet(name: name)
        completion(greeting)
    }
}

greetWithDelay(name: "John", delay: 2.0) { greeting in
    print(greeting)
}

위 코드는 greet 함수를 이용하여 name 파라미터를 받아 해당 이름을 포함한 문자열을 반환하는 함수입니다. 이 함수를 이용하여 greetWithDelay 함수를 구현했습니다. greetWithDelay 함수는 name 파라미터와 delay 파라미터를 받고, DispatchQueue의 asyncAfter 메서드를 이용하여 일정 시간 이후에 greet 함수를 호출하여 해당 이름을 포함한 문자열을 얻은 뒤, completion 클로저에 이 값을 전달합니다.

이후 greetWithDelay 함수를 호출할 때 name 파라미터와 delay 파라미터를 전달하고, 클로저를 이용하여 해당 함수가 종료된 이후 실행될 코드를 구현합니다. 위 코드에서는 간단하게 greeting 값을 출력하는 코드를 클로저 내부에 작성했습니다.


3. 메모리 누수 가능성

클로저가 참조하는 객체가 클로저보다 먼저 해제되지 않는 경우 메모리 누수가 발생할 수 있습니다. 이 경우 클로저에서 weak 또는 unowned 키워드를 사용하여 참조하는 객체를 약한 참조로 선언하여 문제를 해결할 수 있습니다.

func getRestaurant(completion: @escaping ([Restaurant]?, Error?) -> Void) {
    AF.request("https://myapi.com/restaurant").responseDecodable(of: [Restaurant].self) { response in
        switch response.result {
        case .success(let restaurants):
            completion(restaurants, nil)
        case .failure(let error):
            completion(nil, error)
        }
    }
}

이 코드는 completion handler를 사용하여 getRestaurant 함수의 결과를 반환합니다. 이 함수는 API에서 레스토랑 정보를 가져오는데 사용됩니다. API 요청이 성공하면 해당 레스토랑 배열을 반환하고, 실패하면 nil과 에러 객체를 반환합니다. 이렇게 하면 호출하는 쪽에서 에러 처리를 수행할 수 있습니다.


4. 클로저 재사용 어려움

클로저는 일회성으로 사용되는 경우가 많기 때문에, 같은 클로저를 여러 번 재사용하기 어려울 수 있습니다. 이 경우 클로저를 함수나 메서드로 분리하여 재사용성을 높이는 것이 좋습니다.

let completion = { (result: Result<String, Error>) in
    switch result {
    case .success(let message):
        print(message)
    case .failure(let error):
        print("Error: \(error.localizedDescription)")
    }
}

fetchData(from: url, completion: completion)

이 예시 코드에서는 completion closure 내부의 switch문이 상당히 길어져 코드 가독성이 저하됩니다. 또한, 이러한 closure를 다른 코드와 함께 사용하면 가독성이 더욱 저하될 수 있습니다.



결론

클로저는 매우 강력한 기능을 제공하지만, 그만큼 사용하는 방법과 주의할 점도 많습니다. 따라서 잘못 사용하면 코드 가독성과 유지보수성이 떨어질 수 있습니다.

클로저를 사용할 때에는 다음과 같은 주의점을 염두에 두는 것이 좋습니다.

1. 캡처링(capturing) 주의: 클로저에서 참조한 외부 객체는 캡처링되어 메모리에 계속 남아있을 수 있습니다. 그렇기 때문에 적절한 순간에 클로저를 해제해주는 것이 중요합니다.

2. 순환 참조 주의: 클로저와 외부 객체가 서로 참조할 경우, 메모리 누수가 발생할 수 있습니다. 이를 해결하기 위해서는 weak, unowned 키워드를 사용하여 약한 참조를 지정해줄 필요가 있습니다.

3. 클로저의 중첩 사용 주의: 클로저를 중첩하여 사용하면 코드의 가독성이 떨어질 수 있습니다. 따라서, 중첩 사용을 최소화하고 필요한 경우에만 사용하는 것이 좋습니다.

4. 클로저 파라미터 이름 주의: 클로저를 파라미터로 전달할 때, 클로저의 파라미터 이름을 명확하게 지정해주는 것이 가독성을 높이는 데 도움이 됩니다.

profile
Hi, I'm an iOS Developer. I will archive the minor details.

0개의 댓글