objective-c - readInBackgroundAndNotify 方法在 NSTask 完成之前不会更新

标签 objective-c cocoa nsnotifications nsnotificationcenter nstask

我正在尝试在后台线程中运行 NSTask 并使用 readInBackgroundAndNotify 在附加到我的窗口(首选项 Pane )的 NSPanel 上的 NSTextview 中显示其输出 我似乎没有收到通知,因为应该调用的方法没有收到。

我有 Controller 类 (PreferencePane.m) 初始化负责运行 NSTask 的类 (Inventory.m)

- (IBAction)updateInventoryButtonPressed:(id)sender
{
    inventory = [[Inventory alloc] init];
....

然后我向它发送一个 NSNotification 来启动后台(来自 PreferencePane.m):

....
[[NSNotificationCenter defaultCenter]
 postNotificationName:NotificationInventoryRequested
 object:self];
}

此类 (Inventory.m) 在其初始化覆盖中是此常量 (NotificationInventoryRequested) 的观察者

 - (id)init
{   
        [super init];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(inventoryRequested:) 
                                                 name:NotificationInventoryRequested    
                                               object:nil];
    return self;

}

这会运行(Inventory.m 的)inventoryRequested 方法

-(void)inventoryRequested:(NSNotification*)aNotification
{
    if (inventoryIsRunning) {
        NSLog(@"Inventory is already running, ignoring request");
    }
    else {
        NSLog(@"Starting Inventory in background...");
        [NSThread detachNewThreadSelector:@selector(runInventoryTask)
                                 toTarget:self
                               withObject:nil];
}

}

这运行我的 NSTask 方法,我已经根据示例重构了几次

设置一个 BOOL 以帮助重复运行的完整性由 inventoryRequested 使用 ivar inventoryIsRunning 处理

    -(void)runInventoryTask
  {
    inventoryIsRunning = YES;
 ....

我仔细检查我的任务并设置 readInBackgroundAndNotify 将我自己添加为观察者。

 ....
if (task) {
NSLog(@"Found existing task...releasing");
    [task release];
}
task = [[NSTask alloc] init];
NSLog(@"Setting up pipe");
[task setStandardOutput: [NSPipe pipe]];
[task setStandardError: [task standardOutput]];
// Setup our arguments
[task setLaunchPath:@"/usr/bin/local/inventory"];
[task setArguments:[NSArray arrayWithObjects:@"--force",
                    nil]];
//Said to help with Xcode now showing logs
//[task setStandardInput:[NSPipe pipe]];

[self performSelectorOnMainThread:@selector(addLogText:)
                       withObject:@"Launching Task..."
                    waitUntilDone:false];

[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(readPipe:) 
                                             name: NSFileHandleReadCompletionNotification 
                                           object: [[task standardOutput] fileHandleForReading]];

[[[task standardOutput] fileHandleForReading] readInBackgroundAndNotify];
[task launch];
[task waitUntilExit];
// Let Any Observers know we are finished with Inventory
[[NSNotificationCenter defaultCenter]
 postNotificationName:NotificationInventoryComplete
 object:self];
inventoryIsRunning = NO;
}

这一切似乎运行良好。但是这个方法永远不会被调用(即我没有在控制台中看到窗口更新或 NSLog ):

-(void)readPipe:(NSNotification *)notification
{
NSData *data;
NSString *text;
NSLog(@"Read Pipe was called");

data = [[notification userInfo] 
        objectForKey:NSFileHandleNotificationDataItem];
if ([data length]){
    text = [[NSString alloc] initWithData:data 
                                 encoding:NSASCIIStringEncoding];
    // Update the text in our text view

    [self performSelectorOnMainThread:@selector(addLogText:)
                           withObject:text
                        waitUntilDone:false];
    NSLog(@"%@",text);
    [text release];

}
[[notification object] readInBackgroundAndNotify];


}

这一切似乎运行良好。但是这个方法永远不会被调用(即我没有在控制台中看到窗口更新或 NSLog )。我看到这个 thread并认为可能是我的 NSPanel 阻塞了运行循环,所以我将其设置为非模态。我还记得读过关于 NSNotification 不是同步的,所以我想也许是因为 NSNotification 正在调用我的方法,为了测试我只是很快地做了这个:

- (IBAction)updateInventoryButtonPressed:(id)sender
{

inventory = [[Inventory alloc] init];

/*[[NSNotificationCenter defaultCenter]
 postNotificationName:NotificationInventoryRequested
 object:self];*/
[inventory inventoryRequested:self];
[self showPanel:sender];

显然 self 在那里无效,但它向我展示了即使直接调用此方法似乎也无济于事(因此让我认为这与 NSNotification“阻塞”无关。)

关于我遗漏的任何想法,我已经在我的代码中的任何地方检查了 removeObserver(我知道我需要将它添加到 dealloc 并且可能在 readPipe 中:当命令运行完成时)。如果这里有帮助,那就是需要工作的小 NSTextview 包装器,因为我现在不在字符串中执行\n 行。

库存.h

//NSTextview
IBOutlet NSTextView  *      inventoryTextView;

库存.m

-(void)addLogText:(NSString *)text
{
NSRange myRange = NSMakeRange([[inventoryTextView textStorage] length], 0);
[[inventoryTextView textStorage] replaceCharactersInRange:myRange                                                          withString:text];
}

任何对此的帮助也将不胜感激,因为它是我的下一个绊脚石。

更新: 看起来正在调用此 readData 方法,但是在 NSTask 完成之前它不会更新我的 Textview,所以我遇到了流量控制问题。

最佳答案

我可以通过添加以下内容来使它正常工作

NSDictionary *defaultEnvironment = [[NSProcessInfo processInfo] environment];
NSMutableDictionary *environment = [[NSMutableDictionary alloc] initWithDictionary:defaultEnvironment];
[environment setObject:@"YES" forKey:@"NSUnbufferedIO"];
[task setEnvironment:environment];

这阻止了通知只向我发送缓冲区(在我的例子中是我的所有输出一次)。

关于objective-c - readInBackgroundAndNotify 方法在 NSTask 完成之前不会更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8251010/

相关文章:

iPhone CFSocket 传入/传出消息

objective-c - 检测 macOS 中无响应的窗口

iphone - Cocoa XML-RPC 框架 XMLRPCConnection 方法

objective-c - 发布两个 NSNotifications 时的执行顺序

macos - 基于 VLCKit 的应用程序阻止屏幕保护程序

objective-c - 无论如何,有没有像线程那样调用NSNotificationQueue?

objective-c - 如何使 XCode 4 的逻辑单元测试从 Nib 正确初始化 UIViewController?

ios - 如何按数据大小缩小 IOS 中的 UIImage

ios - UIPrintPageRenderer 是否有相当于 "renderInContext"的值

objective-c - NSTextField 像 safari 地址栏