ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • UIKit과 DispatchQueue.main의 관계 / 실제 예시를 통해 알아보기
    iOS/UIKit 2021. 12. 26. 23:41

    InfoView
    - shadowLayer
    - StackView
        - textLabel
        - tagLabel
        - descriptionLabel

    위와 같은 계층 구조를 갖고 있는 InfoView가 있다.

    초기 화면 구성시 동작 순서는 다음과 같다.

    1. API를 통해 받아온 데이터를 InfoView에 대입해준다.

    2. 데이터를 대입해줌에 따라서, StackView의 크기가 바뀔 것이다.

    3. InfoView의 bound에 맞춰서 shadow를 적용하는 updateShadow() 메서드가 실행된다.

    그러나 왼쪽 화면과 같이 bounds가 제대로 계산되지 않고 shadow가 적용된 것을 볼 수 있다.

     

    각 함수들에 `height`을 출력하는 프린트문을 넣어서 디버깅 해보면 결과는 아래와 같다.

    public func setInformation(_ beer: Beer) {
        self.nameLabel.text = beer.name
        self.taglineLabel.text = beer.tagline
        self.descriptionLabel.text = beer.beerDescription
        DispatchQueue.main.async {
          print(#function, "height : ", self.bounds.height)
        }
        self.updateShadow()
    }

    이 이유를 생각해보자.

    UIKit와 DispatchQueue

    Zedd님 블로그에서 보면 Cocoa Touch의 event들은 main thread에서 동작한다고 설명 해 놓으셨다.

    그래서 UIKit의 공식 문서를 찾아보니 아래와 같이 쓰여있었다.

    UIKit은 반드시 main thread에서 동작해야 한다.

    자, 그렇다면 위에서 shadow의 path가 제대로 계산되지 않은 이유를 생각해보자.

    모든 요소들에 디버깅 프린트문을 넣고 다시 실행해보았다.

    결과를 보면 이렇다.

    1에서 각 label에 text가 설정되었고,

    그 뒤에 2가 실행되지 않고 바로 3이 실행됐다.

    다음으로 label의 contraints가 다시 업데이트 되고나서 높이 또한 달라졌다

    모두 업데이트가 된 뒤에 비로소 `3`이 실행됐다.

    즉, UI를 설정해주는 UIKit 시스템함수인 `updateContraints()`는 DispatchQueue.main.async를 통해서 실행됐기에,

    mainQueue(serial)안에서는 Queue에 들어간 순서대로 실행이 되지만, Queue에 있지 않은 코드들과는 비동기적으로 수행되므로

    그 실행 순서를 장담할 수 없다.


    SerialQueue와 async 예시

    출처 : zedd님 블로그


    따라서 UI를 업데이트 하는 메소드인 `updateShadow()`는 mainQueue에서 실행돼야 한다.

    결과는 아래와 같이 잘 나오는 것을 확인할 수 있다.

    댓글

Designed by Tistory.