How can I easily redirect the console output in a NSTextView? - objective-c

Suppose you have the code of a Cocoa app which logs its own messages through NSlogs and printfs to the Console output. My goal is to redirect all this output into a separate NSWindow in a NSView.
How can I achieve this in a way that
minimizes the amount of code to rewrite
makes it possible to revert back
maximizes the reuse of written code
(the usual software engineering guide lines)?

Soon after your program starts use the dup(2) call to make duplicate File Descriptors for fd 1 (stdout) and 2 (stderr). Save the returned values for later.
Call close(2) on FD 1 and FD 2.
Call openpty(2) twice. The first master returned should be FD 1 (because it is the first avaiable FD), and the second master should be 2. Save the two slave FDs for later. Don't worry about saving the name parameter. Now whenever your program printf(2) to stdout, or NSLogs to stderr the data will be written to your slave FDs.
Now you must choose wether you want to poll the slave FDs or setup a signal when there is data to be read.
For polling use an NSTimer. In your timer use select(2) on your two slave FDs to see if they have data. If they do read(2) it and then output it to your window.
You can also get the two slave FDs to use non blocking IO (use fcntl(2) to F_SETFL the slave FDs to O_NONBLOCK). Then you don't need select(2), you just read(2) and it will return zero if there is nothing to read.
For signaling, use fcntl(2) to F_SETFL the slave FDs to O_ASYNC. Then use signal(3) to install a signal handler for SIGIO. When your signal handler is called use one of the two methods I describe in the polling section.
If at run time you want to discard all these changes and set everything back to normal do this:
Call close(2) on FD 1, and FD 2.
Call dup(2) on the two FDs saved from step 1 in the first section, above. Do the dup(2) in the correct order so stdout uses FD 1 and stderr uses FD 2.
Now anything written to stdout and stderr will go to the original FDs.

How about writing your own log method that informs some controller of the log message (so it can place it into your (NSTextView?) view, then calls NSLog() in turn?

Here is the solution I came up with:
Create an NSWindowController that has a NSTextView as an outlet (let's call this A)
Create a Singleton class (let's call this B) that encapsulates an A object and provides some methods for sending strings to A (which appends it to its NSTextView) by reading a file (which contains all the loggings) using readInBackgroundAndNotify from NSFileHandle. When notified it calls the appending method. It has a method for starting the logging on file as well, which uses freopen(3) to redirect some stream (stderr and stdout atm) to a file in append mode.
In the project just call the starting logging method of B (no needs of instantiation, but I guess it really does not matter) after importing it.
This solution was created considering both Joshua Nozzi's answer and tlindner's one, and combines them. I have and encapsulated solution that respects the three requests in the question (I have to add only a line of code, I can revert back easily and I can use this solution in other apps too). I noticed that maybe sometimes it can be wrong to have an NSWindowController encapsulated this way (whereas all the other ones are managed by some super-controller).
I finally opted for the file solution since it is very easy to implement and more Cocoa-like than tlindner's one. Also it gives the opportunity to have a logging file that persists on the disk. But of course I may have missed something, point that to me in the comments please ^^

Related

RTC : How to handle variants at the component level

i am trying to work out a method to have component level variants in RTC.
Requirement is that there should be say at least two lines of development for the component. If there is an bug-fix on the parent line, i need to merge that change into the second line. I could not bring up yet a method using the streams. Any suggestions?
2 lines of developments means two stream.
You can easily add, in the flow target of the repo workspace, another stream.
I would recommend adding the parent stream to the flow target of repo workspace on the second stream.
That means you would accept changes coming from the parent stream (each time you set that stream in the flow target section as the "current" stream).
Once you have accepted those changes (and merged them in your local workspace or sandbox), you set again your second (and "default") stream as the current one, and are ready to deliver the change set you just accepted back to the second stream.
See an illustration in the section "How do I use the "new" method to accept from an integration stream instead of delivering to it?":

Best multithreading approach in Objective C?

I'm developing an iPad-app and I'm currently struggling with finding the best approach to multithreading. Let me illustrate this with a simplified example:
I have a view with 2 subviews, a directory picker and a gallery with thumbnails of all the images in the selected directory. Since 'downloading' and generating these thumbnails can take quite a while I need multithreading so the interaction and updating of the view doesn't get blocked.
This is what I already tried:
[self performSelectorInBackground:#selector(displayThumbnails:) withObject:currentFolder];
This worked fine because the users interactions didn't get blocked, however it miserably fails when the user taps on another folder while the first folder is still loading. Two threads are trying to access the same view and variables which results in messing up each others proper execution. When the users taps another folder, the displayThumbnails of the currently loading folder should get aborted. I didn't find any way to do this..
NSThreads
I tried this but struggled with almost the same problems as with the first method, I didn't find a (easy) way to cancel the ongoing method. (Yes, I know about [aThread cancel] but didn't find a way to 'resume' the thread). Maybe I should subclass NSThread and implement my own isRunning etc methods? But isn't there any better way or a third (or even fourth and fifth) option I'm overlooking?
I think this is a fairly simple example and I think there is perhaps a better solution without subclassing NSThread. So, what would you do? Your opinions please!
NSOperationQueue should work well for this task.
Another option would be plain GCD, however, if you've never worked with it, NSOperationQueue is probably the better choice since it pretty much automatically guides you to implementing things "the right way", has obvious ways for cancellation, etc.
You want to use Concurrent NSOperations to download and process images in the background. These would be managed by an NSOperationsQueue. Essentially these operations would be configured to fetch one image per operation, process it, save it in the file system, then message back to the main app in the main thread that the image is available.
There are several projects on github that you can look at that show how to do this - just search github using "Concurrent" or "NSOperation".
iOS has a really nice facility for doing background work. Grand Central Dispatch (GCD) and Blocks, but those don't let you have an object using delegate callbacks - thus NSOperation.
So you need to read up on blocks, GCD, and then look at some open source Concurrent NSOperations code. Using Concurrent NSOperations is not as simple as using blocks.
If I had this problem, I would probably go for an approach like this:
a single thread that will load the images, and causes the main thread to display results (I'm not a big fan of having thread mess around with GUI objects)
when a new directory is requested... well, it depends on how you want to manage things. Basically, a standard queue construct (condition variable and array) could be used for the main thread to tell the thread that "this directory will be needed" by passing it the path name; the thread will check the queue even when it's loading images (like after every image or so), and switch to the new directory whenever one shows up
you could make a directory-reader object that keeps all the state, and store this indexed by the path into a dictionary. When a new directory is requested, check that dictionary first, and only create a new object if there's none for this directory. That way, partially loaded directories would stick around until they are needed again, and can continue to load instead of having to start from scratch.
Pseudocode for the thread:
while (forever)
new element = nil
if we have an active directory loader
tell directory loader to load one image
if false then make directory loader inactive
lock queue condition
if queue has elements
new element = retrieve LAST element (we aren't interested in the others)
empty queue
unlock with status "empty"
else
unlock queue
else
lock queue on condition "has elements"
new element = retrieve last element
empty queue
unlock with status "empty"
if new element != nil
if directory loader for new path does not exist
setup new directory loader for new path
store in dictionary
make it the "active" one
else
make the current one the "active"
As for the directory loader, it might look something like this:
read one image:
if there are still images to read:
read, process and store one
return true
else
performSelectorOnMainThread with an "update GUI" method and the image list as parameter
return false;
This is just a quick sketch; there's some code duplication in the thread, and the way I wrote it will only update the GUI after all images have been read, instead of making them appear as we read them. You'll have to copy the current image list, or add synchronization if you want to do that.

Obj-C, how can I log to a file how long a method took in seconds?

I envisage I'll run into problems as i haven't done this before.
I'm thinking that I can either define a date at the start of the method or initialise a class.
Then at the end of the method, call the commit method, which will write the time taken about with some sort of code to determine where the measurement was made.
Since you're crashing before the app finishes launching, so no code is going to fix this. If TestFlightApp isn't working, any other code-based solutions are likely to have the same problem.
As #dasblinkenlight noted, NSLog timestamps, so that's a really easy first step. Then you need to get the logs.
If possible, have your user install and run the iPhone Configuration Utility. Have her connect her device and select it from the Devices list. Then select Console and "Save Console As..." She can then mail it to you.

Is a file available to be opened?

Short version: I think I'm asking for a file too soon, but it's pretending like it's ready. Am I missing something?
Slightly longer version: I am writing files to disk. Before I do so, I have the user add some meta data, including the new file name. Once the user is done, the screen goes away and the program writes the file to disk. The user can then look at a list of files. That list is generated by reading the contents of a folder. The new file is in the list of files, but when I try to extract info from the file to display (e.g. file size) the program crashes. As best as I can tell, the crash occurs because, while the file is there in name, it's not available to be read. (By the way, these are small files - a few hundred k.)
First, is it possible that a file shows up in the directory but isn't all there yet?
a
And second, if so, how do I check to see if the file is ready to be read?
Thanks much.
UPDATE:
Thanks. I'll try to add more info. I'm recording an audio file with AVAudioRecorder. The init line is:
soundrecording = [[AVAudioRecorder alloc] initWithURL:url settings:recordSettings error:&error];
The program goes through it's UI updates and metering and all that. When the audio is stopped, I call:
[soundrecording stop];
and when everything else is updated and ready to move on, I call:
[soundrecording release];
soundrecording=NULL;
As far as I understand, this should take care of releasing the file, yes?
Thanks again.
The first thing I would do is confirm that you're right about the file not being ready yet. To do that, sleep your program for a second or two after writing and before reading. A few hundred KB should not take longer than that to be ready.
If it still fails, my guess is that you haven't closed the file handle that you used to write it. It may be unready for reading because the file system thinks you might keep writing.
Usually, the way to check to see if a file is ready is to attempt to open it. If that succeeds, you can read it. Or if it fails with an error, you can handle the error gracefully:
In a command-line utility, you might print the error and quit, and the user could try again.
If it's a background program that should not quit, like a server, you could log the error. You might also try again automatically after a delay. If it's a big deal kind of error, you might want to have the program email you about it.
In an GUI window app, you probably want to show an error dialog or panel, and then give the user an opportunity to retry.
Now that you have added sample code, I can say some more.
First, the class reference seems to say that the stop method will close the file. However it also seems to suggest that there is an underlying audio session going on, and possibly some conversion. I think I recall that the iPhone's Voice Notes app, which probably uses this API, has to do some work to compress a long recording after it's completed.
So I support your hunch. I think that your file may not be closed yet, but on another thread that is processing the recorded data into a proper format to save.
You probably want to set a NSTimer to attempt to open the file every second or so, so that your user interface can perk up when it's done. You probably want to show a "Please wait" sort of message in the meantime, or otherwise let the user know it's working.

Notifications in wxWidgets?

I'm working on a small application using C++/wxWidgets, where several parts of the GUI need to be updated based on e.g. received UDP datagrams. More specifically, a secondary thread tries to keep a list of available "clients" in the network (which may come and go away) and e.g. corresponding comboboxes in the UI need to be updated to reflect the changes.
The documentation mentions that for this kind of thing EVT_UPDATE_UI would be a good choice. As far as I can understand from the sparse documentation, this event is sent automatically by the system and provides some support for assisted UI change.
However, I'd feel more comfortable using a more direct approach, i.e. where e.g. a window object could register/subscribe to receive notifications (either events or callbacks) upon particular events and another part of the code is sending out these notifications when required. I could do this in C++ using my own code, however I guess if wxWidgets already supports something like that, I should make use of it. However I haven't found anything in that regards.
So, the question is: does wxWidgets support this kind of notification system (or similar alternatives) or would I be best served coding my own?
AFAIK there is nothing directly usable in wxWidgets, but doing it on your own seems easy.
What I would do:
Create a wxEvtHandler-descendent class to hold the list of available "clients" in the network. Let this class have a wxCriticalSection, and use a wxCriticalSectionLocker for that in all methods that add or delete "clients".
Create a worker thread class by inheriting wxThread to handle your UDP datagrams, using blocking calls. The thread should directly call methods of the client list object whenever a client has to be added or removed. In these methods update the list of clients, and ::wxPostEvent() an event to itself (this will execute the whole notification calls in the main GUI thread).
Handle the event in the client list class, and notify all listeners that the list of clients has changed. The observer pattern seems to me a good fit. You could either call a method of all registered listeners directly, or send a wxCommandEvent to them.
Have you tried calling Update() on the widget(s) that change? Once you update the contents of the combo box, call Update(), and the contents should update.