iOS RunLoop(1)

4672 단어
RunLoop의 개념은 일반적으로 한 라인은 한 번에 한 작업만 수행할 수 있고 실행이 완료되면 라인이 종료된다.만약 우리가 스레드가 수시로 사건을 처리할 수 있지만 종료되지 않도록 하는 메커니즘이 필요하다면 일반적인 코드 논리는 다음과 같다.
function loop() {
    initialize();
    do {
        var message = get_next_message();
        process_message(message);
    } while (message != quit);
}

일반적으로 이 모델을 Event Loop이라고 합니다.Event Loop은 많은 시스템과 프레임워크에서 이루어진다. 예를 들어 Node.js의 이벤트 처리, 예를 들어 Windows 프로그램의 메시지 순환, 그리고 OSX/iOS의 RunLoop.이러한 모델을 실현하는 관건은 이벤트/메시지를 어떻게 관리하는가, 정보를 처리하지 않을 때 자원 점용을 피하고 메시지가 왔을 때 즉시 깨어나게 하는가이다.
따라서 RunLoop은 사실상 하나의 대상이다. 이 대상은 처리해야 할 이벤트와 메시지를 관리하고 입구 함수를 제공하여 위의 Event Loop의 논리를 집행한다.루틴이 이 함수를 실행하면 이 함수 내부의 '메시지 수락 -> 대기 -> 처리' 순환에서 이 순환이 끝날 때까지 (예:quit에 전송된 메시지) 함수가 되돌아옵니다.OSX/iOS 시스템에는 NSRunLoop과 CFRunLoopRef라는 두 개의 객체가 있습니다.CFRunLoopRef는CoreFoundation 프레임워크 내에 있으며 순수 C 함수의 API를 제공합니다. 모든 API는 라인이 안전합니다.
RunLoop과 스레드의 관계는 우선 iOS 개발에서 두 개의 스레드 대상을 만날 수 있다. pthreadt 및 NSThread과거 애플은 NSThread가 pthread일 뿐이라는 문서를 표시했다.t의 봉인이지만, 그 문서는 이미 효력을 상실했습니다. 현재 그것들도 모두 맨 밑에 있는mach thread에서 직접 포장할 수 있습니다.애플은 이 두 대상이 서로 전환하는 인터페이스를 제공하지 않았지만, 어쨌든 pthreadt와 NSThread는 일대일로 대응한다.예를 들면, 당신은 pthread 를 통과할 수 있습니다.main_np() 또는 [NSThread mainThread]에서 마스터 스레드를 가져옵니다.pthread를 통해서도현재 스레드를 가져오려면 self () 또는 [NSThread currentThread] 를 사용하십시오.CFRunLoop은 pthread를 기반으로 관리됩니다.
애플은 RunLoop을 직접 만들 수 없습니다. 이것은 자동으로 가져오는 두 가지 함수만 제공합니다. 그것이 바로 CFRunLoopGetMain () 과 CFRunLoopGetCurrent () 입니다.이 두 함수 내부의 논리는 대략 다음과 같다.
///    Dictionary,key   pthread_t, value   CFRunLoopRef
static CFMutableDictionaryRef loopsDic;
///    loopsDic    
static CFSpinLock_t loopsLock;
  
///      pthread     RunLoop。
CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
    OSSpinLockLock(&loopsLock);
     
    if (!loopsDic) {
        //       ,     Dic,           RunLoop。
        loopsDic = CFDictionaryCreateMutable();
        CFRunLoopRef mainLoop = _CFRunLoopCreate();
        CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
    }
     
    ///     Dictionary    。
    CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));
     
    if (!loop) {
        ///     ,    
        loop = _CFRunLoopCreate();
        CFDictionarySetValue(loopsDic, thread, loop);
        ///       ,      ,          RunLoop。
        _CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
    }
     
    OSSpinLockUnLock(&loopsLock);
    return loop;
}
  
CFRunLoopRef CFRunLoopGetMain() {
    return _CFRunLoopGet(pthread_main_thread_np());
}
  
CFRunLoopRef CFRunLoopGetCurrent() {
    return _CFRunLoopGet(pthread_self());
}

위의 코드를 통해 알 수 있듯이 루틴과 RunLoop 사이에는 일일이 대응하고 그 관계는 하나의 전역적인 Dictionary에 저장된다.루프가 처음 만들어졌을 때 RunLoop이 없었습니다. 만약 당신이 주동적으로 가져오지 않았다면, 루프는 없었을 것입니다.RunLoop의 생성은 처음 가져왔을 때 발생했고, RunLoop의 삭제는 라인이 끝났을 때 발생했습니다.한 라인의 내부에서만 RunLoop을 얻을 수 있습니다. (주 라인을 제외합니다.)
RunLoop 외부 인터페이스
CoreFoundation에서 RunLoop과 관련된 5가지 클래스는 다음과 같습니다.
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef

이 가운데 CFRunLoopModeRef류는 외부에 노출되지 않고 CFRunLoopRef의 인터페이스를 통해 봉인됐다.하나의 RunLoop은 몇 개의 모드를 포함하고, 각각의 모드는 몇 개의 Source/Timer/Observer를 포함한다.RunLoop의 주 함수를 호출할 때마다 그 중 하나만 지정할 수 있습니다. 이 모드는CurrentMode라고 부릅니다.Mode 를 전환하려면 Loop 을 종료하고 Mode 를 다시 지정해야 합니다.이렇게 하는 것은 주로 서로 다른 그룹의 Source/Timer/Observer를 분리하여 서로 영향을 주지 않도록 하기 위해서이다.CFRunLoopSourceRef는 이벤트가 발생하는 곳입니다.Source에는 Source0과 Source1의 두 가지 버전이 있습니다.Source0은 하나의 콜백(함수 포인터)만 포함하고 이벤트를 자발적으로 트리거할 수 없습니다.사용할 때, CFRunLoop Source Signal (source) 을 호출해서 이 Source를 처리할 것으로 표시하고, 이 사건을 처리할 수 있도록 CFRunLoop Wake Up (runloop) 을 수동으로 호출해야 합니다.Source1에는 mach 이 포함되어 있습니다.port와 리셋 (함수 바늘) 은 내부 핵과 다른 라인을 통해 서로 메시지를 보내는 데 사용됩니다.이런 Source는 RunLoop의 라인을 자발적으로 깨울 수 있는데 그 원리는 아래에서 설명할 것이다.CFRunLoopTimer Ref는 시간 기반 트리거로 NSTimer와toll-free bridged로 혼용할 수 있습니다.그것은 시간 길이와 리셋을 포함한다.RunLoop에 가입하면 RunLoop은 해당 시점을 등록하고 해당 시점이 되면 RunLoop은 해당 콜백을 수행하기 위해 깨어납니다.
CFRunLoopObserverRef는 관찰자로서 모든 Observer에는 리셋(함수 바늘)이 포함되어 있으며 RunLoop의 상태가 변할 때 관찰자는 리셋을 통해 이 변화를 받아들일 수 있다.관측 가능한 시점은 다음과 같습니다.
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry         = (1UL << 0), //     Loop
    kCFRunLoopBeforeTimers  = (1UL << 1), //      Timer
    kCFRunLoopBeforeSources = (1UL << 2), //      Source
    kCFRunLoopBeforeWaiting = (1UL << 5), //       
    kCFRunLoopAfterWaiting  = (1UL << 6), //        
    kCFRunLoopExit          = (1UL << 7), //     Loop
};

위의 Source/Timer/Observer는 모뎀 item이라고 통칭하는데 하나의 item은 여러 모뎀을 동시에 추가할 수 있다.그러나 하나의 item이 같은 모델을 중복적으로 추가할 때 효과가 없습니다.모드에 item이 없으면 RunLoop은 순환에 들어가지 않고 종료됩니다.

좋은 웹페이지 즐겨찾기