2016-06-25 7 views
0

매우 비슷한 문제는 이미 here에 대해 논의되었습니다. 손에 문제가 무엇을 달성하기 위해 노력하고 스레드에서 만든 주어진 개체에 대한 함수를 호출 할 수 있습니다. 다음은 전체 사례입니다.Objective-C : 스레드가 주 스레드가 아닌 경우 [NSObject performSelector : onThread ...]가 호출되지 않습니다.

  1. 클래스 A의 인스턴스는 주어진 NSThread (스레드 A) (주 어댑터가 아닌)에서 생성됩니다. 인스턴스는 구성원 변수로 NSThread을 계속 작성합니다.
  2. 클래스 B의 인스턴스는 다른 멤버 함수 중 하나가 NSThread - 스레드 B에서 실행되며 A의 작성 스레드에서 A의 함수를 호출하려고합니다. 따라서 B의 현재 실행중인 함수는 다음 호출을 발행 :

    [_a performSelector: @(fun) 
          onThread: _a.creationThread 
         withObject: nil 
         waitUntilDone: NO]; 
    

A의 인스턴스의 생성 스레드가 메인 하나, fun가 호출 결코 극복하지 않으면. 생성 스레드가 주 스레드 인 경우 항상 호출됩니다. 먼저 A의 인스턴스를 만든 스레드가 파괴되었고 포인터가 잘못된 스레드를 가리키는 지 실제로 스레드 객체 (스레드 A)의 함수를 호출하면 유효한 결과가 나타나고 충돌은 발생하지 않는지 생각하고있었습니다. 또한 객체 검사는 this check에 따라 유효합니다. 어떤 제안?


업데이트 :

내가 백그라운드 스레드에서 타이머를 만드는 일을 해요 :

_timer = [NSTimer timerWithTimeInterval:60.0 target:self selector:@selector(fun:) userInfo:nil repeats:YES]; 
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode]; 

이 코드는 타이머를 시작하는 것입니다. 특정 백그라운드 스레드에서 타이머가 시작되지 않았습니다. 타이머를 만드는 함수가 모든 스레드에서 호출 될 수 있습니다. 따라서 타이머는 NSTimer 문서 상태와 정확히 같은 것으로 무효화되어야합니다 : "항상 타이머가 설치된 동일한 스레드에서 무효화 메소드를 호출해야합니다."

+2

(a) 당신이 스레드를 시작했는지 궁금합니다. (b) 실행중인 루프가있는 경우; 그리고 (c) 그 스레드를 차단하는 것이 있으면. 귀하의 질문을 편집하고 스레드의 생성과 시작 및 실행 루프의 시작을 보여주는 문제의 [아주 작은 재생 가능한 예제] (http://stackoverflow.com/help/mcve)를 포함 시키십시오. 기존 코드를 모두보고 싶지는 않지만, 행동을 재현하기 위해 최소한의 코드 만 추출하십시오. (제쳐두고, GCD는이 작업을 훨씬 쉽게 만들어 주지만,'NSThread'를 사용해야 할 강력한 이유가 있다고 가정합니다.) – Rob

+0

작은 재현 가능한 예제를 만들려고했지만 대신 문제를 해결하는 데 집중했습니다. GCD는 도움이되지 않습니다 - 객체 A의 주어진 스레드에서 생성 된 타이머가 있으며 동일한 스레드에서 무효화되어야합니다. 타이머 생성 순간에 호출을 전달하는 큐를 캐싱 할 수 없습니다.'dispatch_get_current_queue()'는 더 이상 사용되지 않습니다. 또한 'NSThread', 디스패치 대기열, NSOperations를 혼합하는 것이 그리 건강하지 않다는 것을 알고 있습니다. 쓰레드 나 디스패치 큐 캐싱에 대한 해결책이 마음에 들지 않지만 더 좋은 제안이 있다면 공유하십시오. – Ivan

+1

GCD 디스패치 타이머 소스를 사용할 수 있습니다. 그것들은 어떤 스레드에서나 취소 될 수 있습니다. –

답변

2

백그라운드 스레드에서 타이머를 실행하려면 두 가지 옵션이 있습니다.

  1. 사용 파견 타이머 소스 : 당신이 다음이 타이머는 2 초마다 화재로 구성 할 수 있습니다

    @property (nonatomic, strong) dispatch_source_t timer; 
    

    과 : 백그라운드 스레드에

    - (void)startTimer { 
        dispatch_queue_t queue = dispatch_queue_create("com.domain.app.timer", 0); 
        self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); 
        dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), 2.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC); 
        dispatch_source_set_event_handler(self.timer, ^{ 
         // call whatever you want here 
        }); 
        dispatch_resume(self.timer); 
    } 
    
    - (void)stopTimer { 
        dispatch_cancel(self.timer); 
        self.timer = nil; 
    } 
    
  2. 실행 NSTimer. 이렇게하려면, 당신은 같은 것을 할 수있다 : 나는 shouldKeepRunning 상태 변수에 대한 미치지 않았어

    @property (atomic) BOOL shouldKeepRunning; 
    @property (nonatomic, strong) NSThread *timerThread; 
    @property (nonatomic, strong) NSTimer *timer; 
    

    그리고

    - (void)startTimerThread { 
        self.timerThread = [[NSThread alloc] initWithTarget:self selector:@selector(startTimer:) object:nil]; 
        [self.timerThread start]; 
    } 
    
    - (void)stopTimerThread { 
        [self performSelector:@selector(stopTimer:) onThread:self.timerThread withObject:nil waitUntilDone:false]; 
    } 
    
    - (void)startTimer:(id)__unused object { 
        @autoreleasepool { 
         NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; 
         self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(handleTimer:) userInfo:nil repeats:YES]; 
         [runLoop addTimer:self.timer forMode:NSDefaultRunLoopMode]; 
    
         self.shouldKeepRunning = YES; 
         while (self.shouldKeepRunning && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) 
          ; 
    
         self.timerThread = nil; 
        } 
    } 
    
    - (void)handleTimer:(NSTimer *)timer { 
        NSLog(@"tick"); 
    } 
    
    - (void)stopTimer:(id)__unused object { 
        [self.timer invalidate]; 
        self.timer = nil; 
        self.shouldKeepRunning = FALSE; 
    } 
    

    ,하지만 당신은 애플 documentation for the run method 보면, 그들은시 의존성을 억제

    실행 루프를 종료하려면이 방법을 사용하지 않아야합니다. 대신 다른 run 메서드 중 하나를 사용하고 루프의 다른 임의 조건을 검사하십시오.간단한 예는 다음과 같습니다

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

개인적으로, 나는 디스패치 타이머 접근 방식을 권하고 싶습니다.