[swift/iOS] Escaping closure

2.so_j·2022년 6월 16일
post-thumbnail

@escaping이라는 키워드는 서버 통신시에 볼 수 있었을텐데요
대충 함수 반환 후에 사용한다 정도로 알던 것에서 더 나아가 정리해보려고 합니다

Escaping Closure

: 함수의 인자가 함수의 영역을 탈출하여 함수 밖에서 사용가능한 closure입니다

간단한 예시와 함께 설명해보자면

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        hello {
            print("hi 2022")
        }
    }

    func hello(closure: ()-> Void){
        closure()
    }
}

closure를 인자로 받는 hello 함수를 작성해봤는데요
예상대로 'hi 2022'가 출력됩니다

closure를 담는 sampleClosure 변수를 만들고
hello의 파라미터로 받은 클로저를 대입해주게 되는데요,

@escaping 클로저가 와야할 자리에 non-escaping 클로저를 할당해주었다는 에러를 마주치게 됩니다

이 상태에서 @escaping을 작성해주면 에러가 말끔히 사라지는데요
이게 대체 무슨 의미일까요 ?

기존에 알던 변수 scope 개념

  • 함수 내부에 전달된 클로저는 함수 내부 스코프에서만 사용이 가능함
  • 전달된 클로저는 직접 호출만 가능하고 외부 변수에 저장하지 못함

이것과 반대된다고 생각하면 되는데요,
📌 로컬 변수가 함수 밖에서도 유효하게 됩니다

escaping이 의미를 가질 때는 서버 통신에서 비동기 처리를 할 때 인데요
왜냐하면 함수의 실행 순서를 보장받을 수 있고,
A 함수가 마무리된 상태에서만 B 함수가 실행될 수 있도록 할 수 있기 때문입니다 !


서버 통신할 때 사용해보기

서버 통신은 비동기로 이루어지기 때문에 대뜸 서버 통신의 결과값을 사용하려고 하면
값이 비어있는 경우가 있었을텐데요,

서버 통신의 결과값을 담은 클로저를 맨 마지막에 실행시킬 수만 있다면 서버통신의 값을 성공적으로 사용할 수 있을것입니다 !

그러기 위해선 위에서 설명한 @escaping closure를 사용하면 된답니다 !
(야호 .. 라고 하기엔 async/await도 공부해야할듯? ㅠ)


실제 사용되는 함수를 냅다 들이밀기 전에 간단한 예시를 봐봅시다

위에서 사용했던 hello함수를 살짝 수정해보았는데요
물론 비동기 처리가 되는 과정은 아니라 적합한 예시인지는 모르겠으나

hello함수를 실행하게 되면 closurehello함수의 마지막에 실행되면서
print this!item에 담기게 됩니다

서버 통신 코드도 이 맥락과 크게 보면 비슷한데요,

// 게시글 전체 정보 가져오기
    func getPosts(completion: @escaping(NetworkResult<Any>) -> Void) {
        let url = APIConstants.getPostsURL
        let header: HTTPHeaders = ["Content-Type" : "application/json"]
        
        let dataRequest = AF.request(url, method: .get, encoding: URLEncoding.default, headers: header)
        
        dataRequest.responseData { response in
            switch response.result {
            case .success:
                guard let statusCode = response.response?.statusCode else { return }
                guard let value = response.value else { return }
                let networkResult = parseJSON(by: statusCode, data: value, type: PostData.self)
                completion(networkResult)
            case .failure:
                completion(.networkFail)
            }
        }
    }

게시글 정보를 가져오는 api를 사용해보려고 합니다
파라미터로 completion 클로저를 받고있는데요,

이 글에서 다루던 @escaping closure를 만나게 됩니다
getPosts함수가 모두 실행된 이후에 실행되는 클로저인데요
서버 통신에 성공할 경우 completion 클로저
서버 통신의 결과값을 전달해주게 됩니다

escaping closure가 아니었다면
함수의 실행 순서를 보장받지 못하니
서버 통신이 종료되지 않은 채 실행되어서 빈 값이 들어갈 수 있었겠죠 !

이제 사용할 일만 남았는데요

func getPosts() {
        PostService.shared.getPosts { response in
            switch response {
            case .success(let data):
                guard let data = data as? BaseResponse<PostData> else { return }
                guard let postData = data.data else { return }
                self.feedList = postData.posts
                self.popularProfileList = postData.hotProfiles
            case .requestErr(let data):
                print("request error")
                print(data)
            case .pathErr(let data):
                print("path error")
                print(data)
            default:
                print("DEBUG: Fail to get Posts.")
                return
            }
        }
    }

(여기에서 PostService.shared.getPosts 함수가
게시글 전체 정보 가져오기 주석에 달려있는 함수입니다)

completion 클로저로 전달해준 값은
response에 넣어주어 사용하고 있음을 볼 수 있습니다 🧐


Reference

https://i-colours-u.tistory.com/17
https://hcn1519.github.io/articles/2017-09/swift_escaping_closure

profile
싱글코어 두뇌의 개발자 도전기

0개의 댓글