인프런 커뮤니티 질문&답변

정발산님의 프로필 이미지
정발산

작성한 질문수

앨런 iOS Concurrency(동시성) - 디스패치큐와 오퍼레이션큐의 이해

2) 디스패치 그룹의 사용 (비동기 디스패치 그룹)

completion closure안에 @escaping 질문입니다

작성

·

612

1

 escaping에 해당하는 closure가 작동하려면 runQueue와 completionQueue설정이 되어 있어야만 하나요?

답변 9

1

정발산님의 프로필 이미지
정발산
질문자

자세한 답변 정말 감사드립니다!

PHImageManager의 메서드들이 비동기적으로 구현되어 있다는 것은 메인쓰레드가 아닌 

다른 쓰레드 상에서 돌아간다는 뜻이고 비동기적 실행이 끝난후에 requestAVAsset의 resultHandler는 실행이 끝남을

알려주는 방식으로 이해하였습니다.

근데 제가 현재 진행중인 프로젝트가 있는데 requestAVAsset함수를 사용하면 이 함수가 다끝나기 전에

다음 문장으로 넘어가 버립니다.  (그래서 url을 얻지 못하고 return부분의 videoURL이 nil되어 오류가 발생합니다)

requestAVAsset함수의 completion 클로저는 escaping 클로저로써 비동기 실행의 끝남을 알려줘야 하고 값을 받아

videoURL 변수에 집어 넣어줘야 하는데 기다리지 못하고 바로 videoURL을 반환해 버리는 상황을 

해결하려면 비동기 실행을 기다려주는 코드를 삽입해야 하나요?

1

정발산님의 프로필 이미지
정발산
질문자

답변 정말 감사드립니다!

escaping 클로저에대해 한가지 더 질문드립니다.

4번째 문단에서 asyncAdd함수의 정의 내부에서 실행을 원하는 큐를 반드시 정해야 한다고 하셨는데

escaping 클로저를 기본적으로 파라미터로 받는 requestImage 함수나 requestAVAsset과 같은 이미지, 동영상을

가져오는 함수에 대해 비동기 처리를 꼭 해주어야 하나요? 

1

앨런(Allen)님의 프로필 이미지
앨런(Allen)
지식공유자

안녕하세요! 정발산 님

일단 반드시, runQueue나 completionQueue를 설정해야하는 것은 아닙니다. 해당 함수(asyncAdd)를 보다 다양한 방식으로 활용할 수 있도록, 외부에서 사용하려는 큐를 정해줄 수 있도록 만들기 위해서 다양한 가능성을 부여해준 방법일 뿐입니다.

파라미터로 runQueue를 정해놓으면, 실제 실행을 원하는 큐를 DispatchQueue.global로 할 수도 있고, 커스텀큐로도 만들 수 있습니다. 물론 completionQueue도 마찬가지의 이유로 파라미터로 받을 수 있도록 만든 것입니다.

만약, runQueue나 completionQueue를 정하지 않았다면, asyncAdd함수의 정의 내부에서 실행을 원하는 큐를 반드시 미리 정해야 할 것입니다. (그렇다면, 함수를 여러가지 큐에서 실행할 수 있는 가능성이 없어지겠지요 ^^;)

해당하는 내용을 더 잘 이해해 보실 수 있도록 제가 코드를 만들었는데,

아래에서 다운로드 받아서 플레이그라운드 파일을 보시면 됩니다. ^^

https://drive.google.com/file/d/1ulGgI1D50dOR5HkmbWw4LoebPpZN69rm/view?usp=sharing

다운 받아서 실행해보시면 좋을 것 같고, 혹시 코드를 보고 이해가 안되시면 다시 질문 남겨주시면 제가 해당부분 동영상을 찍어서 올려드리던지 하겠습니다. ^^

고맙습니다. :)

0

앨런(Allen)님의 프로필 이미지
앨런(Allen)
지식공유자

============================================================
escaping Handler가 다른 쓰레드에서 동작하는 비동기 함수 실행의 끝남을 알려 주는데 

그 끝남의 결과를 받으려면 어쨌든 비동기 함수를 기다려야 하는게 아닌가 하는 궁금증이 있습니다.

=============================================================

이부분에 대한 답변만 다시 드리면,

비동기 함수를 기다리는 방법이, 클로저를 실행하는 것입니다.

정확하게 말씀드리면, 기다린다기 보다는... 아무리 비동기적으로 함수를 사용하더라도 그 함수 내부는 순차적으로 실행(동기적)이 됩니다. 따라서.. 무엇인가를 리턴하는 것이 아닌 해당 비동기 함수 끝에 클로저를 꼬리표로 매달아서.. 그 클로저에서 진짜 함수가 끝났다는 표시를 하도록 만드는 방법입니다. ^^

해결이 안 되시면, we.love.code.allen@gmail.com 로 카톡 아이디를 보내주시면 카카오톡 통화로 알려드리도록 하겠습니다. ^^

0

앨런(Allen)님의 프로필 이미지
앨런(Allen)
지식공유자

saveImage( )함수에서 내부적으로 getVideos함수(비동기적)를 사용하고 계시니 saveImage() 함수도 String을 리턴하시면 안되고  saveImage함수도

func saveImage(completion: (String) -> Void) { ...내부 구현... } 이렇게 하셔야 합니다.

왜냐하면 제가 그림으로 표현하자면

지금 만드신 구조가 지금 위와 같은 그림식이라고 보시면 됩니다. String이 생성되기 전에 리턴하고 있습니다.....

제가 보았을때 비동기를 이해못하신게 아니고 클로저를 왜 사용하는지에 대한 이해를 먼저 하셔야 할 것 같습니다.

비동기적인 처리를 할때, 비동기적인 처리가 끝난 후의 함수(=클로저)를 실행하기 위한 구조가 컴플리션 클로저 입니다.
(아무리 비동기 처리라도 하더라도, 클로저 내부적으로는 동기적(순서적)으로 작동합니다.) 

다시 한번 시도해 보시고, 다시 질문 남겨주세요^^


근데 지금 제가 봤을땐 전체적으로 클로저로 물리고 물리는게.. 비효율적인 구조를 만든 것은 맞습니다....ㅠㅠ 설계를 다시 하시는게 좋을 것 같긴하지만 일단은 문제 해결을 하신 후에!

나중에 다시 고민해 보시는 걸로 ^^

0

정발산님의 프로필 이미지
정발산
질문자

답변 감사드립니다.

getVideos 함수에 return을 빼고 파라미터에 escaping closure(escapingHandler)를 추가 하였습니다.

후에 requestAVAsset함수의  escaping closure에 escapingHandler를 실행하였습니다.

문제는 getVideos함수의 requestAVAsset함수 자체를 들어가지 않고 함수를 끝내버립니다(resultHandler 클로저 안의 "In requestAVAsset"이 프린트 되지 않습니다). 

getVideos함수의 클로저가 String을 받지 못하고( "In escapingHandler"가 프린트 되지않습니다.) videoURL을 

리턴하여 nil error가 발생합니다. requestAVAsset함수 resultHandler 클로저 안 여러군데 escapingHandler를 위치해 보았

는데 다 똑같은 오류가 발생합니다. 이런식으로 escaping Handler처리를 하면 비동기 처리의 끝남을 캐치 할수 없는 건가요? 

-----------------------

개인적으로 궁금한 점이 있습니다! 

escaping Handler가 다른 쓰레드에서 동작하는 비동기 함수 실행의 끝남을 알려 주는데 

그 끝남의 결과를 받으려면 어쨌든 비동기 함수를 기다려야 하는게 아닌가 하는 궁금증이 있습니다.

(혹시 제가 비동기 함수 자체를 이해하지 못한 상황이라고 생각드시면 강의를 한번더 보라고 말씀부탁드립니다)

0

앨런(Allen)님의 프로필 이미지
앨런(Allen)
지식공유자

다시 답변드리면.. 비동기 실행을 기다리는 코드를 삽입하는게 아니고, 함수자체 설계에서 파라미터에서 클로저를 만들어서, 함수 내부에서 동작이 끝나고 클로저를 호출하도록 만들어야 합니다. ^^

0

앨런(Allen)님의 프로필 이미지
앨런(Allen)
지식공유자

일단은 위의 코드만 보면 함수 설계를 잘 못하셨어요.. resultHandler의 시점보다 String을 반환하는 시점이 빠릅니다..왜냐면 getVideos의 함수가 내부적으로 requestAVAsset메서드가 실행될때 비동기적으로 보내버립니다.

전부 다 코드를 알려드리는 것보다는 힌트를 드리자면,

만약에 저 함수를 꼭 쓰시고 싶다면 함수 반환을 String으로 하지 마시고 리턴없이, 파라미터에서 클로저를 받아야 합니다.

예를 들자면, func getVideos(asset:....... , completion: (String) -> Void ) { ...내부 만들기... }

이렇게요.. 한번 고민해 보시고.. 그래도 안 풀리면 다시 알려드리도록 할께요 ^^ 코드를 그냥 알려드리는 것보다 고민해보시는게 도움 될 것 같아서...그러는 점 양해 부탁드립니다. ^^

0

앨런(Allen)님의 프로필 이미지
앨런(Allen)
지식공유자

음.. 

제가 약간 정발산 님께서 해주신 질문의 포인트를 정확하게 못잡겠는데,
(클로저 내부를 어떻게 써야하나?, 아니면 requestImage 함수 같은 것들을 비동기적 처리를 해줘야 하나?, 아니면 꼭 비동기처리 같은 걸 해줘야 하나?)

제가 이해한 선에서 말씀드리면..

제가 만든 예제는 동기적인 함수비동기적으로 변환하는 방법에 대해서 보여드렸던 것이고,

일반적으로 네트워크 통신을 하는 URLSession과 같은 API 들은 기본적으로 내부에 비동기적으로 구현이 되어 있습니다. 예를 들어 URL Session 문서에서 캡처한 부분을 아래 보여드리면

문서에서 비동기적으로 구현(highly asynchronous)이 되어 있다고 말하고 있죠..

그래서 사실은 URLSession을 사용할때는 다시 비동기적 처리를 해줄 필요가 없습니다.(이미 내부적 처리를 하고 있으니까요.)


정확한 내용들은 사실 해당 API문서를 보시면 비동기적으로 구현되어있는지 찾아보시면 다 나와있는데, 일단 기본적으로 네트워크를 사용하거나 내장 파일을 로드하는 API들은 대부분 디폴트로 비동기적으로 구현이 되어있습니다. (수업에서 말씀드린 것처럼 이런  작업들은 오래 걸리는 작업들이고, 까딱하다간 화면이 버벅일 수 있는 가능성이 있으니 개발자들을 위해 이미 애플이 그런 부분을 고려한 것이겠죠.)

질문해주신 부분을 보니 PHImageManager의 메서드들을 비동기적으로 구현이 되어있네요.

이렇게 비동기적으로 이미 구현이 되어 있으니, 딸린 클로저 부분에서만 이미지, 동영상을 표시하거나 할때만 DispatchQueue.main으로 잘 보내주시면 될 것 같은데.. 답변이 되시려나요?

정발산님의 프로필 이미지
정발산

작성한 질문수

질문하기