60176


メイン画面のボタンを押した時に、プログラム上でNSWindowを使って別画面を生成していますが  
×ボタンで閉じた後に、メモリが解放されていないため、再度生成するところで「EXC_BAD_ACCESS」となってしまいます。  
window閉じた時にメモリを解放する「isOneShot」というのを使おうと思いましたが  
OS X 10.14では使えなくなっているみたいでした。

そこで、isOneShotの代わりになるものはありますでしょうか？  
もしくは、他に、×ボタンでwindowを閉じた際に、再度開く時にエラーにならないような仕組みは  
どのようにすれば良いのでしょうか？

ViewController.swift

[code]

    import Cocoa
    
    class ViewController: NSViewController {
    
        var window:NSWindow? = nil
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // Do any additional setup after loading the view.
        }
    
        override var representedObject: Any? {
            didSet {
            // Update the view, if already loaded.
            }
        }
    
        @IBAction func createWindow(_ sender: Any) {
            window = NSWindow(contentRect: NSMakeRect(0, 0, 500, 200), styleMask: [.titled, .closable, .miniaturizable], backing: .buffered, defer: false)
            window!.title = "test"
            window!.center()
            window!.makeKeyAndOrderFront(self)
            window!.isOneShot = true // これが使えない
    
        }
    }
    
[/code]


CC BY-SA 4.0
2019-10-30T08:21:59.930
60123
2019-11-01T06:33:42.783
35619
question
swift macos
cocoa swift4 NSWindowのメモリ解放について


`NSWindow()`で作られたWindowは、プロパティ`isReleasedWhenClosed`のデフォルトが`true`のため、クローズボタンでの解放と、次に作成したウィンドウの作成代入での解放と２回解放しようとされるのがエラーの原因の様です。

以下のように

  1. `VieweController`を`NSWindowDelegate`プロトコルに適応させる
  2. ウィンドウ生成時にプロパティ`isReleasedWhenClosed`を`false`に設定し、自動でメモリー解放が行われないようにする
  3. Window作成時に自身(`ViewController`)が作成したウィンドウの`delegate`であることを作成したwindowにセットする
  4. `NSWindowDelegae`プロトコルの`windowWillClose`でクローズされるウィンドウが自身が管理するウィンドウであった場合、メンバー変数`window`に`nil`を代入して、ここでメモリー解放を行わせる

で、回避できると思います。  
ただし、1.,4.はウィンドウが閉じられてから、次のウィンドウ作成されるまでの間にそのウィンドウにアクセスしないための予防措置なので、ウィンドウが閉じられている間は絶対にメンバー変数`window`にアクセスしない自信があるなら不要です。

[code]

    class ViewController: NSViewController, NSWindowDelegate {  // 1
    
        var window: NSWindow? = nil
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // Do any additional setup after loading the view.
        }
    
        @IBAction func createWindow(_ sender: Any) {
    
            let win: NSWindow = NSWindow(contentRect: NSMakeRect(0, 0, 500, 200), styleMask: [.titled, .closable, .miniaturizable], backing: .buffered, defer: false)
            win.title = "test"
            win.center()
            win.makeKeyAndOrderFront(self)
            win.isReleasedWhenClosed = false    // 2
            win.delegate = self // 3
    
            window = win
        }
    
            // 4
        func windowWillClose(_ notification: Notification) {
            if let win: NSWindow = notification.object as? NSWindow {
                if win == window {
                    window = nil
                }
            }
        }
    }
    
    
[/code]


CC BY-SA 4.0
2019-11-01T06:33:42.783
60176
2019-11-01T06:33:42.783
14745
60123
answer