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?
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.
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.
来源:https://stackoverflow.com/questions/25473764/open-file-dialog-crashes-in-swift