Run NSRunLoop in a Cocoa command-line program
Is it p开发者_运维问答ossible to initialize a NSRunLoop
without loading any NIB files (i.e., without calling NSApplicationMain()
)?
Thanks.
The solution is to invoke NSApplication manually. Create your app delegate first than replace the NSApplicationMain() call in main.m with the following:
AppDelegate * delegate = [[AppDelegate alloc] init];
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSApplication * application = [NSApplication sharedApplication];
[application setDelegate:delegate];
[NSApp run];
[pool drain];
[delegate release];
The delegate will be invoked when ready, without needing a nib
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
In Swift, you can achieve this by appending the following line to the end of your main.swift
:
NSRunLoop.currentRunLoop().run(); // Swift < 3.0
RunLoop.current.run(); // Swift >= 3.0
If you want to be able to stop the run loop you have to use the Core Foundation methods.
CFRunLoopRun(); // start
And you can stop it like this
CFRunLoopStop(CFRunLoopGetCurrent()); // stop
Yes; you can write your own main method and run NSRunLoop
without returning from NSApplicationMain
.
Have a look at this link; this guy is using NSRunLoop in his main method, he is not loading NIB files though, but it should get you going with NSRunloops
.
// Yes. Here is sample code (tested on OS X 10.8.4, command-line).
// Using ARC:
// $ cc -o timer timer.m -fobjc-arc -framework Foundation
// $ ./timer
//
#include <Foundation/Foundation.h>
@interface MyClass : NSObject
@property NSTimer *timer;
-(id)init;
-(void)onTick:(NSTimer *)aTimer;
@end
@implementation MyClass
-(id)init {
id newInstance = [super init];
if (newInstance) {
NSLog(@"Creating timer...");
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(onTick:)
userInfo:nil
repeats:YES];
}
return newInstance;
}
-(void)onTick:(NSTimer *)aTimer {
NSLog(@"Tick");
}
@end
int main() {
@autoreleasepool {
MyClass *obj = [[MyClass alloc] init];
[[NSRunLoop currentRunLoop] run];
}
return 0;
}
Follow the recommendations in the docs for [NSRunLoop run]:
BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
Have a look at asynctask.m that runs an NSRunLoop manually to enable the use of asynchronous "waitForDataInBackgroundAndNotify" notifications.
http://www.cocoadev.com/index.pl?NSPipe
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
while(!terminated)
{
//if (![[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:100000]])
if (![[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]])
{
break;
}
[pool release];
pool = [[NSAutoreleasePool alloc] init];
}
[pool release];
Here is my take using only DispatchQueue. Most command line tools want to exit with a status. The background dispatch queue is concurrent.
import Foundation
var exitStatus: Int32 = 0
let background = DispatchQueue(label: "commandline", qos: .userInteractive, attributes: [], autoreleaseFrequency: .workItem)
background.async {
var ii = 1
while ii < CommandLine.arguments.count {
process(file:CommandLine.arguments[ii])
ii += 1
}
}
background.async {
exit(exitStatus)
}
RunLoop.current.run()
I just wrote a CLI app and I needed to run a runLoop to let NSWorkspace class works right and also to get notifications from NSWorkspace. I made it thanks this discussion, but I don't understand why most of the answers suggest to use a NSTimer so I would like to share my solution without it.
Because [NSRunLoop run] "blocks" the thread and also my CLI logic blocks the thread by waiting for stdin, in the main function I create a thread for my CLI app logic, then I runned the NSRunLoop:
int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
pthread_t thread;
// current NSRunLoop passed to the thread
err = pthread_create(&thread, NULL, run_thread, runLoop);
if (err != 0) {
return err;
}
[runLoop run];
[pool release];
// I'm not able to stop the NSRunLoop even with CFRunLoopStop
// so you probably never reach this point.
// However, for safety, here I wrote code for handling pthread_join
return 0;
}
Then in my thread I do my CLI logic and, when I need to access to NSWorkspace API (that needs the NSRunLoop to work) I use the performBlock:
method on NSRunLoop:
void *run_thread(void *data) {
NSRunLoop *runLoop = (NSRunLoop *)data;
FILE *input = stdin;
FILE *output = stdout;
// my CLI code that read from stdio
// while (1) {
// fread( ... );
// ...
// then, when I need, I schedule code to the NSRunLoop
[runLoop performBlock:^{
// call API that need the main runLoop to work
// [[[NSWorkspace sharedWorkspace] notificationCenter] addObserverForName: ...];
// ...
}];
// to stop the runLoop so the application I call exit inside the runLoop
[runLoop performBlock:^{
exit(0);
}];
// ...
}
To be more accurate I decided to read data (input) from my thread and work with output in the runLoop so to not have concurrency on resources.
精彩评论