CFRunLoop в программе командной строки Swift
Я пишу приложение командной строки в Swift, используя стороннюю платформу, которая (если я правильно понимаю код) использует обратные вызовы GCD для выполнения определенных действий, когда сокет получает данные. Чтобы лучше понять фреймворк, я поэкспериментировал с примером приложения Cocoa, написанного автором фреймворка в дополнение к фреймворку.
Поскольку пример приложения является приложением Какао, циклы выполнения обрабатываются автоматически. Я включил фрагменты кода из примера приложения (лицензия MIT), чтобы дать представление о том, как это работает:
class AppDelegate: NSObject, NSApplicationDelegate {
var httpd : Connect!
func startServer() {
httpd = Connect()
.onLog {
[weak self] in // unowned makes this crash
self!.log($0)
}
.useQueue(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))
...
httpd.listen(1337)
}
...
func applicationDidFinishLaunching(aNotification: NSNotification?) {
startServer()
...
}
}
Я хотел бы изменить пример приложения для запуска из командной строки. Когда я помещаю функцию startServer() в приложение командной строки, она запускается, но сокет сразу же закрывается после его открытия, и программа завершает выполнение с кодом выхода 0. Это ожидаемое поведение, поскольку циклов выполнения нет в проекте командной строки Xcode, и, таким образом, программа не знает, ждать, пока сокет получит данные.
Я полагаю, что правильный способ заставить сокет оставаться открытым и непрерывно запускать программу - поместить основной поток в CFRunLoop. Я просмотрел документацию Apple, и, за исключением базовой справочной информации по API, в Swift нет ничего о многопоточности. Я посмотрел на сторонние ресурсы, но все они связаны с альтернативными потоками в приложениях iOS и Cocoa. Как правильно реализовать CFRunLoop для основного потока?
2 ответа
Кажется, что ответ Мартина Р. должен сработать, однако я смог заставить сокет оставаться открытым с помощью одного вызова функции. В конце функции startServer() я поставил строку:
CFRunLoopRun()
Который работал.
Ссылка класса NSRunLoop имеет пример для простого цикла выполнения:
BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
который можно перевести на Swift:
var shouldKeepRunning = true // global
let theRL = NSRunLoop.currentRunLoop()
while shouldKeepRunning && theRL.runMode(NSDefaultRunLoopMode, beforeDate: NSDate.distantFuture()) { }
В качестве альтернативы, может быть достаточно просто позвонить
dispatch_main()
Обновление для Swift 3.1:
let theRL = RunLoop.current
while shouldKeepRunning && theRL.run(mode: .defaultRunLoopMode, before: .distantFuture) { }
или же
dispatchMain()