Is there a function for "processing messages", i.e. updating screen, responding to accumulated user input - objective-c

I have encountered a strange bug in my application, and I am trying to debug it using step execution.
However it seems that things on an iphone often do not happen as synchronously as I would like, for example when I step-over this line
[self.view addSubview:FinndomoEmbeddedMWView.view];
nothing happens in the emulator.
If I just let the program run, the view is added and the screen changes as it should.
So I am guessing, addSubview does not do everything related to adding a view, it just sort of starts the process, and then it is completed later.
I don't know if there are message queues on ios similair to winapi, but there must be something like that, so is there a function for "processing all accumulated messages". I would then like to call this function after my addSubview and actually see the things change in the emulator while I debug, not when the program is running.
And I experience this not only with addSubview, so I want to have a general solution for things like this.

Sat yesterday and found out the answer:
NSDate *dtr = [[NSDate alloc] initWithTimeIntervalSinceNow:0.5];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:dtr];
This is sort of what I wanted. As you can see, this particular code may end up waiting for 0.5 seconds if there are no more events, and if there are events pending, it is only processing the first one and then returns (according to documentation).
But that could be avoided of course, this is just an example, if anyone will want the same thing. In my case there was only 1 important event, and so the provided snippet worked.

The short answer is no.
Cocoa is event-driven.
The core of each app is the event loop. On each pass through the event loop, the app handles events added to the event queue. Updating the screen is one such event.
Thus, changes to screen display don't take place until after your code returns, on the next pass through the event loop.
For debugging purposes where you want to figure out what's happening line-by-line, you need to either use the debugger's facilities, or add NSLog statements to your code.

Related

Modal NSSavePanel disappears after animating in when begun from a completion block

G'day!
Note: Minimal example linked below. I'll refrain from longish code excerpts and rather explain the problem concisely.
I am in the process of updating an old (but small) Cocoa application to current APIs.
One of the places that looked easy enough at first: When the user tries to close the application window with unsaved changes, the app first displays an NSAlert asking "Save your stuff?". If that is confirmed a modal NSSavePanel is shown. In the original code they were opened via, respectively:
beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:
beginSheetForDirectory:file:modalForWindow:modalDelegate:didEndSelector:contextInfo:
Current Cocoa API uses completion blocks and thus the alert prefers to be shown via beginSheetModalForWindow:completionHandler:. So I moved the code from the didEndSelector into the completionHandler.
Unfortunately the modal NSSavePanel does animate in but disappears immediately together with the application main window if it is shown from the NSAlert's completion block. If I switch the alert back to the didEndSelector I can show the NSSavePanel either selector-basedly or completion block-ly just fine.
Here's the NSAlert's completion block that forwards to the disappearing save panel.
I have thought about threading issues. All of this is happening on the main thread. Maybe there's something subtle going on with run loop modes that I'm missing?
The minimal example is available over on GitHub. You can switch between selectors and blocks with defines in AppDelegate.h. All the interesting code is in AppDelegate.m. (Unless the problem is somewhere else...)
As #Willeke pointed out this wasn't an overly mysterious issue with threading and whatnot. No. It was just me having looked at the code way too often over the course of days.
The solution is simple:
The breaks in the switch statement in confirmUnsavedChanges are missing.

OSX 10.10 App doesn't get focus or event loop

Restarting app development after a 15 year hiatus. Current project is conversion of old windows-type command line utility into interactive OS X windowed app.
I created a view delegate inside main window and can draw and update NSTable view.
The updates are generated in the App's main loop which takes a UDP/TCP stream, parses and updates view via appropriate delegation.
Here's the problem: When I run the app, the main window does not apparently get
focus (window control buttons on upper left are grey), the Menu created from my .xib is inert, and the window itself does not respond to resizing or to mouse hits inside the table view scroll bars. Also, the mouse pointer is the spinning beachball when over the App's window.
My guess is that I am not providing time to the Objective C run loop for event processing. I do send a "display" to my window on every iteration of my app loop, but I guess it is not sufficient (Apple is not very clear about what objects get what messages when sending this kind of update message). Am I on the right track?
Is there a way to let the system Event loop run an iteration each time through my app main loop?
Thanks!
Update: I tried explicitly providing event loop time in my app's main loop with:
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
There was no apparent change in app behavior.
My guess is that I am not providing time to the Objective C run loop for event processing.
In this case, I recommend you read about Grand Central Dispatch, which provides concurrency, allowing the GUI to remain responsive.
There's a good explanation of GCD here and whilst it looks like a large and complicated subject, you'll probably only need a few lines of code to make use of GCD. For example: -
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// do some work
});
Despite comparing the nibs and connections between my code and a freshly created .xib prototype, i could find no reason for my code to not get focus.
The fix was to move my code guts into the project with a new .xib.
This works but is unsatisfying

UIPickerView "preemptive" messages?

I have a situation where my UIPickerView is getting "starved" by a computation task; in other words, the UIPickerView is never updated -- and hence, never sends messages -- because a very heavy compute task is happening. The picker controls aspects of the computation, so the two have to play nice.
I thought of running the computation in a separate thread. Seems like that would leave the picker free to update. However, it'd be a massive undertaking to make my computation multithread-able, so I'd like to find another solution.
Is it possible for a picker (or other UI controls) to "preempt" the execution of a block of code? The computation is in a loop; the number of iterations is what makes it heavy. If the picker could even set a flag somewhere, the loop could break itself, which would work with the flow of the program.
If the loop could poll the picker, that would also work. But, I haven't found a way to do that.
Ideas?
(ps. I posted a similar question yesterday, but didn't really ask it correctly -- didn't quite know what the problem was at that time!)
I assume you mean by flag that if the picker is moved, set a flag. If so you can do this - look at the picker delegates, and when any or all of them get called, set a flag. If your computation is done by another class or classes, then create a new property on those classes "cancelled", the picker can set it, and when set the computation ends.
Before you start another computation you would clear that cancel flag, then kick off the computation.
You can also put a simple toolbar above the picker (common practice) where you have controls that could start the computation, show progress, and cancel it.
EDIT: if the issue is the picker is stuttering when the user is trying to manipulate it, then subclass UIPicker, intercept touch events, and while the picker is being touched, cancel all computations. The only complication is that if the user "spins" the picker, you'd want to wait til it settles, but you would not know how long to wait. Depending on the last touch message, you would have to use a heuristic to wait for didSelectRow: or a timeout before restarting the computation.

iOS automatic threading?

I have been working on an opencv project for iOS. I was given a simple project to start developing with that captured and displayed frames. I never payed much attention to how it worked until I started having memory issues and traced them back to the original project setup. I now plan to re-write the capturing/displaying code but I don't understand why it worked in the first place. There was a play/pause button which called the method
- (IBAction)play_pause:(id)sender
{
play = !play;
while(play)
{
if (_videoCapture && _videoCapture->grab())
{
(*_videoCapture) >> _display_frame;
//process frame
self.imageView.image = [UIImage imageWithCVMat:_display_frame];
}
}
}
play is just a global bool that signifies whether the application is playing or paused. The strange thing is that the processing should be taking place inside an infinite loop, there is no way out. play is never modified within the loop. In spite of that, when the application is running the play/pause button remains responsive and is capable of flipping the play bool and pausing the execution. Not only that, other bools (use_greyscale for instance) can be flipped by other buttons and their values change inside the loop. I would have expected the application to freeze and never even draw new frames to the screen. The application should stay trapped inside that function for most of its lifetime, unable to perform other tasks such as drawing and UIControl. It seems as though the only way this is possible is if the IBAction call is running on its own thread. I cannot find any evidence of threading in the source code. Could someone explain how apple handles threading in its UI? I was under the impression that there was one main runloop thread and that extra threads were not created automatically. If that is true, how can this behavior be explained?
side note-
What finally made me investigate this was that [UIImage imageWithCVMat:_display_frame] returns an auto-released object. Since all this takes place inside a loop, the objects could not be released without the execution being paused which was causing crashes.
The reason is worked is because the implementation of the cv::VideoCapture::grab() method runs the current run loop to pause the thread until it gets a frame.
When you launch your application, the main function executes a function named UIApplicationMain, which executes CFRunLoopRun. When CFRunLoopRun is executed on the main thread, it runs the main run loop, which is the run loop that processes all the UI events received from the system and refresh the user interface. For information on run loops, you may read Apple Threading Programming Guide.
So, when you execute an infinite loop, your code never returns to the run loop and the waiting events cannot be processed. But in your case, the grab method runs the run loop again with an expiration delay. So the run loop may process incoming events (which may invoke your code again) until the delay expires, then return to your code that will run the run loop again.
If you look at the callstack when you touch the button to pause, you will see this:
main function → run loop → event handling → your code → OpenCV → run loop → event handling → your code
The run loop is running inside itself, which is perfectly fine because run loops are reentrant. Scroll views actually use that behavior: When you scroll a UIScrollView, it runs the run loop again in a different mode in order to ignore some events until you end scrolling.
But I'm not sure the developers of OpenCV had this in mind when they wrote their code. So I think it would be better to load your frames in a background thread/queue.
You are correct, there is no "automatic threading" in iOS. Grand Central Dispatch (GCD) certainly makes threading much easier, but it does not happen automatically.
You can write some debug code and test for [NSThread isMainThread] in the while loop to see if play_pause is indeed being run on the main UI thread, which I suspect it is not.

iPad app freezes during NSOperationQueue

I have a problem with an app where I implemented an NSOperationQueue.
It seems that pressing the close button of the iPad makes the UI freeze. The app itself is still running in the background, the UI is updated, it's just that it won't answer to touches or rotation anymore and it doesn't get closed as it should be.
I have an update module that downloads quite a long list of xml files and saves them on the device. The operation queue has a MaxConcurentOperations value of 2.
Usually everything works fine, the app runs fine and dandy, responding to my touches and rotation UNTIL I press the device's button. After this, the UI simply freezes. The progress is still updated (an UILabel), recurrent animations are still displayed, but the app is not closed until all the operations are done.
I'm not calling waitUntilAllOperationsAreFinished on my queue so I don't know what might be causing this.. So far, I've only made tests on a first generation iPad, with iOs 5.0
If anyone can provide me with some tips, I'll really appreciate it. If necessary, I can post the NSOperationQueue and NSOperation class codes, but somehow I have the feeling this about me approaching this wrongly, and not about a faulty line of code
[edit]
I also use a timer to periodically check on the download status, but I noticed that not calling the timer doesn't eliminate the problem
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(xmlDownloaded) userInfo:nil repeats:YES];
[edit2]
After some further research, I made sure that my operations are concurrent and just to be sure, I changed the way I added my operations to the queue.
Instead of
[downloadQueue addOperation:op];
I added them to a mutable array called "operations" and in the end I used
[downloadQueue addOperations:operations waitUntilFinished:NO];
but my app still freezes when I press the close button...
Wild guess, you are locking the main thread waiting for your operation to complete / you are destroying your operation delegate on viewWillDisappear?
It seems there was another function causing this, even though I don't understand why...
I had a label animation
CABasicAnimation *fadey = [CABasicAnimation animationWithKeyPath:#"opacity"];
[fadey setToValue:[NSNumber numberWithFloat:0.35f]];
fadey.repeatCount = HUGE_VALF;
[fadey setAutoreverses:YES];
[fadey setDuration:0.6f];
This small function was causing the app to wait until all the operations were finished in order to close the app. The weird part is that the function was not even called in with the NSOperations, but before them
Again, I have no idea why... everything breaks when I press the close button, otherwise there are no problem. So if anyone else runs into a similar issue, it might help to check for some repeating animations