Ghostboard pixel

Hard-to-Reproduce Bugs

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