【问题标题】:Run NSRunLoop in a Cocoa command-line program在 Cocoa 命令行程序中运行 NSRunLoop
【发布时间】:2010-01-28 12:57:01
【问题描述】:

是否可以在不加载任何 NIB 文件的情况下初始化 NSRunLoop(即不调用 NSApplicationMain())?

谢谢。

【问题讨论】:

  • 为什么要在 CLI 应用程序中使用运行循环?
  • 为什么要在 cli 应用中加载 nib 文件?
  • bertolami:我认为他的意思是没有
  • @KennyTM:可能想要这样做的一个原因是使用诸如 CoreLocation(以及其他各种功能)之类的功能,它需要运行循环才能运行,但本质上没有任何 GUI 需求。
  • @KennyTM - 他可能想做一些事情,比如在main 中创建一个NSURLConnection。无需任何特殊处理,由于调用了委托,他可以在程序真正完成之前退出main

标签: objective-c cocoa


【解决方案1】:

解决方案是手动调用 NSApplication。首先创建您的应用委托,然后将 main.m 中的 NSApplicationMain() 调用替换为以下内容:

AppDelegate * delegate = [[AppDelegate alloc] init];

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

NSApplication * application = [NSApplication sharedApplication];
[application setDelegate:delegate];
[NSApp run];

[pool drain];

[delegate release];

委托将在准备就绪时被调用,无需 nib

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification

【讨论】:

  • 没有理由一定需要使用 NSApplication(因此引入了对 AppKit 的依赖);这一切都可以通过 NSRunLoop 来完成。因此,虽然这对某人来说可能是有用的信息,但我认为这是对这个问题的不恰当答案。出于这个原因投反对票。
  • 我正在使用 [[NSRunLoop currentRunLoop] run];在我的无 GUI 应用程序中,但我的应用程序进入无响应状态。这种方法对我来说效果很好。
  • 对于 ARC,删除 [delegate release]、[pool drain] 和 NSAutoreleasePool,并在所有代码周围放置 @autoreleasepool { }。
【解决方案2】:

在 Swift 中,您可以通过将以下行附加到 main.swift 的末尾来实现此目的:

NSRunLoop.currentRunLoop().run();  // Swift < 3.0
RunLoop.current.run();             // Swift >= 3.0

如果您希望能够停止运行循环,您必须使用 Core Foundation 方法。

CFRunLoopRun(); // start

你可以这样阻止它

CFRunLoopStop(CFRunLoopGetCurrent()); // stop

【讨论】:

  • 这太棒了……不过,现在我不知道如何停下来。你对我有什么建议吗?
【解决方案3】:

是的;您可以编写自己的 main 方法并运行 NSRunLoop 而无需从 NSApplicationMain 返回。

看看这个link;这家伙在他的主要方法中使用 NSRunLoop,虽然他没有加载 NIB 文件,但它应该让你继续使用NSRunloops

【讨论】:

【解决方案4】:
// 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;
}

【讨论】:

    【解决方案5】:

    遵循 [NSRunLoop 运行] 文档中的建议:

    BOOL shouldKeepRunning = YES;        // global
    NSRunLoop *theRL = [NSRunLoop currentRunLoop];
    while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate     distantFuture]]);
    

    【讨论】:

    • 需要更多解释 - 当我尝试它时,while() 永远不会在结束日期之前退出(并且当它设置为远距离未来时 - 它永远不会检查 shouldKeepRunning)。我试图从运行循环中删除所有内容,但无济于事。 CFRunnLoopStop() 崩溃,并且......好吧......需要更多解释。这没有按预期工作。
    • 嗨,您是否在某个时间点设置了 shouldKeepRunning = NO 在您的 runloop 中安排的某些内容?在进入这个 while 循环之前,您必须在 runloop 上安排一些事情。祝你好运!
    【解决方案6】:

    查看手动运行 NSRunLoop 的 asynctask.m 启用异步“waitForDataInBackgroundAndNotify”通知。

    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];
    

    【讨论】:

      【解决方案7】:

      这是我仅使用 DispatchQueue 的结果。大多数命令行工具都希望以状态退出。后台调度队列是并发的。

      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()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-02-17
        • 1970-01-01
        • 2015-01-01
        • 2015-03-05
        • 1970-01-01
        相关资源
        最近更新 更多