swiftはnull安全な言語なので、nullの変数にアクセスしてしまって落ちる・・・ということがないはずなのですがCocoaの内部でnullアクセスしてアプリが落ちることがあります・・・
その罠のひとつを紹介したいと思います。
例えば何かのビューの上にマウスカーソルを持ってきた時、すぐさまポップアップを表示するのではなく少し遅延させて表示したい場合があると思います。(以下の例では1秒遅延)
その場合にダメな例と良い例のサンプルが以下。
class sampleView: NSView
{
let popover = NSPopover()
//ダメな例
func showPopover_NG() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
guard let _self = self else { return }
_self.popover.show(relativeTo: NSRect.zero, of: _self, preferredEdge: .minX)
}
}
//良い例
func showPopover_OK() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
guard let _self = self, _self.window != nil else { return }
_self.popover.show(relativeTo: NSRect.zero, of: _self, preferredEdge: .minX)
}
}
}
ダメな例がなぜダメか、というと
popover.showが実行されるその瞬間にsampleViewのwindowがnullになっていることがあるからです。
sampleViewのwindowがnullになっていると、popover.showした途端にアプリが落ちます。popover.showは内部的にsampleViewのwindowにアクセスしているようです。
本来的には「sampleViewのwindowがnullであるならshowは無視される」というcocoaフレームワークの挙動になっていれば良いのですがそうはしてくれないのがApple。
ですので、showの前にwindowのnullチェックが必要になります。このnullチェック、何のためにやっているのかあとからソースを見た人がわからなくなるのでやりたくないんですが・・・