Open File Dialog вылетает в Swift
Я хотел бы использовать диалоги открытия файлов из NSFilemanager, но мой код иногда падает, а иногда работает, и я не знаю почему. Иногда это работает на 100%, иногда окно пустое, иногда фон за диалогом не отображается в окне. Когда происходит сбой, "сигнал:SIGABRT" отображается в 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 ("")
}
}
Что я сделал не так? Почему это терпит крах?
Приложение не запускается в главном потоке. Я всегда открываю новую ветку, в которой запускается моя программа. Основной поток обрабатывает только обновления экрана из SpriteKit, которые я использую для своих программ.
Я только что создал новое приложение на основе какао и позволил функции запускаться в главном потоке, и там все работает. Когда я запускаю поток в Какао-приложении, он падает, как в среде SpriteKit.
Мне нужно запустить новый поток в среде Sprite-Kit, потому что обновления не будут выполнены, если я начну свою основную программу прямо из AppDelegate. Основная программа работает до тех пор, пока не выйдет весь SpriteKit, поэтому у меня нет шансов выполнить свою работу в главном потоке.
Сбой происходит в строке с runModal(), а затем в "NSSavePanel._spAuxiliStorage":
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
В окне терминала отображается:
CocoaTest(54483,0x106f78000) malloc: *** error for object 0x60800017efc0: Heap corruption detected, free list canary is damaged
Любая идея, как решить эту проблему, не делая это в основном потоке?
2 ответа
Ограничения вызовов пользовательского интерфейса (UI), которые не являются Thread-Save, могут быть устранены при использовании следующего кода, который асинхронно выполняет блок команд в основном потоке:
dispatch_async(dispatch_get_main_queue())
{
// This commands are executed ansychronously
}
Таким образом, вы должны написать свои собственные функции для каждой встроенной функции, которая не является поточно-сохраняющей, как это (пример с диалогом открытия файла):
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)
}
Обратите внимание, что блок вызывается асинхронно, это означает, что вы должны проверить, был ли блок обработан и завершен или нет. Поэтому я добавляю логическую переменную "finsihed", которая показывает, когда блок достигает своего конца. Без этого вы не получите путь, а только пустую строку.
Если вам интересно, я также опубликую свою функцию savefiledialog. Пожалуйста, оставьте комментарий, если так.
Большинство вызовов пользовательского интерфейса не сохраняют потоки, это означает, что они работают только без сбоев и необычного поведения в основном потоке.
Это проблема и с вызовами NSOpenPanel. При звонке из основного потока все ок.