image
image

Go Back   macosx.com > Design, Media, Programming & Scripting > Software Programming & Web Scripting

Reply
 
LinkBack Thread Tools
  #1  
Old July 9th, 2001, 08:03 AM
rharder's Avatar
Do not read this sign.
 
Join Date: Mar 2001
Location: Virginia, USA
Posts: 1,188
Thanks: 0
Thanked 2 Times in 2 Posts
rharder is on a distinguished road
Lightbulb Better way to read from an NSTask

An earlier thread had information on using NSTimer-s to read from an NSPipe attached to an NSTask. This required regular polling and reading from the NSPipe which seems like kind of a silly way to do it. Apparently there's a better way, and boy am I glad it's there. I'll share it with you.

Let's assume you have some controller class that's going to start up the NSTask and do something with the NSPipe you read. Maybe you're just going to display the results in an NSTextView that you created in Project Builder.

In the controller's .h file, let's declare some variables:
Code:
NSTask       *_task;
NSFileHandle *_fileHandle;
Of course you would have put the _task in there anyway, right, but we want to have access to the _fileHandle that will become very important soon.

As always we launch the NSTask:
Code:
NSPipe *pipe = [NSPipe pipe];

_fileHandle = [pipe fileHandleForReading];
[_fileHandle readInBackgroundAndNotify];

_task = [[NSTask alloc] init];
[_task setLaunchPath:@"/bin/ls"];
[_task setStandardOutput: pipe];
[_task setStandardError: pipe];
// arguments if you want 'em
[_task launch];
Now you'll be notified when the file handle (and thus the pipe attached to the task) has data to read. Oh wait, no you won't. We have to register with the NSNotificationCenter.

Make up an init method like this:
Code:
-(id)init
{
    [super init];
    [[NSNotificationCenter defaultCenter] addObserver:self
        selector:@selector( readPipe: )
        name:NSFileHandleReadCompletionNotification 
        object:nil];
    return self;
}
Of course you may need to add your own stuff too. Don't forget to unregister when you dealloc:
Code:
-(void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
So what does our readPipe method look like? This:
Code:
-(void)readPipe: (NSNotification *)notification
{
    NSData *data;
    NSString *text;

    if( [notification object] != _fileHandle )
        return;

    data = [[notification userInfo] 
            objectForKey:NSFileHandleNotificationDataItem];
    text = [[NSString alloc] initWithData:data 
            encoding:NSASCIIStringEncoding];

    // Do something with your text
    // ...

    [text release];
    if( _task )
        [_fileHandle readInBackgroundAndNotify];
}
Note that at the end we have to tell the file handle again that we want to be notified of changes, but we check to make sure that the _task isn't nil (assuming we did that somewhere).

Now isn't active notification much better than polling?

Let me know, y'all, if I have any errors.

-Rob
__________________
There are only 10 kinds of people in the world:
Those who understand binary, and those who don't.

Last edited by rharder; July 9th, 2001 at 08:19 AM.
Reply With Quote
  #2  
Old July 17th, 2001, 09:05 PM
Darkshadow's Avatar
wandering shadow
 
Join Date: Jul 2001
Location: DE, USA
Posts: 1,532
Thanks: 0
Thanked 0 Times in 0 Posts
Darkshadow is on a distinguished road
Thumbs up

Thank you! I have been looking for <i>exactly</i> this! I've been having to wait until the task has finished before seeing the results. This is much much better!

Now onto my next mountain: opening a png file that's being written to and displaying it on the fly. Sounds like fun
Reply With Quote
  #3  
Old July 18th, 2001, 07:06 AM
rharder's Avatar
Do not read this sign.
 
Join Date: Mar 2001
Location: Virginia, USA
Posts: 1,188
Thanks: 0
Thanked 2 Times in 2 Posts
rharder is on a distinguished road
That would be a trick. I wonder if you could 'tail' a binary file.

Oh, and one of the reasons that polling or waiting until the end of an NSTask is a bad idea is that apparently there's only an 8K or so buffer in the pipe somewhere, and you could lose data otherwise. I think this technique should get around that. Maybe.

Good luck.

-Rob
__________________
There are only 10 kinds of people in the world:
Those who understand binary, and those who don't.
Reply With Quote
  #4  
Old July 19th, 2001, 10:17 AM
Unperson Spotter
 
Join Date: Mar 2001
Posts: 275
Thanks: 0
Thanked 0 Times in 0 Posts
ladavacm is on a distinguished road
Quote:
Originally posted by rharder
That would be a trick. I wonder if you could 'tail' a binary file.

Oh, and one of the reasons that polling or waiting until the end of an NSTask is a bad idea is that apparently there's only an 8K or so buffer in the pipe somewhere, and you could lose data otherwise. I think this technique should get around that. Maybe.

Good luck.

-Rob
There is 8KB buffer in pipe kernel implementation. However, that buffer does not lose data; the writing application blocks on write when the buffer is full. So, unless you are reading from the pipe being written to, the writing application will not finish if it has more than 8K to write.
Reply With Quote
  #5  
Old July 19th, 2001, 02:36 PM
rharder's Avatar
Do not read this sign.
 
Join Date: Mar 2001
Location: Virginia, USA
Posts: 1,188
Thanks: 0
Thanked 2 Times in 2 Posts
rharder is on a distinguished road
Oh, good. So even though it may be less efficient, you could still just use NSTimers (if you really really wanted to) to read data instead of the "active" method mentioned above.

-Rob
__________________
There are only 10 kinds of people in the world:
Those who understand binary, and those who don't.
Reply With Quote
  #6  
Old July 21st, 2001, 08:58 PM
Darkshadow's Avatar
wandering shadow
 
Join Date: Jul 2001
Location: DE, USA
Posts: 1,532
Thanks: 0
Thanked 0 Times in 0 Posts
Darkshadow is on a distinguished road
Rob, I found a limitation in this...sorta kinda. I was wondering if you could help me out.

This works great for input/output (whichever you want to call it) that's not too long, but if the data from the pipe takes longer to display than it takes for the task to finish executing, the data stops displaying. I'm guessing this is from the if(task).... line. I know this is the reason the text gets cut in the display (I'm using an NSTextView to display the results) because sometimes the task won't end (or my app isn't getting that it ended), and all the data gets displayed. (BTW, if anyone out there is having a problem with tasks not finishing, I found a strange workaround. Not in the code, but in the running app. If you click on one of the menus, which won't open, then click on the desktop, then click back on your window, the app will get that the task has finished...for whatever reason. This is most likely a bug in my code, but I'd thought I'd share in case someone else was seeing this).

Anyway, I was wondering if you could help me think of a way to get the last bit of data from the pipe displayed. I tried adding a [_fileHandle readInBackgroundAndNotify] to the task finished part (BTW, I have two observers added in init - seems to work fine, but I'm new to Cocoa, and so I'm asking if that's alright? One for catching the data from the pipe, and one for catching when the task quits). That didn't work. I tried making an else after the if (task) line, but that didn't really work either...I had set up an NSTimer to fire a few times and then had it do [_fileHandle readInBackgroundAndNotify], but it didn't get anything else.

Any thoughts to what may be happening?
Reply With Quote
  #7  
Old July 23rd, 2001, 11:03 AM
rharder's Avatar
Do not read this sign.
 
Join Date: Mar 2001
Location: Virginia, USA
Posts: 1,188
Thanks: 0
Thanked 2 Times in 2 Posts
rharder is on a distinguished road
So, when there's a lot of output, some of the last bit of data doesn't get read?

And you tried reading after the Task Finished notification too?

Weird.

I had something weird like that when I 'tail'ed the system log, but since that task never finished, I never quite had the problem you describe.

I did discover that I had to manually scroll the NSTextView a few times to "reset" the viewing.

I wonder if some of this comes from threading issues between the task and the main event-dispatching thread.

I haven't worried about it too much, figuring it might just be little bugs that will get worked out or explained in a release note some time.

-Rob
__________________
There are only 10 kinds of people in the world:
Those who understand binary, and those who don't.
Reply With Quote
  #8  
Old July 31st, 2001, 09:41 PM
Registered User
 
Join Date: Jul 2001
Posts: 2
Thanks: 0
Thanked 0 Times in 0 Posts
jterry94 is on a distinguished road
Get all the output

To get all of the output

change the If statement to look at the data structure instead of the program.


ie.


If( data != 0 )

[_fileHandle readInBackgroundAndNotify];



This will allow the remainder of the pipe to be read after the unix program finishes executing.

Reply With Quote
Reply

Bookmarks

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are Off
Pingbacks are Off
Refbacks are Off

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
Looking for Software: Bookmarks to be read by both Windows and Mac? Sogni Mac OS X System & Mac Software 0 August 29th, 2003 05:28 PM
DVR-104 won't read or write DVD-R's Bad Co Hardware & Peripherals 1 March 14th, 2003 06:29 PM
Won’t read DVD-video discs, and slow CD read speed koim Hardware & Peripherals 1 March 11th, 2003 02:38 PM
Is there a way to know if your recipient read the email you sent them? chemistry_geek Mac OS X System & Mac Software 7 October 3rd, 2002 01:49 PM


All times are GMT -5. The time now is 02:41 PM.


Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2009, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.3.0 RC1
Copyright 2000-2010 DigitalCrowd, Inc.