问题
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.");
}
}
回答1:
Don't use waitUntilExit
.
The problem is how to do something after the task finishes without blocking the UI (or freezing that one button). The solution, as for all similar problems, is to be notified when the task finishes, and proceed further (show the alert) in response to that notification.
The notification, in this case, is an NSNotification named NSTaskDidTerminateNotification
. When the task exits, for any reason, the NSTask object will post this notification on the default NSNotificationCenter. You can ask the task what its termination status was to determine whether it succeeded, failed, or crashed.
See also: Notification Programming Topics.
回答2:
Have a look at AMShellWrapper which is based on Apple's Moriarity sample code.
"Connect your own methods to stdout and stderr, get notified on process termination and more."
See: http://www.cocoadev.com/index.pl?NSTask
回答3:
Don't use waitUntilExit
on your main thread. That will block your UI, and freeze your app.
You need to subscribe to the notification NSTaskDidTerminateNotification, which is posted when the task has stopped execution:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(taskDidTerminate:)
name:NSTaskDidTerminateNotification
object:nil];
Note that the notification can be posted if the task completed normally, or as the result of a terminate
message:
- (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.");
}
}
Don't forget to unsubscribe to the notification ; depending on where you subscribe to the notification, a good place would be in your dealloc
method:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Update: You expect something that is documented on Mac to work the same on iOS, where it is not documented. Not really surprised it doesn't work.
Have you tried to execute the task - and use the waitUntilExit
method - in a background thread?
If you are lucky and it works, don't forget to switch back to the main thread when showing your UIAlert.
来源:https://stackoverflow.com/questions/5139092/nstask-waituntilexit-hanging-app-on-jailbroken-ios