인디노트

[Objective-c] NSRunLoop이 플래그가 설정되기를 기다리는 가장 좋은 방법은 무엇입니까? 본문

소스 팁/Objective C, Swift, iOS, macOS

[Objective-c] NSRunLoop이 플래그가 설정되기를 기다리는 가장 좋은 방법은 무엇입니까?

인디개발자 2018. 6. 9. 08:02

좋아, 문제를 설명했는데 가능한 해결책은 다음과 같다.

@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

start 는 진행 표시기를 표시하고 내부 runloop의 주 스레드를 캡처합니다. 그것은 다른 스레드가 완료되었음을 알 때까지 거기에 머무를 것입니다. 메인 쓰레드를 깨우기 위해서, 메인 쓰레드를 깨우는 것 이외의 다른 목적을 가진 함수를 처리하게 만들 것이다.

이것은 당신이 그것을 할 수있는 방법 중 하나입니다. 메인 스레드에서 알림을 게시하고 처리하는 것이 좋습니다 (다른 스레드도 등록 할 수 있음). 그러나 위의 해결책은 내가 생각할 수있는 가장 간단한 방법입니다. BTW 정말 스레드로부터 안전하지 않습니다. 실제로 스레드로부터 안전하려면 부울에 대한 모든 액세스가 스레드에서 NSLock 객체에 의해 잠길 필요가 있습니다 (이러한 잠금을 사용하면 POSIX 표준에 따라 잠금으로 보호되는 변수가 암시 적으로 휘발성이므로 "휘발성"을 쓸모 없게 만듭니다. C 표준은 자물쇠에 대해 몰라서, 휘발성 만이 코드가 작동하도록 보장 할 수 있으며, GCC는 자물쇠에 의해 보호되는 변수에 대해 휘발성을 설정할 필요가 없습니다.



 Question


NSRunLoop에 대한 Apple 문서에는 플래그가 다른 것으로 설정 될 때까지 기다리는 동안 실행을 일시 중단하는 샘플 코드가 있습니다.

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

나는 이것을 사용해 왔지만 작동하지만 성능 문제를 조사하면서이 코드 조각까지 추적했다. 나는 거의 동일한 코드 조각을 사용한다. 플래그의 이름이 다르다. 플래그가 설정되어있는 라인에 NSLog 를 놓고 (다른 메소드에서), while() 후에 라인을 쓴다. 몇 초 동안 두 개의 로그 문 사이에 임의로 기다리는 것 같습니다.

지연은 느린 기계 또는 더 빠른 기계에서는 다를 수는 없지만 최소 2 초에서 최대 10 초 사이의 실행마다 다릅니다.

이 문제를 해결하기 위해 다음 코드를 사용했지만 원래 코드가 작동하지 않는 것으로 보입니다.

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

이 코드를 사용하면 플래그를 설정할 때와 while 루프 이후의 로그 명령문이 일관되게 0.1 초 미만이됩니다.

원래 코드가이 문제를 나타내는 이유는 누구나?

반응형
Comments