aci*_*ime 3 cocoa objective-c nstask nsnotifications nsnotificationcenter
我正在尝试在后台线程中运行NSTask并在NSTextview中显示其输出,该NSTextview使用readInBackgroundAndNotify附加到我的窗口(Preference Pane)上的NSPanel我似乎不接收通知作为应该调用的方法不是.
我有控制器类(PreferencePane.m)初始化负责运行NSTask的类(Inventory.m)
- (IBAction)updateInventoryButtonPressed:(id)sender
{
inventory = [[Inventory alloc] init];
....
Run Code Online (Sandbox Code Playgroud)
然后我发送一个NSNotification来启动后台(来自PreferencePane.m):
....
[[NSNotificationCenter defaultCenter]
postNotificationName:NotificationInventoryRequested
object:self];
}
Run Code Online (Sandbox Code Playgroud)
此类(Inventory.m)是其init重写中此常量(NotificationInventoryRequested)的观察者
- (id)init
{
[super init];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(inventoryRequested:)
name:NotificationInventoryRequested
object:nil];
return self;
}
Run Code Online (Sandbox Code Playgroud)
这将运行inventoryRequested方法(Inventory.m)
-(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];
}
}
Run Code Online (Sandbox Code Playgroud)
这运行我的NSTask方法,我已经从示例中重构了几次
设置BOOL以帮助重复运行完整性由inventoryRequested使用ivar inventoryIsRunning处理
-(void)runInventoryTask
{
inventoryIsRunning = YES;
....
Run Code Online (Sandbox Code Playgroud)
我仔细检查我的任务并设置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;
}
Run Code Online (Sandbox Code Playgroud)
这一切似乎都运行良好.但是这个方法永远不会被调用(即我没有看到窗口更新或控制台中的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];
}
Run Code Online (Sandbox Code Playgroud)
这一切似乎都运行良好.但是这个方法永远不会被调用(即我没有看到窗口更新或控制台中的NSLog).我看到了这个帖子,想到也许是我的NSPanel阻止了运行循环,所以我把它设置为非模态.我还记得读过有关NSNotification不同步的内容,所以我想也许是因为我正在通过NSNotification调用我的方法来测试我只是快速做到这一点:
- (IBAction)updateInventoryButtonPressed:(id)sender
{
inventory = [[Inventory alloc] init];
/*[[NSNotificationCenter defaultCenter]
postNotificationName:NotificationInventoryRequested
object:self];*/
[inventory inventoryRequested:self];
[self showPanel:sender];
Run Code Online (Sandbox Code Playgroud)
显然自我在那里是无效的,但它告诉我,即使直接调用这种方法似乎没有帮助(因此让我认为这不是关于NSNotification"阻塞".)
关于我缺少的任何想法,我已经在我的代码中的任何地方检查了removeObserver(我知道我需要将它添加到dealloc并且可能在readPipe中:当命令运行完成时).如果它有帮助,那么需要工作的小NSTextview包装器,因为我现在不在字符串中行.
Inventory.h
//NSTextview
IBOutlet NSTextView * inventoryTextView;
Run Code Online (Sandbox Code Playgroud)
Inventory.m
-(void)addLogText:(NSString *)text
{
NSRange myRange = NSMakeRange([[inventoryTextView textStorage] length], 0);
[[inventoryTextView textStorage] replaceCharactersInRange:myRange withString:text];
}
Run Code Online (Sandbox Code Playgroud)
任何有关这方面的帮助也将是我的下一个绊脚石.
更新:看起来这个readData方法正在被调用,但是在NSTask完成之前它没有更新我的Textview,所以我遇到了流量控制问题.
通过添加以下内容,我能够实现这一目标
NSDictionary *defaultEnvironment = [[NSProcessInfo processInfo] environment];
NSMutableDictionary *environment = [[NSMutableDictionary alloc] initWithDictionary:defaultEnvironment];
[environment setObject:@"YES" forKey:@"NSUnbufferedIO"];
[task setEnvironment:environment];
Run Code Online (Sandbox Code Playgroud)
这停止了通知只发送缓冲区(在我的情况下我的所有输出都是立即).
| 归档时间: |
|
| 查看次数: |
2550 次 |
| 最近记录: |