主な問題
Socket Rocket の帯域幅管理を実装しています。NSOutputStreamSocket Rocket の変更量を減らすために、ソケット用に作成されたラップのサブクラスを作成することにしましNSOutputStreamた。コンセプトは非常に素晴らしく、魅力的に機能するはずです。
遭遇問題
メソッドでは- (NSInteger)write:(const uint8_t *)buffer maxLength:(NSUInteger)len、データを送信できるかどうかを帯域幅マネージャーから問い合わせています。そうでない場合、帯域幅マネージャーは次の書き込み操作に必要な遅延を与えてくれます。
したがって、私のコードは多かれ少なかれ次のようになります。
- (NSInteger)write:(const uint8_t *)buffer maxLength:(NSUInteger)len
{
auto bandwithInfo = [_bandwidthManager bandwidthInfoForSize: len];
if (bandwithInfo.m_bOKtoSendNow)
{
auto actualWritten = [self.throttledStream write: buffer maxLength: len];
if (actualWritten<0)
{
// report failure of sending data
[_bandwidthManager reportNetworkBackPressure: len];
}
else
{
[_bandwidthManager ReportConsumedSendBandwidth: actualWritten];
int bytesNotSentCount = len - actualWritten;
if (bytesNotSentCount>0)
{
[_bandwidthManager ReportNetworkBackPressure: bytesNotSentCount];
}
[self enqueueEvent: NSStreamEventHasSpaceAvailable];
}
return actualWritten;
}
else
{
auto delay = bandwithInfo.m_nWaitTimeMilliseconds;
NSASSERT(delay>0);
[self scheduleNotifyReadyToWriteAfterMs: delay];
return 0;
}
}
- (void)scheduleNotifyReadyToWriteAfterMs: (int)miliseconds
{
if (!isReadyToWriteScheduled)
{
isReadyToWriteScheduled = YES;
static const NSTimeInterval kTimeIntervalMilisecond = 0.001;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval: miliseconds * kTimeIntervalMilisecond
target: self
selector: @selector(notifyReadyToWrite:)
userInfo: nil
repeats: false];
NSLog(@"timer=%@\nRunLoop=%@", timer, [NSRunLoop currentRunLoop]);
// this alternative also doesn't work:
// [self performSelector: @selector(notifyReadyToWrite:)
// withObject: nil
// afterDelay: miliseconds * kTimeIntervalMilisecond];
}
}
- (void)notifyReadyToWrite: (NSTimer *)timer
{
[timer invalidate];
if (self.hasSpaceAvailable) {
isReadyToWriteScheduled = NO;
[self enqueueEvent: NSStreamEventHasSpaceAvailable];
}
}
私が見ることができるログでは、(実行ループがあり、作成されたタイマーが含まれています):
timer=<__NSCFTimer: 0x109a96180>
RunLoop=<CFRunLoop 0x109a962d0 [0x7fff7208f440]>{wakeup port = 0x4507, stopped = false, ignoreWakeUps = true,
current mode = (none),
common modes = <CFBasicHash 0x109a96390 [0x7fff7208f440]>{type = mutable set, count = 1,
entries =>
2 : <CFString 0x7fff71fa1940 [0x7fff7208f440]>{contents = "kCFRunLoopDefaultMode"}
}
,
common mode items = (null),
modes = <CFBasicHash 0x109a963d0 [0x7fff7208f440]>{type = mutable set, count = 1,
entries =>
2 : <CFRunLoopMode 0x109a96410 [0x7fff7208f440]>{name = kCFRunLoopDefaultMode, port set = 0x440b, queue = 0x109a964e0, source = 0x109a96570 (not fired), timer port = 0x4b03,
sources0 = (null),
sources1 = (null),
observers = (null),
timers = <CFArray 0x109a95b10 [0x7fff7208f440]>{type = mutable-small, count = 1, values = (
0 : <CFRunLoopTimer 0x109a96180 [0x7fff7208f440]>{valid = Yes, firing = No, interval = 0, tolerance = 0, next fire date = 485078914 (-0.00818103552 @ 3808422033916), callout = (NSTimer) [SRSendStreamWithThrottling notifyReadyToWrite:] (0x7fff87025d0d / 0x10264b100) (/Users/maru/Library/Developer/Xcode/DerivedData/AvayaCommonWorkspace-cnweundajqjciphewynfexnutumh/Build/Products/Debug/testrunner), context = <CFRunLoopTimer context 0x109a93370>}
)},
currently 485078914 (3808417241824) / soft deadline in: 0.00479207 sec (@ 3808422033916) / hard deadline in: 0.004791892 sec (@ 3808422033916)
},
}
}
これで、カスタム ストリームとラップされたストリームがこの実行ループでスケジュールされた実行ループができたので、ソケット ロケットとラッピング クラスがすべての通知を受け取っているので、明らかな間違いをすべて除外しました。
それでも、何らかの不思議な理由notifyReadyToWrite:で呼び出されることはありません。誰かが理由を知っていますか?