それは長距離でした。この問題に関するフォローアップは次のとおりです。
早い段階で、残りのキャッシュを維持およびチェックするというアイデアを捨てました。これは、入力ストリームもブロックされる可能性があることをさらに反映したときに、出力ストリームに対してのみ機能するためです。
代わりに、アイドリングwhileループを設定しました。
- (void) stream:(NSStream *)theStream handleEvent:(NSStreamEvent)eventCode {
switch (eventCode)
// RECEIVING
case NSStreamEventHasBytesAvailable: {
if (self.receiveStage == kNothingToReceive)
return;
// Get the data from the stream. (This method returns NO if bytesRead < 1.)
if (![self receiveDataViaStream:(NSInputStream *)theStream]) {
// If nothing was actually read, consider the stream to be idling.
self.bStreamIn_isIdling = YES;
// Repeatedly retry read, until (1) the read is successful, or (2) stopNetwork is called, which will clear the idler.
// (Just in case, add nil stream property as a loop breaker.)
while (self.bStreamIn_isIdling && self.streamIn) {
if ([self receiveDataViaStream:(NSInputStream *)theStream]) {
self.bStreamIn_isIdling = NO;
// The stream will have started up again; prepare for next event call.
[self assessTransmissionStage_uponReadSuccess];
}
}
}
else
// Prepare for what happens next.
[self assessTransmissionStage_uponReadSuccess];
break;
// SENDING
case NSStreamEventHasSpaceAvailable:
if (self.sendStage == kNothingToSend)
return;
if (![self sendDataViaStream:(NSOutputStream *)theStream]) {
self.bStreamOut_isIdling = YES;
while (self.bStreamOut_isIdling && self.streamOut) {
if ([self sendDataViaStream:(NSOutputStream *)theStream]) {
self.bStreamOut_isIdling = NO;
[self assessTransmissionStage_uponWriteSuccess];
}
}
}
else
[self assessTransmissionStage_uponWriteSuccess];
break;
// other event cases…
次に、「キャンセル」ボタンを使用して、ユーザーが開始したキャンセルをテストするときが来ました。同期の途中で、Cocoa側で一時停止があり、ユーザー入力を待機しています。ユーザーがこの時点でキャンセルした場合、Cocoaアプリはストリームを閉じて実行ループから削除するため、接続の反対側のストリームがNSStreamEventEndEncountered
イベントを生成するか、おそらくNSStreamEventErrorOccurred
。しかし、いや、たった1つのイベント、NSStreamEventHasBytesAvailable
!図に行きます。
もちろん、ストリームは書き込まれずにCocoa側で閉じられていたため、実際には「使用可能なバイト」はありませんでした。そのため、iOS側のストリームハンドラーは無限ループに入りました。そんなに良くない。
次に、デバイスの1つがスリープ状態になった場合にどうなるかをテストしました。ユーザー入力の一時停止中に、自動ロック*を介してiPhoneをスリープ状態にしてから、Cocoa側でユーザー入力を提供しました。もう一度驚いたことに、Cocoaアプリは同期の最後まで混乱することなく継続し、iPhoneを起動すると、iOSアプリも同期の側面を完了したことがわかりました。
私のアイドルループによって修正されたiPhone側の問題があった可能性がありますか?ネットワーク停止ルーチンを投入して、次のことを確認しました。
if (![self receiveDataViaStream:(NSInputStream *)theStream])
[self stopNetwork]; // closes the streams, etc.
同期はまだ完了しました。障害はありませんでした。
最後に、入力の一時停止中にMac(Cocoa側)がスリープ状態になった場合に何が起こるかをテストしました。これにより、一種の逆げっぷが発生しました。2つのNSStreamEventErrorOccurred
イベントが受信されました— Mac側で、その後、出力ストリームに書き込むことができなくなりました。iPhone側ではイベントはまったく受信されませんでしたが、iPhoneのストリームステータスをテストすると、5、NSStreamStatusAtEndが返されます。
結論と計画:
- 「一時的なブロック」はユニコーンのようなものです。ネットワークがスムーズに動作するか、完全に切断されます。
- 本当に一時的なブロックのようなものがある場合、それを完全な切断と区別する方法はありません。一時ブロックに対して論理的と思われるストリームステータス定数は、
NSStreamStatusAtEnd
とだけNSStreamStatusError
です。しかし、上記の実験によれば、これらは切断を示しています。
- その結果、whileループを破棄し、bytesRead /Written<1をチェックするだけで切断を検出しています。
* Xcodeにスレーブされている場合、iPhoneはスリープしません。iPhoneから直接実行する必要があります。