1

I would like to use the open file dialogs from NSFilemanager but my code crashes sometimes and sometimes works and I do not know why. Sometimes it works 100%, sometimes the window is empty, sometimes the background behind the dialog ist shown in the window. When a craash occurs, "signal:SIGABRT" is shown in Xcode.

func openfiledlg (title: String, message: String) -> String
{
    var myFiledialog: NSOpenPanel = NSOpenPanel()

    myFiledialog.prompt = "Öffnen"
    myFiledialog.worksWhenModal = true
    myFiledialog.allowsMultipleSelection = false
    myFiledialog.canChooseDirectories = false
    myFiledialog.resolvesAliases = true
    myFiledialog.title = title
    myFiledialog.message = message
    myFiledialog.runModal()
    var chosenfile = myFiledialog.URL
    if (chosenfile != nil)
    {
        var TheFile = chosenfile.absoluteString!
        return (TheFile)
    }
    else
    {
        return ("")
    }
}

What have I done wrong? Why does it crash?

The App does not run on the main thread. I always open a new thread, which runs my program. The Main-Thread only handels the Screen Updates from SpriteKit, which I use for my programs.

I just built up a new Cocoa-Based App and let the function run in the main-Thread and there it works. When I start a Thread in the Cocoa-App it crashes like in the SpriteKit Environment.

I need to start a new thread in the Sprite-Kit Environment because the updates will not be done if I start my main program directly from the AppDelegate. The main Program runs until the whole SpriteKit quits, so I have no chance, to do my work in the main thread.

The crash occurs in the line with the runModal() and then in "NSSavePanel._spAuxiliaryStorage":

0x7fff84dfec20:  movq   -0x10c18197(%rip), %rsi   ; "_refreshDelegateOptions"
0x7fff84dfec27:  movq   %rbx, %rdi
0x7fff84dfec2a:  callq  *%r15
0x7fff84dfec2d:  movq   -0x10c17ed4(%rip), %rsi   ; "_loadPreviousModeAndLayout"
0x7fff84dfec34:  movq   %rbx, %rdi
0x7fff84dfec37:  callq  *%r15
0x7fff84dfec3a:  movq   -0x10b57079(%rip), %r12   ; NSSavePanel._spAuxiliaryStorage <--- Thread 7: signal SIGABRT
0x7fff84dfec41:  movq   (%rbx,%r12), %rax
0x7fff84dfec45:  movq   -0x10b5716c(%rip), %rcx   ; NSSavePanelAuxiliary._clientSetADirectory
0x7fff84dfec4c:  movb   (%rax,%rcx), %al
0x7fff84dfec4f:  shrb   $0x2, %al
0x7fff84dfec52:  andb   $0x1, %al
0x7fff84dfec54:  xorb   $0x1, %al
0x7fff84dfec56:  movzbl %al, %ecx
0x7fff84dfec59:  movq   -0x10c18310(%rip), %rsi   ; "_configureForDirectory:forceDefault:"
0x7fff84dfec60:  movq   %rbx, %rdi
0x7fff84dfec63:  xorl   %edx, %edx
0x7fff84dfec65:  callq  *%r15
0x7fff84dfec68:  movq   -0x10c2d767(%rip), %rsi   ; "drain"
0x7fff84dfec6f:  movq   %r14, %rdi
0x7fff84dfec72:  callq  *%r15
0x7fff84dfec75:  movq   (%rbx,%r12), %rsi

In the Terminal Window is shown:

CocoaTest(54483,0x106f78000) malloc: *** error for object 0x60800017efc0: Heap corruption detected, free list canary is damaged

Any idea how to solve this problem without doing it inside the main thread?

Floern
  • 33,559
  • 24
  • 104
  • 119
j.s.com
  • 1,422
  • 14
  • 24
  • on which line does it crash? could you add a stacktrace? did you make sure to call it on main thread? is your app sandboxes and / or signed? – mahal tertin Aug 25 '14 at 06:53

2 Answers2

4

The restrictions of the User Interface (UI) Calls, which are not Thread-Save, can be solved, when you use the following code, which executes a block of commands in the main thread asynchronously:

dispatch_async(dispatch_get_main_queue())
{
    // This commands are executed ansychronously
}

So you have to write your own functions for every built-in-function, which is not thread-save like this (example with the open file dialog):

func not (b: Bool) -> Bool
{
    return (!b)
}

func suspendprocess (t: Double)
{
    var secs: Int = Int(abs(t))
    var nanosecs: Int = Int(frac(abs(t)) * 1000000000)
    var time = timespec(tv_sec: secs, tv_nsec: nanosecs)
    let result = nanosleep(&time, nil)
}

func openfiledialog (windowTitle: String, message: String, filetypelist: String) -> String
{
    var path: String = ""
    var finished: Bool = false

    suspendprocess (0.02) // Wait 20 ms., enough time to do screen updates regarding to the background job, which calls this function
    dispatch_async(dispatch_get_main_queue())
    {
        var myFiledialog: NSOpenPanel = NSOpenPanel()
        var fileTypeArray: [String] = filetypelist.componentsSeparatedByString(",")

        myFiledialog.prompt = "Open"
        myFiledialog.worksWhenModal = true
        myFiledialog.allowsMultipleSelection = false
        myFiledialog.canChooseDirectories = false
        myFiledialog.resolvesAliases = true
        myFiledialog.title = windowTitle
        myFiledialog.message = message
        myFiledialog.allowedFileTypes = fileTypeArray

        let void = myFiledialog.runModal()

        var chosenfile = myFiledialog.URL // Pathname of the file

        if (chosenfile != nil)
        {
            path = chosenfile!.absoluteString!
        }
        finished = true
    }

    while not(finished)
    {
        suspendprocess (0.001) // Wait 1 ms., loop until main thread finished
    }

    return (path)
}

Please note, that the block is called asynchronously, that means you have to check, if the block has been processed and is finished or not. So I add a boolean variable "finsihed" which shows, when the block reaches its end. Without this you do not get the pathname but only an empty string.

If you are interested, I will post my savefiledialog-function, too. Please leave a comment if so.

neoneye
  • 50,398
  • 25
  • 166
  • 151
j.s.com
  • 1,422
  • 14
  • 24
  • 1
    What exactly is "frac"? Currently, Swift 1.2 cannot resolve that identifier. – Charlweed Apr 21 '15 at 21:23
  • 1
    frac gives the fractional part of a real. This is "var - Int(var)". I currently use Xcode 6.3.1 and it compiles wll. You have to import Cocoa, perhaps this is your problem. Frac is part of the standard math, which has not been changed for this function for long. – j.s.com Apr 23 '15 at 07:42
0

Most of the User Interface (UI) Calls are not Thread-Save, that means that they only work without crash and unusual behaviour in the main-thread.

This is the problem with NSOpenPanel Calls, too. When calling from the main-thread everything is OK.

j.s.com
  • 1,422
  • 14
  • 24