#1

func downloadJson(_ url:String, _ completion: @escaping (String?) -> Void ) {
        DispatchQueue.global().async {
            let url = URL(string: MEMBER_LIST_URL)!
            let data = try! Data(contentsOf: url)
            let json = String(data: data, encoding: .utf8)
            DispatchQueue.main.async {
                completion(json)
            }
        }
        
    }

비동기부분을 처리하는 부분만 함수로 정의하여 따로 처리해준다

나머지 메인쓰레드 부분에서는 동기로 처리하면 되기 때문에 디스패치큐를 사용할 필요가 없다.

@escaping 에 대해서

해당 클로저를 어떤 구문 밖으로 탈출 시켜서 사용하겠다는 뜻입니다.

// @escaping 선언이 없다면 구문 밖에서 사용이 불가능하기 때문에, 배열에 할당이 불가능합니다.var completionHandlers: [() -> Void] = []

func withEscaping(completion: @escaping () -> Void) {
    completionHandlers.append(completion)
}

보시면 받아온 completion을 함수 구문 밖인 배열에 넣는 것을 알 수 있죠

이를 통해, 나중에 언제든 배열에서 꺼내어서 사용하겠다는 것입니다.

즉, 함수 구문 밖으로 탈출을 한 것이죠.

그렇다면 한 번 더 명시적인 예제를 만들어서 볼게요.

class Myclass {
    var x = 10

    func callFunc() {
        withEscaping { self.x = 100 }
        withoutEscaping { x = 200 }
    }

    var completionHandlers: [() -> Void] = []

    func withEscaping(completion: @escaping () -> Void) {
        completionHandler.append(completion)
    }

    func withoutEscaping(completion: () -> Void) {
        completion()
    }
}

이렇게 생긴 MyClass라는 클래스가 있죠.

그럼 한 번 메소드를 실행시켜보고 결과가 어떻게 나오는지 볼게요.

let mc = MyClass()

mc.callFunc()
print(mc.x)// 200

mc.completionHandlers.first?()
print(mc.x)// 100

callFunc()을 호출하게 되면 먼저 withoutEscaping 메소드가 클로저를 바로 호출하기때문에, x의 값이 200으로 바뀌는 것을 확인 할 수 있어요.

이후 withEscaping에서는 클로저를 바로 호출하는 것이 아니라 completionHandlers 배열에 저장을 하게 되죠