Hard-to-Reproduce Bugs
Sometimes you encounter bugs that are very hard to reproduce. In this post we discuss the two most common reasons for this kind of bug.
Multithreading Issues
The number one reasons for these bugs are multithreading issues. They seem to be not deterministic, although they are. So one common reason for a multithreading bug is changing the user interface offside the main thread:
class ViewController: UIViewController { @IBOutlet weak var titleLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { () -> Void in // doing some concurrrent stuff... self.titleLabel.text = "Title" } } }
What happens is not predictable. Sometimes everything works fine, but the app could also crash or other wired things can happen. So if you are doing something concurrently, you have to switch back to the main thread before you are changing the user interface:
class ViewController: UIViewController { @IBOutlet weak var titleLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { () -> Void in // doing some concurrrent stuff... dispatch_async(dispatch_get_main_queue(), { () -> Void in self.titleLabel.text = "Title" }) } } }
Another problem arises if you are not waiting until some concurrent operation is finished:
class ViewController: UIViewController { @IBOutlet weak var titleLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() var result = 0 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { () -> Void in for var i = 0; i < 100000; i++ { result = i } } self.titleLabel.text = "result: \(result)" } }
Here the result is assigned to the label before the concurrent operation is finished. So always a different result is displayed. In my test I started the app five times and the output was 53366, 56967, 68647, 61328 and 66055. A correct implementation is the following:
class ViewController: UIViewController { @IBOutlet weak var titleLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() var result = 0 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { () -> Void in for var i = 0; i < 100000; i++ { result = i } dispatch_async(dispatch_get_main_queue(), { () -> Void in self.titleLabel.text = "result: \(result)" }) } } }
Memory Issues
If your app crashes randomly, it is very likely that there are memory issues. Take a look at the console to find out if this is really the case. As a next step, use Xcode’s debugging tools to investigate the memory issue. Take a look at the “Building Memory Efficient Apps” post for more details about this topic.
[thrive_text_block color=”blue” headline=”Conclusion”]Hard to reproduce bugs apps are very annoying. However, in many cases multithreading or memory issues are the reason. [/thrive_text_block]
References
Image: @ Shutter_M / shutterstock.com
Building Memory Efficient Apps