2016-06-25 6 views
0

非常によく似た問題はすでにhereで議論されています。手元の問題と私が達成しようとしているのは、作成されたスレッド内の特定のオブジェクトに対して関数を呼び出すことです。Objective-C:スレッドが主なものでない場合、[NSObject performSelector:onThread ...]は呼び出されません

  1. クラスAのインスタンスが所与NSThread(スレッドA)で作成された(しない主なもの):ここで、完全なケースです。このインスタンスは、メンバー変数として、NSThreadの作成を保持します。
  2. クラスBのインスタンスは、別のNSThread - スレッドBで実行されているメンバ関数の1つを持ち、Aの作成スレッドのAの関数を呼び出したいとします。したがって、Bの現在実行中の関数は、次の呼び出しを発行します。

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

Aのインスタンスの作成スレッドは、メイン1、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のドキュメントの状態とまったく同じもので無効にする必要があります。「タイマーがインストールされている同じスレッドからinvalidateメソッドを呼び出す必要があります。

+2

。 (b)実行ループが走っている場合。 (c)そのスレッドをブロックしているものがあれば。私はあなたの質問を編集し、スレッドの作成と起動と実行ループの開始を示す問題の[非常に小さな再現可能な例](http://stackoverflow.com/help/mcve)を含めることをお勧めします。既存のコードをすべて表示するのではなく、動作を再現するために最小限に留めることをお勧めします。 – Rob

+0

(さておき、GCDはこのようなものがはるかに容易になりますが、私はあなたが 'NSThread'を使用するためにいくつかの説得力のある理由があるとします。として)私は小さな再現性の例を作成しようとしていましたが、代わりに問題を修正するに焦点を当てました。 GCDは役に立ちません - オブジェクトAの特定のスレッドで作成されたタイマーがあり、同じスレッドで無効にする必要があります。タイマー作成の瞬間にコールをディスパッチするキューをキャッシュできません - 'dispatch_get_current_queue()'は推奨されていません。また、 'NSThread'、ディスパッチキュー、NSOperationsを混在させることはあまり健康的ではないことを知っています。私はスレッドやディスパッチキューのいずれかをキャッシュする方法が嫌いですが、それ以上の提案があれば共有してください。 – Ivan

+1

GCDディスパッチタイマーソースを使用できます。それらは任意のスレッドから取り消すことができます。 –

答えて

2

バックグラウンドスレッドでタイマーを実行するには、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。これを行うには、あなたが何かを行うことができます。

    @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; 
    } 
    

    を私はshouldKeepRunning状態変数に夢中ないんだけど、あなたはアップルdocumentation for the run methodを見れば、彼らはへの依存を落胆します

    実行ループを終了させる場合は、この方法を使用しないでください。その代わりに、他のrunメソッドの1つを使用し、ループ内で自分の他の任意の条件をチェックしてください。簡単な例は次のようになります。

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

個人的に、私は派遣タイマーのアプローチをお勧めします。 (a)は、あなたがスレッドを開始した場合、私は疑問に思う

関連する問題