Подтвердить что ты не робот

Выход в режиме реального времени NSTask

У меня есть PHP script, который имеет mutliple sleep() команды. Я хотел бы выполнить его в своем приложении с помощью NSTask. Мой script выглядит следующим образом:

echo "first\n"; sleep(1); echo "second\n"; sleep(1); echo "third\n";

Я могу выполнить свою задачу асинхронно с помощью уведомлений:

- (void)awakeFromNib {
    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath: @"/usr/bin/php"];

    NSArray *arguments;
    arguments = [NSArray arrayWithObjects: @"-r", @"echo \"first\n\"; sleep(1); echo \"second\n\"; sleep(1); echo \"third\n\";", nil];
    [task setArguments: arguments];

    NSPipe *p = [NSPipe pipe];
    [task setStandardOutput:p];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskExited:) name:NSTaskDidTerminateNotification object:task];

    [task launch];

}

- (void)taskExited:(NSNotification *)notif {
    NSTask *task = [notif object];
    NSData *data = [[[task standardOutput] fileHandleForReading] readDataToEndOfFile];
    NSString *str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
    NSLog(@"%@",str);
}

Мой вывод (через 2 секунды, конечно):

2011-08-03 20:45:19.474 MyApp[3737:903] first
second
third

Мой вопрос: как я могу получить эти три слова сразу после их печати?

4b9b3361

Ответ 1

Вы можете использовать метод NSFileHandle waitForDataInBackgroundAndNotify для получения уведомления, когда script записывает данные на свой вывод. Это будет работать, однако, если интерпретатор немедленно отправит строки. Если он выводит буферы, вы получите одно уведомление после выхода задачи.

- (void)awakeFromNib {
    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath: @"/usr/bin/php"];

    NSArray *arguments;
    arguments = [NSArray arrayWithObjects: @"-r", @"echo \"first\n\"; sleep(1); echo \"second\n\"; sleep(1); echo \"third\n\";", nil];
    [task setArguments: arguments];

    NSPipe *p = [NSPipe pipe];
    [task setStandardOutput:p];
    NSFileHandle *fh = [p fileHandleForReading];
    [fh waitForDataInBackgroundAndNotify];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedData:) name:NSFileHandleDataAvailableNotification object:fh];

    [task launch];

}

- (void)receivedData:(NSNotification *)notif {
    NSFileHandle *fh = [notif object];
    NSData *data = [fh availableData];
    if (data.length > 0) { // if data is found, re-register for more data (and print)
        [fh waitForDataInBackgroundAndNotify];
        NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"%@" ,str);
    }
}

Ответ 2

Для справки, вот ответ ughoavgfhw в swift.

override func awakeFromNib() {
    // Setup the task
    let task = NSTask()
    task.launchPath = "/usr/bin/php"
    task.arguments = ["-r", "echo \"first\n\"; sleep(1); echo \"second\n\"; sleep(1); echo \"third\n\";"]

    // Pipe the standard out to an NSPipe, and set it to notify us when it gets data
    let pipe = NSPipe()
    task.standardOutput = pipe
    let fh = pipe.fileHandleForReading
    fh.waitForDataInBackgroundAndNotify()

    // Set up the observer function
    let notificationCenter = NSNotificationCenter.defaultCenter()
    notificationCenter.addObserver(self, selector: "receivedData:", name: NSFileHandleDataAvailableNotification, object: nil)

    // You can also set a function to fire after the task terminates
    task.terminationHandler = {task -> Void in
           // Handle the task ending here
    }

    task.launch()
}

func receivedData(notif : NSNotification) {
    // Unpack the FileHandle from the notification
    let fh:NSFileHandle = notif.object as NSFileHandle
    // Get the data from the FileHandle
    let data = fh.availableData
    // Only deal with the data if it actually exists
    if data.length > 1 {
    // Since we just got the notification from fh, we must tell it to notify us again when it gets more data
        fh.waitForDataInBackgroundAndNotify()
        // Convert the data into a string
        let string = NSString(data: data, encoding: NSASCIIStringEncoding)
        println(string!)
    }
}

Эта конструкция будет необходима, если ваша задача производит много вывода в трубе. Просто вызов pipe.fileHandleForReading.readDataToEndOfFile() не будет работать, потому что задача ожидает, что канал будет опустошен, чтобы он мог писать больше, пока ваша программа ждет окончания данных. Таким образом, ваша программа будет зависать. Эта конструкция уведомления и наблюдателя позволяет считывать трубу асинхронно и, таким образом, предотвращает вышеупомянутый тупик.