Careful with iOS 4 (iPhone) multitasking and threads.
In updating my Farmácias de Serviço App for iOS 4.x and multitasking I ran into an interesting problem related to multithreading. Whenever the app starts, as well as getting the current list of ‘service’ (open 24hrs) pharmacies, it also updates its database of all the pharmacies in Portugal. That update of the full database can take quite a long time (a minute or two) and is kicked off in its own thread so as not to interfere with the running of the app.
Pre iOS 4.x
Before iOS 4.x, when 3rd party app multitasking wasn’t available, when the app was closed, the thread updating the full database was killed off along with the rest of the app. If the update had only got halfway, no problem, it will be kicked off again the next time the user launches the app. Sooner or later the user is likely to keep the app open for long enough that the full update completes.
Post iOS 4.x
Since iOS 4.x, 3rd party app multitasking is allowed. Unless you explicitly disable multitasking (setting the UIApplicationExitsOnSuspend key to YES in Info.plist), your app will continue ‘running’ in background when the user presses the Home button. I put the word running in quotes because most of the documentation seemed to imply that unless your app registered to do one of three allowed background tasks (audio, location or voip), it was effectively suspended until the user opened your app again. On that basis, I didn’t see the need to change my code regarding the update of the database. When the user re-opened the app, if the refresh hadn’t completed, it would be kicked off again in a new thread.
In the iPhone simulator running on my iMac (where the database update runs much quicker than on the iPhone) that didn’t seem to cause any problems. However, when I started testing on my iPhone 4, every time I re-opened my app it crashed with an EXC_BAD_ACCESS in the database update code. I just couldn’t understand what was going on, until I left a couple of breakpoints in the database update code and, to my huge surprise, I was hitting those breakpoints repeatedly even when the app was closed / suspended! I finally realized what was going on: if you start a separate, relatively long running, thread in iOS 4.x (either using old school NSThreads or the now recommended Grand Central Dispatch), when the user closes your app that thread will continue running and doing its thing, regardless of whether you use UIApplication’s beginBackgroundTaskWithExpirationHandler: or not.
So, what was causing my app to crash was that I had two threads accessing the sqlite3 database at the same time. Neither the sqlite3 code nor my code is thread safe, so that led to the two threads inevitably crapping over each other. BANG.
The solution was to keep a reference to the DB refresh thread when I started it and, when the app was re-opened, only kick off a new database update if there isn’t already one in progress. No more crashes.
Conclusion
Contrary to what the Apple documentation seems to imply, if you create a separate background thread in your app, if it hasn’t finished when your app is suspended, it will carry on doing its thing in background. It will not get killed off or suspended. So, think carefully how do deal with background threads you create when your app is suspended and then re-opened.
Wow! My first iPhone development related post!