I have a multi-threaded Cocoa app that processes images. The program has a progress bar and some text showing how far along the process is. This all works great. However, sometimes the interface will just freeze up and everything will stop updating. The progress bar stops moving, and the text stops updating in the percentage counter. However, the actual process is still working! I have an NSBeep() that fires when the program is finished, and I still hear that even though the UI is no longer being updated. It's as though the UI just disconnects from the code.
Another weird problem is that sometimes the NSOpenPanel that opens when the user wants to choose a file will just be a white void. I declared the NSOpenPanel (I'm using [NSOpenPanel openPanel]) in the main thread, which helps (I tried retaining it at first, but that didn't work). I don't know if this is related, but any ideas would be appreciated!
I am using Xcode 3.1.1 (GCC 4.2) on Mac OS X 10.5.8.
You're trying to manipulate the UI from other threads. As stated in the docs, AppKit is generally not thread-safe and you should interact with your GUI on the main thread. See -performSelectorOnMainThread:withObject:waitUntilDone:.
Related
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.
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
Does anyone have any insights into when/under what conditions applicationWillTerminate is called in iOS 5/6?
I've got some logic i'd like to execute whenever the application terminates (not moves to the background), for example if the user navigates to the application bar at the bottom of the screen by double tapping the home button and force quits the app.
when i try to do this on a test device, applicationWillTerminate does not seem to get called. Is there a reason for this?
My plan B is to tie that logic to some persistent object like a singleton or a static that is automatically destroyed when the app quits.
Any suggestions?
thanks
Have you read the documentation for applicationWillTerminate:,
It says,
For applications that do not support background execution or are linked against iOS 3.x or earlier, this method is always called when the user quits the application. For applications that support background execution, this method is generally not called when the user quits the application because the application simply moves to the background in that case. However, this method may be called in situations where the application is running in the background (not suspended) and the system needs to terminate it for some reason.
There is a "maybe" mentioned there. Probably that answers your question. So it is not necessary that this will get called when you quit the app. Probably you might have to use UIApplicationExitsOnSuspend to disable multitasking and then it might get called while putting in background. But that again depends on your app requirement. If you cannot disable multitasking, you might have consider doing that in applicationDidEnterBackground method or so. I am not sure if there are any other delegate methods which will help in identifying the force quit.
So I'm trying to convert a project to ARC. The first attempt, I just converted everything and I had this problem on one of my views, it just hangs. I cannot click on any UI element, and nothing is printed to the console, and it doesn't crash. It just sits there.
So in order to start troubleshooting it, I converted all the simple classes and viewControllers, and then for some of the more complex model classes and UIViewController classes, I set the compiler flag for -fno-objc-arc. My app runs better, but it still just gets in this state where it hangs. I've never seen this on it prior to converting to ARC. I was wondering if anyone else has had this problem and what I can do to troubleshoot it. Thanks~
I would press "pause" in the debugger and look at the call stacks for all threads in your app. This can point out why your app is locked up.
It may sound as a dumb question. In fact, I am still thinking if that might be the cause but working with XCode and having set iOS 4.3 as my Deployment Target and iPad as my Device I am getting an unexpected error.
While running my app through the Simulator I can get it working. But when I run it through my iPad a single IBAction, fired when user taps an UIButton, that takes almost 4 minutes is not completing. In fact it's getting stopped at the same point, in a for loop.
I searched for memory leaks using XCode and it didn't find any, therefore I'm asking if there's a time limit for IBActions. I read there are 10 minutes limit for methods in background but I didn't find anything related to IBActions in foreground yet.
I think the user gets tired of waiting for a response long before the system. Normally, a button press or similar should finish after one second at most (well, maybe 1.5s), or the user thinks that the system is "slow".
I would make sure that you have your button outlets properly hooked up. It is possible that it just isn't throwing the exception so you get a slow system. Put NSLog displays in your IBAction method and make sure it is executing it properly. You should be able to track it back to figure out what is going on.