본문 바로가기

iOS/iOS

iOS) 런 루프(RunLoop) 이해하기

 

 

 

안녕하세요 :) 소들입니다 

오늘은 RunLoop라는 것에대해 공부를 해볼 건데여

음... 내용이 좀 어려울 수도 있어여!! 

저도 오랜만에 다뤄서 완전히 이해하고 쓰는 내용이 아니라...

(한 1년 전에 공부하면서 정리했었는데 정리본이 다 날라감ㅜㅜ훠이훠이)

 

휴.. 뭐 포스팅 하면서 더 공부하고 그런 거 아니겠어여 ^^;

많이 공부하며 쓰지만 틀린 내용ㅇ ㅣ있을 수도 있으니

이 포스팅을 맹신하진 마시길......

모든 포스팅은 편의 말투로 합니다~!!

 

 

 

 

1. Global Thread에선 Timer가 동작하지 않는다구?

 

RunLoop라면서 갑자기 웬 Timer!!!!!!! 싶겠지만,

이 Timer가 RunLoop와 관련이 있으니까, 뭐 오프닝 같은 거라 생각해주셈 :))

 

자, 여러분 지금껏 타이머를 쓰면서

Global Thread에서 타이머를 돌려봤을 분이 몇 분이나 계실진 모르겠지만ㅎ..

 

다음과 같이 Main Thread에선 

 

 

 

 

3초 뒤에 잘 동작하는 Timer가,

Global Thread에서 동작시키려고 하면,

 

 

 

 

아무리 시간이 지나도 Timer가 동작하지 않음!!

 

왜지???에 대한

 그럼 실행시킬 수 있는 방법은???에 대한 을 찾기 위해서

오늘 RunLoop라는 것을 공부할 것임!!!!!!

 

오프닝 끝! 👀

 

 

 

2. RunLoop가 뭘까

 

먼저, RunLoop의 정의에 대해 알고 가보겠음

애플 문서에 나온 것을 토대로 정의 해보겠움!

 

RunLoop 객체는 소켓, 파일, 키보드 마우스 등의 입력 소스를 처리하는 이벤트 처리 루프로,

쓰레드가 일해야 할 때는 일하고, 일이 없으면 쉬도록 하는 목적으로 고안되었다

RunLoop 입장에서 Timer는 입력이 아닌 특수한 유형이지만, Timer의 이벤트 또한 처리한다

 

뭐..... 정의는 이런데, 좀 더 간단하게 설명하자면

RunLoop는 입력 소스를 처리하는 이벤트 처리 루프이고, Timer 또한 같이 처리한다는 것 같음

 

뭐.. 정의는 알았는데

그렇다면 이 RunLoop는 도대체 언제 어디서 어떻게 무엇에 쓰일꼬..? 👀

 

 

 

2-1. Thread와 RunLoop

 

자, 그럼 도대체 RunLoop가 어디에 사용되는지부터 보면,

 

Thread의 외부 입력 소스 및 Timer를 처리할 때 

 

사용됨

자, 여기서 중요한 핵심 단어.. 바로 Thread

그럼 모든 Thread는 각자의 RunLoop를 가질 수 있냐?? 맞음!!

그럼 Thread를 생성할 때 RunLoop도 자동으로 같이 생성 되나??  고건 아님!!

애플 문서에 보면

 

 

current 메서드를 사용할 때, 현재 쓰레드의 런루프가 없으면 생성된 후 반환된다는 걸로 보아

쓰레드가 생길 때 바로 같이 생성되는 건 아니고, 바로 다음에 공부할 내용인데

프로그래머가 해당 쓰레드에서 RunLoop의 current 메서드를 통해 런루프에 접근할 때

해당 쓰레드에 대한 런루프가 없으면, 이때 생성되는 것 같음!

 

아 오키오키~ current 메서드를 통해 런루프 얻었어~

그럼 RunLoop는 생성되고 나면 자동으로 실행 되나?? 이또한 아님!!

쓰레드가 각자 자기만의 런루프를 얻었다고 한들, 자동으로 실행되진 않음

 

RunLoop 실행에 대한 관리는 오롯이 "프로그래머"의 몫

(Main RunLoop는 예외인데 좀이따 설명 하겠음)

 

 

따라서 만약 내가 Thread를 생성 했는데,

이 Thread가 입력 소스나 Timer를 처리해야 한다면, RunLoop를 직접 얻어서 실행시켜 주어야 함

이때, 내가 생성한 Threadㅇㅔ 대한 RunLoop를 얻으려면,

다음 메서드를 이용함

 

 

 

 

그럼 이 친구를 어떻게 사용 하냐면!!

현재 실행 중인 쓰레드 코드 내에서 다음과 같이 작성하면

 

 

let runLoop = RunLoop.current

 

 

현재 Thread에 대한 RunLoop를 얻을 수 있음!!!

아까 위에서 말했듯 만약 해당 쓰레드에 대한 런루프가 이미 있을 경우

그 런루프를 가져오고, 런루프가 없다면 런루프를 새로 생성해서 반환함!

 

근데!! 계속 말하듯이

내가 RunLoop를 얻는 것만으로는, 입력 소스 및 타이머를 처리해주진 않음

 

run이라는 것통해 RunLoop를 직접 실행 시켜주어야 하는데,

이것에 대해 알기 위해선 RunLoop가 어떻게 작동하는지부터 알아보잣:)

 

 

 

 

3. RunLoop의 작동 원리

 

RunLoop는 루프를 수행할 때,  2가지 Event Source를 수신함

 

 Input Source 

 

다른 Thread나 Application으로부터 온 비동기 이벤트를 전달한다

 

 Timer 

 

예약 된 시간 또는 반복 간격으로 발생하는 동기 이벤트를 전달한다

 

 

 

 

왼쪽 그림에서 노란 색 루프를 한바퀴 도는 작업이 한 번의 실행이라고 생각 했을 때,

RunLoop는 한 번의 실행 동안 내 쓰레드에 도착한 이벤트를 받고, 이에 대한 핸들러를 수행하는 객체

 

루프라고 해서,

RunLoop가 자체적으로 계속 이벤트가 들어오나 안들어오나 실행을 반복 한다고 생각할 수 있겠지만,

RunLoop는 내부적으로 반복 실행을 하지 않음;; 

한 번 Event Source를 읽고 전달하는 실행이 끝나면, 그대로 대기해버림;;

그 다음 Event Source가 들어와도 RunLoop는 대기 상태이기 때문에, Event를 받을 수 없는 것임

(근데 생각해보니가 Thread가 일이 없을 때 쉬게 하기 위해서니까 무한으로 돌리지 않는 게 맞을 수도..?)

 

따라서, 쓰레드 내에서 프로그래머가 명시적으로

for, while 등을 이용해 RunLoop를 반복 실행시켜 주어야 함

 

그럼 도대체 RunLoop를 어떻게 실행 시키는데?

 

 

 

 

4. RunLoop를 실행시키는 방법

 

자.. 여기까지 보면, 아까 Global Thread에서 왜 Timer가 실행되지 않은지에 대해

원인을 찾을 수 있음

 

우리는 Global Thread를 손수 생성 했고,

내가 생성한 Global Thread의 RunLoop는 실행되고 있지 않기 때문

Timer를 작동 시켰지만, 내 Thread의 RunLoop가 이 Event를 처리하지 못해서

그래서 실행이 안 됐던 것

 

그럼 실행 시키는 방법을 알기 전에,

한 가지 의문이 들어야 정상임

 

Main Thread도 내가 RunLoop 실행시켜준 적 없는데,

왜 Timer가 정상 동작 하는 거임?

 

이거에 대한 답은,

 

 Main Thread는 애플리케이션이 실행될 때 프레임워크 차원에서 자동으로 RunLoop를 설정하고 실행 

 

하기 때문임.. 이를 Main RunLoop라고 하는데, 때문에

우리가 Input Source, Timer Event를 아무런 RunLoop를 설정하지 않아도

Main Thread에서는 실행할 수 있던 것임

 

이제 왜 내가만든 Global Thread에서만 Timer가 실행되지 않은지 이유가 명확해졌음

(Timer로 국한 했지만, Socket Input, 마우스 입력 등 모든 Input Source도 마찬가지로 실행되지 않음)

 

자, 그럼 Global Thread RunLoop를 실행시키는 방법에 대해 알아보겠음

 

 

 

 

Loop를 Running 시키는 방법엔 4가지 메서드가 있는데,

4가지 모두 다뤄보겠음

 

 

 

4-1. run()

 

 

receiver를 영구 루프에 넣고, 이 기간 동안 모든 부착된 Input Source의 데이터를 처리한다

 

receiver를 parmanent loop에 넣는대;; parmanent loop가 먼데...;;

 

내가 공부하며 생각한 바로는,...

run()을 실행할 당시 부착되어있는 Input Source나 Timer는 영구적으로 처리

한다는 말 같음...

 

왜냐면 테스트 할 때

 

run()을 실행시키기 전에 Thread에 Timer1을 부착하고,

run()을 실행시킨 후에 Thread에 Timer2를 부착하고 테스트 해봤는데

 

 

 

 

run()을 실행시키기 전에 부착한 Timer1제대로 실행되고,

run()을 실행 이후에 부착한 Timer2실행되지 않음....

 

만약 내가 이해한 게 아니라면 아시는 분들 댓글좀 ㅠ

쨌든 이렇게 run을 시켜주면 Global Thread에서 Timer가 작동하긴 한다....

 

 

 

4-2. run(mode:before:)

 

 

 

루프를 한 번 실행하여, 지정된 모드에서 지정된 날짜까지 input을 blocking 한다

 

한국말 읽고 있는거 맞겠지 나

이거는 RunLoop의 Mode에 대해서 알아야 되는데.. 

이번 포스팅에선 RunLoop의 기본만 다뤄서, Mode는 다음에 다룰거라

다음에 RunLoop심화편 포스팅 할 때 추가로 다루겠음!!!

 

이렇게 넘어가도 되는 이유는,

실제 RunLoop를 실행시킬 땐 보통 다음에 나오는 run(until:) 메서드만 쓰기 때문임

 

 

 

4-3. run(until:)

 

 

 

지정된 날짜까지 루프를 실행하고, 그 기간 동안 루프는 부착된 모든 Input Source들의 데이터를 처리한다

 

보통, RunLoop를 반복 실행할 때 이 메서드를 사용함!!!

이전에 부착된 Input Source들만 영구적으로 실행되는 run()과 달리,

이 메서드는 지정된 날짜까지 루프를 실행하기 때문에, 직접 루프의 실행 시간을 지정해줄 수 있음

그리고 그 실행 시간 동안 부착된, 모든 Input Source를 처리해줌

 

 보통 0.1 ~ 1초 정도 RunLoop를 실행시키고,

이를 원할 때까지 for, while 문으로 반복하는 형태로 작성함

 

다음과 같이 :)

 

 

while isRunning {
    runLoop.run(until: Date().addingTimeInterval(0.1))
}

 

 

만약, 더이상 RunLoop가 필요 없어지면 

어디선가 isRunning을 false로 해주면 RunLoop가 더이상 작동하지 않음

 

 

 

 

짜잔~~ 

 

근데, 계속 반복문을 돌려주어야 한단 점에서,

Sync하게 작동할 시 저 while문 이후의 작업은 isRunning이 false가 될 때까지 아무것도 못하잖음???

따라서 Socket 통신할 때 나는 다음과 같이 Running 작업 자체를 또 다른 Thread로 뺐ㅆ는데..

이거 완전 Swift 초보 때 짠 거라, 이게 맞는 방법인닌 잘 모르겠음..;;

 

 

 

 

4-4. acceptInput(fotMode:before:)

 

 

 

지정된 날짜까지 또는 지정된 모드에 대해서만 입력을 허용하여 루프를 한 번 실행한다

 

 

또 modeㄷr.... 

이거도 2번 메서드랑 같이 심화편 쓰면 추가하겠음

(어차피 거의 until로 사용하니까..)

 

 

쨌든, 이런 방식으로 RunLoop를 실행시킬 수 이따 :)

 

 

 

 

5. 언제 RunLoop를 사용할까

 

RunLoop를 직접 사용하는 경우는,

Thread를 직접 만들어서 다음과 같은 작업을 할 때임

 

- Input Source를 통해 다른 Thread와 통신하는 경우

- Timer를 사용해야 하는 경우

- Perform Selector Source를 사용해야 하는 경우

- 주기적인 일을 계속 수행해야 하는 경우

 

머 이럴 때 빼곤 RunLoop 굳이 안 써도 됩니다 ㅎㅎ;

 

 

 

 

 

 

.

.

.

하........................

몇시간째지............?......... ㅏㅎ하하하하하하ㅏ핳

오늘은 RunLoop의 기본 개념과 간단한 사용법에 대ㅐㅎ 알아봤는데

이 외ㅣ에도

 

RunLoop Observer나 RunLoop Mode 나

RunLoop가 Thread-Safe 하지 않다는 내용이나

더 공부할 게 많지만, 일단 여기서 끊고 가볼게요!!!!!!!

 

그리고 RunLoop를 이해시키기 위해 Global Thread에서 Timer를 돌렸지만,

실제로 Timer는 Main Thread에서 돌리는 게 안전하다 합니다ㅠㅠ

 

심화편을 쓸 수 있는 날이 빨리 오길 바라며 ㅠㅠㅠㅠ

피드백 대환영



Calendar
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
최근 댓글
Visits
Today
Yesterday