« J1 2009年シーズンが閉幕しまし(何 | ホーム | Intermezzo 0.9.1 »
2009年4月 9日
NSTerminateLaterとNSStream(っていうかおそらくネットワーク通信全般)ではまる
NSApplicationにterminate:が送られたときの話。Graceful Application Terminationを読むとapplicationShouldTerminate:メソッドが即座に呼ばれるのでここで終了前に片付けときたい処理をやっとけばいい、とか何とか。
http://developer.apple.com/documentation/Cocoa/Conceptual/AppArchitecture/Tasks/GracefulAppTermination.html
Application Architecture Overview: Graceful Application Termination
だが、ここでNSTerminateLaterを返し、その裏で開いていたNSStreamをcloseして完了したら[NSApp replyToApplicationShouldTerminate:YES]を送ろうとして全然ダメっぽいことに気がつく。NSTerminateLaterを返したあとの動きをデバッガで止めたりして追う限り、どうも-[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:]メソッドで止まってるとかいう表示が出てきた。
ここからするとどうやら、NSTerminateLaterの返りを受け取った直後にイベント待ちのブロックを置いていて、replyToApplicationShouldTerminate:メソッドでそれを解除するためのイベントを発行させているらしい。ようだ。
よろしい。ならばs(ry
とりあえず終了に必要な処理、例えば全てのソケットやNSStreamのcloseが完了(もしくはキャンセル)したことを通知するイベントを作って、applicationShouldTerminate:の中でそれを待ち、完了したらNSTerminateNowを、キャンセルだったらNSTerminateCancelを送るようにしてみた。もう少しうまい手があるような気もしないでもないけど、ひとまずの目的は達せたので良しとしよう。
enum {
kFinishClosing = 9998,
kCancelClosing = 9999,
};
- (void)terminateOk:(id)sender
{
NSEvent* ev = [NSEvent otherEventWithType:NSApplicationDefined
location:NSMakePoint(0, 0)
modifierFlags:0
timestamp:[NSDate timeIntervalSinceReferenceDate]
windowNumber:0
context:nil
subtype:kFinishClosing
data1:0
data2:0];
[NSApp postEvent:ev atStart:YES];
}
- (void)terminateCancel:(id)sender
{
NSEvent* ev = [NSEvent otherEventWithType:NSApplicationDefined
location:NSMakePoint(0, 0)
modifierFlags:0
timestamp:[NSDate timeIntervalSinceReferenceDate]
windowNumber:0
context:nil
subtype:kCancelClosing
data1:0
data2:0];
[NSApp postEvent:ev atStart:YES];
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)app
{
if ([[NXSessionController sharedController] hasActiveSession]) {
[[NXSessionController sharedController] closeAllSessions:self];
// ここでNSTerminateLaterを返すと、NSInputStreamがこのあと機能しなくなり、
// "close"イベントが拾えずにアプリケーションが終了できない。
// delegateへの-[NSStream stream:handleEvent:]メソッド自体が呼ばれなくなるようだ。
NSEvent* ev = nil;
while (ev = [NSApp nextEventMatchingMask:NSAnyType
untilDate:[NSDate dateWithTimeIntervalSinceNow:5]
inMode:NSDefaultRunLoopMode
dequeue:YES]) {
if ([ev type] == NSApplicationDefined) {
if ([ev subtype] == kFinishClosing) {
return NSTerminateNow;
} else if ([ev subtype] == kCancelClosing) {
return NSTerminateCancel;
}
}
[NSApp sendEvent:ev];
}
return NSTerminateCancel;
}
return NSTerminateNow;
}
トラックバック(0)
トラックバックURL: http://foursics.jp/cgi-bin/mt/mt-tb.cgi/251
コメントする