2009-05-29 6 views
5

私はちょうどサンプルアプリケーションを使っていましたが、私の頭を完全にNSRunLoopにラップさせようとしました。私が書いたサンプルはNSOperationで簡単なセカンダリスレッドを作成しました。セカンダリスレッドは、NSTimerの処理や、NSStreamの初歩的なストリーミングなどのいくつかのタスクを実行します。これらの入力ソースの両方は、実行するために適切に設定されたNSRunLoopを必要とします。メインスレッドのNSRunLoopがセカンダリスレッドで参照されています

私の質問はこれです。イベントはプロセスだろう、上記のコードで、今

NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop]; 
self.connectTimer = [NSTimer scheduledTimerWithTimeInterval:connectTimeout 
                target:self 
                selector:@selector(connectionConnectedCheck:) 
                userInfo:nil 
                repeats:NO]; 

[myRunLoop addTimer:self.connectTimer forMode:NSDefaultRunLoopMode]; // added source here 
[myRunLoop run]; 

[NSStream getStreamsToHostNamed:relayHost port:relayPort inputStream:&inputStream outputStream:&outputStream]; 
if ((inputStream != nil) && (outputStream != nil)) 
{ 
    sendState = kSKPSMTPConnecting; 
    isSecure = NO; 

    [inputStream retain]; 
    [outputStream retain]; 

    [inputStream setDelegate:self]; 
    [outputStream setDelegate:self]; 

    [inputStream scheduleInRunLoop: myRunLoop //[NSRunLoop currentRunLoop] 
            forMode:NSRunLoopCommonModes]; 
    [outputStream scheduleInRunLoop: myRunLoop //[NSRunLoop currentRunLoop] 
            forMode:NSRunLoopCommonModes]; 

    [inputStream open]; 
    [outputStream open]; 

    self.inputString = [NSMutableString string]; 



    return YES; 
} 

:もともと私は、二次スレッドでこのように見えたいくつかのコードを持っていました。 currentRunLoopにはありません。私は後で何かひどいことをしました。これは単なる教育練習だったので、NSRunLoopmainRunLoopで実行するように修正しました。魔法のように働いた。しかし、私の主なスレッドに応じて、セカンダリスレッドでループを実行すると、が異なる10種類のレベルにあることがほぼ間違っています。

私の質問は2つの部分に分かれているので、これは問題ありません。

  1. おそらく私が実行して、実行ループを経由してイベントに応答するセカンダリスレッドを取得するために適用ハック「少し」と間違って何をして行くことができますか?

  2. 私はステップに洞察力のための1

おかげですべてを行う必要はありませんので、すべてのイベント/タイマーベースのソースを聞くためにセカンダリスレッドを構成するための適切な方法は何でしょう。

答えて

6

逆の順序であなたの質問に答えます。あなたには2つの問題があります。 -[NSRunLoop run]のドキュメントは言う:

無入力ソースまたはタイマーを実行ループに接続 、直ちに終了し この方法ではない場合。それ以外の場合は、 NSDefaultRunLoopModeの受信者を繰り返し実行します。 runMode:beforeDate:を呼び出します。他の ワードでは、このメソッドは実質的に、実行ループの入力ソースと タイマーのデータ を処理する無限ループを から開始します。

スレッドの独自の実行ループを使用すると、実行ループに対して入力ソースが定義されていない可能性があるため、すぐに戻ります。存在する場合、実行ループは無限にループしており、その時点以降の残りのコードは決して実行されません。

正常に動作するためには、実行ループにはいくつかの入力ソースがある必要があり、次に定期的にイベントをチェックする必要があります。 [NSRunLoop run]を使用したくないことに注意してください。その代わり、私は、スレッドがキャンセルされるまで、またはあなたがストリーミングデータを終わっまで継続的に実行ループを実行し、右return YES前にループを設定することをお勧めします。これにより、実行ループは到着時にデータを処理できます。このような何か:

while (!done) { 
    [myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; 
} 

[NSRunLoop runUntilDate:]は、指定した日付までのイベントを処理し、あなたがやりたいことができるように、あなたのプログラムに制御を返します。

1。イベントが処理されているので、あなたのメインスレッドの実行ループは、定期的に実行されているので、それは働いています。ただし、メインスレッドがブロックされた場合、データの到着が停止します。メインスレッドが2番目のスレッドからのデータを待っていた場合、これは特に悪い可能性があります。

警告:また、NSRunLoopはスレッドセーフではありませんNSRunLoopクラスは、一般的に スレッドセーフとそのメソッドのみ 現在のスレッドのコンテキスト内 と呼ばれるべきではないと考え です。あなたは そうすることが 予期しない結果を引き起こす可能性があるとして、 別のスレッドで実行されている NSRunLoopオブジェクトのメソッドを呼び出そうとすることはありません。 (NSRunLoopのドキュメントから。)

Appleのスレッドプログラミングガイドは、ある程度、このすべてを説明しRun Loop Managementのセクションを、持っています。これまで読んだことのない最も明瞭な文書ではありませんが、実行ループで作業している場合は、開始するのが良い場所です。

2

runloopを実行するのを忘れましたか?

2

[[NSRunLoop currentLoop] run] 
+0

私は確かにそれを試してみました。運がない。コードは[NSRunLoop mainRunLoop]で実行された場合にのみ実行されます –

1

まず、scheduledTimer...メソッドは、タイマーを現在の実行ループに自動的に追加することになっています。あなただけがinitWithFireDate...初期化子を使用して、タイマーを作成した場合addTimer:メソッドを使用する必要があります。私はタイマーを2度追加することは問題を引き起こすだろうとは思っていませんが、それは可能です。

runNSRunLoopメソッドは、イベントソースがなくなるか、ループが明示的に終了するまで返されません。つまり、の前にと呼び出す予定のイベントソースをrunに設定する必要があります。 [myRunLoop run]呼び出しをコードサンプルの最後まで移動します。

[NSRunLoop run]が返ってきた場合、イベントソースがスケジュールされていないという結論になります。デバッガを使用してコードをステップ実行します。 [NSRunLoop run]コールを過ぎても処理が進行している場合は、入力ソースに問題があることを確認できます。

関連する問題