iOS

iOS ) 왜 main.sync를 하면 안될까

Zedd0202 2018. 4. 29. 13:03
반응형

안녕하세요 :) Zedd입니다.

오늘은 왜!!! 왜 main.sync를 하면 안되는지 자세히 공부해볼게요.

그렇다고 main.sync를 무조건 하면 안된다는것도 아닙니다.

요 부분은 밑에서 다시 볼게요.

 

main.sync를 하게 되면

이런 에러를 뿜게 되는데요, 이게 왜 나는건지!!!! 알아봅시다.

main.sync가 왜 에러가 난다!!를 말하기 전에, 알아야 할 것이 있습니다.

 

대부분 GCD 글을 보게 되면, UI업데이트는 반드시 main에서 해야한다고 말하는것을 볼 수 있어요!

그 이유부터 봅시다. 이유는 들어보면 간단한데요,

Cocoa Touch 앱에서 UIApplication의 인스턴스가 main thread에 붙기(attach) 때문.
UIApplication은 앱을 시작할 때 인스턴스화 되는 앱의 첫번째 부분인데,
얘는 앱의 run loop를 포함하여 main event loop를 설정하고 event처리를 시작합니다.
앱의 main event loop는 touch, gesture등의 모든 UI event를 수신합니다. 

앱의 UI event는 일반적으로 UIApplication -> UIWindow -> UIViewController -> UIView -> subviews (UIButton 등)와 같이 chain으로 연결되는데, 이 responder chain을 따라 UIResponder로 전달됩니다. 

Responder는 버튼 누르기, 탭, pinch, 확대/축소, 스와이프 등의 이벤트를 UI 변경사항으로 처리합니다. 따라서 이러한 일련의 event chain이 UIKit의 main thread에서 작동하는 이유입니다. 

따지고 보면, label이나 View는 UIApplication의 자손? 자식이므로, 모든 이런 UI작업은 main thread의 “일부”라고 할 수 있어요. 그래서 모든 이벤트는!!! main thread의 일부가 되며, main thread에서 처리해야합니다. 

 

UIKit문서에 가보면,

대부분의 경우, UIKit 클래스는 앱의 main thread에서만 사용해야 합니다. 이는 UIResponder에서 파생되거나 앱의 사용자 인터페이스를 조작하는 것과 관련하여 특히 그렇습니다.

 

구구절절 설명했지만, UI(user interface)와 관련된 모든 event가 main thread에 붙기(attach)때문에, 반드시 main에서 해야한다는 것입니다.<Dispatch Queue>글에서도 설명했지만, dispatch queue에서 제공해주는 main은 

Main dispatch queue : main dispatch queue는 앱의 main 쓰레드에서 task를 실행하는, 전역적으로 사용 가능한 serial queue입니다. 이 큐는 앱의 run loop와 함께 작동하여 큐에 있는 task의 실행을 run loop에 연결된 다른 이벤트 소스의 실행과 얽힙니다. 앱의 main thread에서 실행되므로, main queue는 종종 앱의 주요 동기화 지점으로 사용됩니다. main dispatch queue를 만들 필요는 없지만, 앱이 적절하게 배수(drains)되도록 해야합니다. 

그렇습니다. 그렇기 때문에 우리가 UI와 관련된 업데이트를 치고싶으면 바로 이 main에서 해야하는이유죠. 

 

그러면, 왜!!!! 왜 main.sync를 하면 안되는걸까요. 

 

일단 main queue는 Serial queue입니다. 한 task가 끝나야 (큐에 있는) 다음 task를 실행하는것이죠.

그리고 sync는 이 큐에 있는 작업이 끝날 때 까지 그 코드에 머물러 있죠. 즉, 큐를 block하고 우리가 큐에 넣은 작업들이 완료될때까지 wait상태가 되는 것이죠. 

 

만약 main queue에서 sync를 호출하게 되면, 일단 큐를 block하고 우리가 넣은 task가 끝날때까지 기다리겠네요. 하지만!!!!!!!!!!!!!!!!!!!!! 큐는 이미 block상태가 되었기 때문에 task는 시작조차 할 수 없습니다. 이러한 상태를 deadlock이라고 하는데요, 무한정 기다리는 상태가 되어 앱이 죽게되고맙니다. 

 

그럼 이러한 궁금증이 들 수 있겠죠. 

아 ㅇㅇㅋㅇㅋ;; UI를 main에서 업데이트 해야한다는건 알겠음ㅎㅎ;근데 왜 하필 main.sync가 안되냐고 ㅎㅎ;

let queue = DispatchQueue(label: "zedd")

queue.sync {

    for index in 1...5 {
        print("hello, zedd \(index)")
    }
}

for i in 100...105 {
    print(i)
}

내가 임의로 Serial queue만들면 잘 되는 부분;;

네 그렇습니다. 잘 되죠. 

왜 하필 main.sync만 안되냐??????????????????? 

그것은 바로....확실하진 않지만...main thread가 thead-safe하지 않기 때문입니다.

 objc.io에서 

 


UIKit을 thread부터 보호하지 않는 것은 Apple 측의 의식적인 디자인 결정입니다.(일부러 그렇게 만들었다는 뜻) thread로부터 안전하게 만들면 성능면에서별로 도움이되지 않습니다. 실제로 많은 일이 느려질 것입니다. 

그리고 UIKit이 main thread에 묶여 있다는 사실은, 

concurrent 프로그램을 작성하고 UIKit을 사용하는 것을 매우 쉽게 만듭니다. 

당신이해야 할 일은 항상 UIKit에 대한 호출이 항상 main thread에서 이루어 지도록하는 것입니다.

따라서 UIKit 객체가 main thread에서 액세스되어야한다는 사실은,

 성능에 유리하도록 한 apple의 디자인 결정입니다 


 

 

라고 해서...일단 진짜 그런가 했더니

 

<Thread Safety Summary>에서 이렇게 말합니다.

There is a misconception that the Foundation framework is thread-safe and the Application Kit framework is not. Unfortunately, this is a gross generalization and somewhat misleading. Each framework has areas that are thread-safe and areas that are not thread-safe. 

 
Foundation프레임워크는 쓰레드로부터 안전하고, Application Kit프레임워크(AppKit인데, Cocoa touch에서 UIKit과 동일합니다. UIKit으로 읽으셔도 무방)는 그렇지 않다는 오해가 있습니다. 불행히도, 이것은 총체적인 일반화이며, 오해의 소지가 있습니다. 각 프레임워크에는 쓰레드로부터 안전한 영역과 쓰레드로부터 안전하지 않은 영역이 있습니다. 
 
그리고 iOS 4.0(ㄷㄷ) release note에 보면
 

Drawing to a graphics context in UIKit is now thread-safe. Specifically:

 

  • The routines used to access and manipulate the graphics context can now correctly handle contexts residing on different threads.
  • String and image drawing is now thread-safe.
  • Using color and font objects in multiple threads is now safe to do.
 
 

UIKit에 있는 모든 것?이 thread로 부터 unsafe하지 않다! 라는 것을 말해주네요. 그러니까 모든 UIKit과 관련된 작업들은 thread-safe하지 않다! 라고 말하면 안되...겠죠?

 

아무튼...음...main thread가 thread-unsafe하니까 deadlock이 나는 것 같은데...확실하진 않아서 ㅎㅎ 엄청 찾아봐도 main thread는 unsafe한 thread란다^^;...딱 이렇게 말하는 문서가 안나오네요;;만약 틀렸다면 알려주세요!!!!!!!!!!!!!!!!!!꼭!!!!!!!!!!!

 

<Threading Programming Guide>에서 

Immutable object는 일반적으로 thread로 부터 안전하고, mutable object는 thread로 부터 안전하지 않다고 하는데 (당연한 말이겠죠? 변경이 불가능 하면 여러 thread에서 접근해도 상관없으니깐..) 그럼 main thread가 mutable인지... Immutable인지 모르겠네요. 찾아봐도 안나오고..main thread가 변경이 가능한가...??......ㅎㅎ.................어케 변경하지.... Immutable아닌가...음..잘 모르겠네요. 알려주시면 감사하겠..

 

+ 그렇다고 main.sync를 무조건 하면 안된다는것도 아닙니다.

 

main.sync를 백그라운드 스레드에서 특별한 순서에 맞게 UI가 업데이트 되어야 할 때 사용할 수 있습니다.

DispatchQueue.global().async {
    print("before textLabel update" )
    
    DispatchQueue.main.sync {
        print("Test1")
    }
    
    print("after textLabel update Test1")
    
    DispatchQueue.main.sync {
        print("Test2")
    }
    
    print("after textLabel update Test2" )
}

// before textLabel update
// Test1
// after textLabel update Test1
// Test2
// after textLabel update Test2



도움이 되었길 바랍니다 :)

 

 

반응형