문제

Apple 문서에서 NSRUNLOOP 깃발이 다른 것에 의해 설정되기를 기다리는 동안 실행 중단을 보여주는 샘플 코드가 있습니다.

BOOL shouldKeepRunning = YES;        // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

나는 이것을 사용하고 있으며 작동하지만 성능 문제를 조사 할 때이 코드로 추적했습니다. 나는 거의 정확히 같은 코드를 사용합니다 (플래그의 이름은 다릅니다 :) NSLog 플래그가 설정된 후 줄에 (다른 방법으로) while() 몇 초의 두 로그 문장 사이에는 무작위 대기가 있습니다.

지연은 느리거나 빠른 기계에서는 다르지 않지만 실행마다 다릅니다.

다음 코드 로이 문제를 해결했지만 원래 코드가 작동하지 않는 것 같습니다.

NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];
while (webViewIsLoading && [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate:loopUntil])
  loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];

이 코드를 사용하여 플래그를 설정할 때와 웨드 루프 이후 로그 문은 이제 0.1 초 이상 떨어져 있습니다.

원래 코드 가이 행동을 보여주는 이유는 무엇입니까?

도움이 되었습니까?

해결책

Runloops는 물건이 일어나는 마법 상자가 될 수 있습니다.

기본적으로 당신은 런 루프에 일부 이벤트를 처리 한 다음 돌아 오라고 말합니다. 또는 타임 아웃이 발생하기 전에 이벤트를 처리하지 않으면 반환하십시오.

0.1 초 타임 아웃을 사용하면 타임 아웃을 더 자주 사용하지 않습니다. Runloop는 화재가 발생하며 이벤트를 처리하지 않으며 0.1의 0.1로 반환됩니다. 때때로 이벤트를 처리 할 수있는 기회가 생길 것입니다.

Distantfuture 타임 아웃을 사용하면 Runloop은 이벤트를 처리 할 때까지 Foreever를 기다립니다. 그래서 그것이 당신에게 돌아 오면, 그것은 방금 어떤 종류의 사건을 처리했습니다.

짧은 타임 아웃 값은 무한 타임 아웃보다 훨씬 더 많은 CPU를 소비하지만 짧은 시간 초과를 사용하는 이유가 있습니다. 예를 들어 런 루프가 실행되는 프로세스/스레드를 종료하려면 런 루프가 주목하기를 원할 것입니다. 깃발이 바뀌었고 최대한 빨리 구제해야합니다.

Runloop Observers와 함께 놀고 싶을 수도 있습니다.

보다 이 Apple Doc 자세한 내용은.

다른 팁

좋아요, 문제를 설명했습니다. 여기에 가능한 해결책이 있습니다.

@implementation MyWindowController

volatile BOOL pageStillLoading;

- (void) runInBackground:(id)arg
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // Simmulate web page loading
    sleep(5);

    // This will not wake up the runloop on main thread!
    pageStillLoading = NO;

    // Wake up the main thread from the runloop
    [self performSelectorOnMainThread:@selector(wakeUpMainThreadRunloop:) withObject:nil waitUntilDone:NO];

    [pool release];
}


- (void) wakeUpMainThreadRunloop:(id)arg
{
    // This method is executed on main thread!
    // It doesn't need to do anything actually, just having it run will
    // make sure the main thread stops running the runloop
}


- (IBAction)start:(id)sender
{
    pageStillLoading = YES;
    [NSThread detachNewThreadSelector:@selector(runInBackground:) toTarget:self withObject:nil];
    [progress setHidden:NO];
    while (pageStillLoading) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }
    [progress setHidden:YES];
}

@end

시작 진행률 표시기를 표시하고 내부 런 루프에서 기본 스레드를 캡처합니다. 다른 스레드가 완료되었다고 발표 할 때까지 그곳에 머무를 것입니다. 메인 스레드를 깨우려면 메인 스레드를 깨우는 것 외에 다른 목적으로 기능을 처리하게됩니다.

이것은 당신이 할 수있는 방법 중 하나 일뿐입니다. 메인 스레드에 게시 및 처리되는 알림이 바람직 할 수 있지만 (다른 스레드도 등록 할 수 있음) 위의 솔루션은 내가 생각할 수있는 가장 간단합니다. btw 그것은 실제로 스레드 안전이 아닙니다. 실제로 스레드 안전이 되려면 부울에 대한 모든 액세스는 어느 스레드에서 NSLock 객체에 의해 잠겨 있어야합니다 (이러한 잠금을 사용하여 잠금으로 보호되는 변수가 POSIX 표준에 따라 암시 적 휘발성이므로 "휘발성"이 불가능합니다. C 표준은 잠금에 대해 알지 못하므로 여기서 휘발성만이 코드가 작동하도록 보장 할 수 있습니다. GCC는 잠금으로 보호되는 변수를 위해 휘발성을 설정할 필요가 없습니다).

일반적으로, 이벤트를 직접 처리하고 있다면, 당신은 잘못하고 있습니다. 내 경험에 따라 많은 지저분한 문제를 일으킬 수 있습니다.

모드로 실행하려면 (예 : 진행 패널을 표시하는) 모드로 실행하십시오! 계속해서 nsapplication 메소드를 사용하고 진행 시트를 모드로 실행 한 다음 부하가 완료되면 모달을 중지하십시오. 예를 들어 Apple 문서를 참조하십시오 http://developer.apple.com/documentation/cocoa/conceptual/winpanel/concepts/usingmodalwindows.html .

부하 기간 동안보기를 원하지만 모달이되기를 원하지 않는다면 (예 : 다른 견해가 이벤트에 응답 할 수 있기를 원한다면) 훨씬 간단한 일을해야합니다. 예를 들어, 당신은 다음을 수행 할 수 있습니다.

- (IBAction)start:(id)sender
{
    pageStillLoading = YES;
    [NSThread detachNewThreadSelector:@selector(runInBackground:) toTarget:self withObject:nil];
    [progress setHidden:NO];
}

- (void)wakeUpMainThreadRunloop:(id)arg
{
    [progress setHidden:YES];
}

그리고 당신은 끝났습니다. 런 루프를 제어 할 필요가 없습니다!

-wil

플래그 변수를 설정하고 런 루프에 즉시 주목할 수 있으려면 사용하십시오. -[NSRunLoop performSelector:target:argument:order:modes: 실행 루프에 플래그를 False로 설정하는 메소드를 호출하도록 요청합니다. 이렇게하면 런 루프가 즉시 회전하고 메소드가 호출되고 플래그가 확인됩니다.

코드에서 현재 스레드는 변수가 0.1 초마다 변경되었는지 확인합니다. Apple 코드 예제에서 변수를 변경하면 아무런 영향을 미치지 않습니다. 런 루프는 일부 이벤트를 처리 할 때까지 실행됩니다. WebViewIsLoading의 값이 변경되면 자동으로 이벤트가 생성되지 않으므로 루프에 머무르면 왜 나가는가? 다른 이벤트가 처리 될 때까지 그곳에 머물게됩니다. 이것은 1, 3, 5, 10 또는 20 초에 발생할 수 있습니다. 그리고 그렇게 될 때까지, 그것은 런 루프에서 벗어나지 않으므로이 변수가 바뀌 었음을 알지 못할 것입니다. IOW 당신이 인용 한 Apple 코드는 결정적입니다. 이 예제는 WebViewisLoading의 가치 변경이 런 루프가 일어나는 이벤트를 생성하는 경우에만 작동하며, 이것이 사실이 아닌 것 같습니다 (또는 적어도 항상 그렇지는 않습니다).

문제를 다시 생각해야한다고 생각합니다. 변수가 WebViewisLoading이라는 이름이 지정되어 있으므로 웹 페이지가로드 될 때까지 기다리십니까? WebKit을 사용하고 있습니까? 그런 변수가 전혀 필요하지 않거나 게시 한 코드가 필요합니다. 대신 앱을 비동기로 코딩해야합니다. "웹 페이지로드 프로세스"를 시작한 다음 기본 루프로 돌아가서 페이지가로드가 완료 되 자마자 기본 스레드 내에서 처리 된 알림을 비동기로 게시하고 곧 실행 해야하는 코드를 실행해야합니다. 로딩이 완료되었습니다.

관리하려고하는 동안 비슷한 문제가있었습니다 NSRunLoops. 그만큼 논의 ~을 위한 runMode:beforeDate: 수업 참조 페이지에서 다음과 같이 말합니다.

런 루프에 입력 소스 나 타이머가 부착되지 않으면이 메소드는 즉시 종료됩니다. 그렇지 않으면 첫 번째 입력 소스가 처리되거나 LimitDate에 도달 한 후 반환됩니다. 런 루프에서 알려진 모든 입력 소스와 타이머를 수동으로 제거하는 것은 런 루프가 종료 될 것이라는 보장이 아닙니다. Mac OS X는 수신기의 스레드를 대상으로하는 요청을 처리하기 위해 필요에 따라 추가 입력 소스를 설치하고 제거 할 수 있습니다. 따라서 이러한 소스는 런 루프가 종료되는 것을 막을 수 있습니다.

가장 좋은 추측은 입력 소스가 귀하의 NSRunLoop, 아마도 OS X 자체에 의해 runMode:beforeDate: 해당 입력 소스에 일부 입력이 처리되거나 제거 될 때까지 차단됩니다. 당신의 경우에 그것은 "몇 초와 최대 10 초"이것이 일어나기 위해 runMode:beforeDate: 부울과 함께 돌아올 것입니다 while() 다시 실행되면 감지 할 것입니다 shouldKeepRunning 설정되었습니다 NO, 그리고 루프가 종료 될 것입니다.

당신의 개선과 함께 runMode:beforeDate: 입력 소스가 부착되었는지 또는 입력을 처리했는지 여부에 관계없이 0.1 초 이내에 반환됩니다. 교육받은 추측입니다 (저는 런 루프 내부의 전문가가 아닙니다). 그러나 당신의 개선이 상황을 처리하는 올바른 방법이라고 생각합니다.

두 번째 예제는 시간 간격 0.1 내에서 런 루프의 입력을 확인하기 위해 설문 조사를 통해 해결됩니다.

때때로 나는 당신의 첫 번째 예를위한 해결책을 찾습니다.

BOOL shouldKeepRunning = YES;        // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSRunLoopCommonModes beforeDate:[NSDate distantFuture]]);
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top