2

So I've got NSTask to run a script which generates a list of something, into a txt, which I read from. But if I use my current code (below), the alert pops up before the NSTask is finished, thus resulting in a blank alert. I've tried waitUntilExit but that makes the button that invokes this action freeze, but the UI doesn't lock up itself.

- (void) runSupported {
    stask = [[NSTask alloc] init];
    [stask setLaunchPath:@"/bin/bash"];
    NSString *script;
    script = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/apps.sh"];
    NSArray *sargs = [NSArray arrayWithObjects:script, @"-txt", nil];
    [stask setArguments: sargs];
    [stask launch];

    NSString *apps;
    apps = [NSString stringWithContentsOfFile:@"/var/mobile/supported.txt" encoding:NSUTF8StringEncoding error:nil];
    NSFileManager *fm = [NSFileManager defaultManager];
    if ([fm fileExistsAtPath:apps]) {
        UIAlertView *supported = [[UIAlertView alloc] initWithTitle:@"App List" message:apps delegate:self cancelButtonTitle:@"Ok!" otherButtonTitles:nil];
        [supported show];
        [supported release];
    } else {
        UIAlertView *supported = [[UIAlertView alloc] initWithTitle:@"App List" message:@"Error generating list." delegate:self cancelButtonTitle:@"Ok!" otherButtonTitles:nil];
        [supported show];
        [supported release];
    }
}

Any idea how I'd have the NSTask finish before invoking the alert? Thanks.

Edit: Code with NSNotification:

-(IBAction) supported {
    stask = [[NSTask alloc] init];
    [stask setLaunchPath:@"/bin/bash"];
    NSString *script;
    script = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/apps.sh"];
    NSArray *sargs = [NSArray arrayWithObjects:script, @"-txt", nil];
    [stask setArguments: sargs];
    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector(taskEnded:)
                                                 name: NSTaskDidTerminateNotification
                                               object: nil]; 
    [stask launch];
}

- (void)taskEnded:(NSNotification *)notification {
    if (stask == [[notification object] terminationStatus]) {
        NSString *apps;
        apps = [NSString stringWithContentsOfFile:@"/var/mobile/supported.txt" encoding:NSUTF8StringEncoding error:nil];
        NSFileManager *fm = [NSFileManager defaultManager];
        if ([fm fileExistsAtPath:apps]) {
            UIAlertView *supported = [[UIAlertView alloc] initWithTitle:@"Apps" message:apps delegate:self cancelButtonTitle:@"Ok!" otherButtonTitles:nil];
            [supported show];
            [supported release];
        } else {
            UIAlertView *supported = [[UIAlertView alloc] initWithTitle:@"Apps" message:@"Error generating list." delegate:self cancelButtonTitle:@"Ok!" otherButtonTitles:nil];
            [supported show];
            [supported release];
        }
    } else {
        NSLog(@"Task failed."); 
    }
}
4

3 回答 3

1

不要使用waitUntilExit.

问题是如何在任务完成后做某事而不阻塞 UI(或冻结那个按钮)。对于所有类似问题,解决方案是在任务完成时收到通知,并根据该通知继续进行(显示警报)。

在这种情况下,通知是一个名为 的 NSNotification NSTaskDidTerminateNotification。当任务退出时,无论出于何种原因,NSTask 对象都会在默认的 NSNotificationCenter 上发布此通知。您可以询问任务的终止状态,以确定它是成功、失败还是崩溃。

另请参阅:通知编程主题

于 2011-02-28T07:50:34.683 回答
0

不要waitUntilExit在你的主线程上使用。这将阻止您的 UI,并冻结您的应用程序。

您需要订阅通知NSTaskDidTerminateNotification,该通知在任务停止执行时发布:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(taskDidTerminate:)
                                             name:NSTaskDidTerminateNotification
                                           object:nil];

请注意,如果任务正常完成,或者作为terminate消息的结果,可以发布通知:

- (void) taskDidTerminate:(NSNotification *)notification {
    if (YOUR_TASK_SUCCESS_VALUE == [[notification object] terminationStatus]) {
        NSLog(@"Task succeeded.");  
        // Here you can add your checks on the creation on the files and user alerts confirmation
    } else {
        NSLog(@"Task failed."); 
    }
}

不要忘记取消订阅通知;dealloc根据您订阅通知的位置,您的方法中会有一个好地方:

[[NSNotificationCenter defaultCenter] removeObserver:self];

更新:您希望 Mac 上记录的内容在未记录的 iOS 上也能正常工作。并不奇怪它不起作用。

您是否尝试过waitUntilExit在后台线程中执行任务并使用该方法?如果你很幸运并且它有效,在显示你的 UIAlert 时不要忘记切换回主线程。

于 2011-02-28T08:02:22.267 回答
0

查看基于 Apple 的 Moriarity 示例代码的 AMShellWrapper。

“将您自己的方法连接到标准输出和标准错误,获得进程终止通知等等。”

见:http ://www.cocoadev.com/index.pl?NSTask

于 2011-02-28T11:02:17.847 回答