Debugging Memory Leaks in Swift’s Enumerations Data Type

January 11, 2018 by Cem Kozinoglu

Background

We have been working on developing the GIPHY Core SDK for the open source community & 3rd party developers. So, if they’re developing an iOS or macOS app, they can easily integrate GIPHY search, trending topics, trending GIFs, Stickers, etc. directly into their apps without needing to re-invent the wheel. We decided to use Swift 3 as our main programming language.

The Problem

To make sure we became a consumer of our own product and battle tested it, we dogfooded the GIPHY Core SDK into our own flagship product, GIPHY Search (https://itunes.apple.com/us/app/giphy-the-gif-search-engine-for-all-the-gifs/id974748812?mt=8) Just like with any other development lifecycle, we started to profile and see how the GIPHY Core SDK was performing inside the GIPHY Search app, and to see if there were any memory leaks. We were caught off-guard as things were not working the way they were supposed to. We ran into thousands of memory leaks!

Debugging time!

So we started debugging, trying to figure out what we potentially did wrong in the GIPHY Core SDK to see if it was caused by our code. It turns out, we couldn’t fix it. We exhausted all our options, rewriting code, involving our peers, changing data models. Gene even did a work around to convert Enums into Objects which stopped the leaking. However it was still clear that there were something going on with the compiler and we wanted to get to the bottom of it.

AHA Moment!

While we were keen on trying to figure out what was going on, I ran into a question in the StackOverflow community which had similarities to what we were experiencing:

https://stackoverflow.com/questions/42602301/swift-3-enums-leak-memory-when-the-class-contains-an-array

We created an public open-sourced GitHub repo (https://github.com/GIPHY/ios-memory-leak-sample) and with a few modifications to the original StackOverflow problem, voila! We were able to consistently reproduce the memory leak, and even managed to find a workaround.

How to Reproduce the Leak

The issue was, when a Swift class contains an optional Enum property that is marked with @objc, and this same class also contains a mutable (var) array holding at least one element, there is an unexpected memory leak.

Here is an example:

Gist Embed

// Without @objc this enum won't leak
// however when this enum is included in a class
// which contains an array, it will leak
@objcenumleakingObjCMarkedEnum: Int{
    // Just some random cases.
    caseapple, orange
}
// Wrapper class which contains an enum and Array
// The class needs to contain the the Array in order for
// the Enum to leak.
classWrapperClass { 
  // Optional enums marked with @objc will leak.
  var leakyOptionalEnum: leakingObjCMarkedEnum?
  
  // Include an array to trigger this behaviour.
  // Empty arrays won't cause the leak, so lets add an arbitrary Int
  var myArray: [Int] = [80]
}

classViewController: UIViewController {
  // Hang on to a reference to our Wrapper Class instance.
  var wc: WrapperClass?

  overridefuncviewDidLoad() {
    super.viewDidLoad()
    
    // Allocate an instance of our class
    // and things will start leaking at this point.
    wc =WrapperClass()
  }
}

How to Prevent the Leak

If we convert the ‘leakOptionalEnum’ optional var to a non-optional var the leak disappears.

// Let's convert the optional property to a non-optional
var leakOptionalEnum: leakingObjCMarkedEnum = .apple

Command + I -> Leaks Instrument -> Record

Conclusion

We filed a Radar to the Apple/Swift community (https://bugs.swift.org/browse/SR-5625) and in less than 10 hours, a member of the community took care of the bug and merged it into the Swift repo (https://github.com/apple/swift/pull/11341) & Xcode Version 9.0 beta 6 (9M214v).

The bug fix, as implemented by community member @slavapestov: since objective c enumerations “lower as their raw type”, they should go through the same code path as imported enumerations.  This change applies to how the xcode compiler handles metadata related to these types of Enumerations when compiling projects that are hybrids of Swift and Objective C.

In conclusion, it was an awesome experience to see how quickly the Swift Open Source community is and we are excited to be a part of this journey with them.

— Cem Kozinoglu

via GIPHY