开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜