0

I am dealing with an infuriating memory leak in regards to loading an interstitial ad via AdMob. When observing the memory in Xcode & Instruments, the memory jumps 10 MB every time I visit the view controller hosting the ad. Also when closing the app on my phone and reopening it, causes the memory to jump 30-40 mb as well which is just ridiculous.

I have tried profiling this in Instruments and the memory being allocated are all system libraries and nothing that points out to what is wrong. I have read other Stack Overflow answers such as ADMOB Memory Leaking? but no answer has helped me so far. Maybe somebody can tell me what is wrong with my code? I have followed the exact documentation AdMob provides which is https://developers.google.com/admob/ios/interstitial and all works fine except this crazy memory leak. Here is the exact code that is causing the leak below.


class ViewController: UIViewController, GADInterstitialDelegate {

var interstitial: GADInterstitial!

override func viewDidLoad() {
  interstitial = createAndLoadInterstitial()
  interstitial.delegate = self
}

func update() {
        if interstitial.isReady {
            interstitial.present(fromRootViewController: self)
        }
    }

    func createAndLoadInterstitial() -> GADInterstitial {
        let interstitial = GADInterstitial(adUnitID: "ca-app-pub-3940256099942544/4411468910")
        interstitial.delegate = self
        let request = GADRequest()
        interstitial.load(request)
        return interstitial
    }

    func interstitialDidDismissScreen(_ ad: GADInterstitial) {
        interstitial = createAndLoadInterstitial()
    }

// I am calling update() inside a button when pressed in this VC.
  • Maybe capturing the memory graph in Xcode may help. It lists all objects, that are currently allocated. You visit the ad hosting view controller, hide it again and then see, whether there are objects that are still in memory, even though they should not be there. – Palle Apr 26 '19 at 23:11
  • Yea I recall that but do you think there is anything I did that seems out of place? There is no other line in my view controller that is contributing to the memory spike. I muted block by block until I was left with the ad stuff and they were the culprit. I’m just struggling on why this is occurring unless it’s a secondary object like you said but I don’t see much – Paul Purser Apr 26 '19 at 23:23
  • it's possible GAD is creating the memory leak for you by holding on a strong reference to the delegate. Try implementing the delegate on a struct. If it compiles they are not holding a weak reference to your VC and you just created a retain cycle. In my experience, google does a pretty shitty job with some of their older libraries. Edit: I just checked and your VC needs to extend NSObject therefore using it on a struct shouldn't compile. But they still might hold a strong reference. – Jacob Apr 26 '19 at 23:39
  • also see if your deinit is being triggered when you close the screen. – Jacob Apr 26 '19 at 23:40
  • If you find that there is a retain cycle, you can just add a layer between your VC and GAD. This layer will hold a weak reference to your VC and a strong reference to GAD. this way, you eliminate the circular reference on the VC. Then on the deinit of your VC, nullify the GAD delegate on this middle layer to remove the circular reference there. the nice thing with this (and I do this sometimes) you can use it on an MVVM or Viper architecture and then your class that would otherwise be the GAD delegate doesn't need to conform to NSObject. I know I said a lot so feel free to ask more questions. – Jacob Apr 26 '19 at 23:52
  • Kubee thank you for the kind comments. Can you give me an example of adding a layer between my VC & GAD or what you said in that last comment? I did not mention before, but I tried to nullify my ``interstitial`` variable above in ``viewDidDissapear`` but sadly that did not do the job if that was somewhat relevant to what you said above. – Paul Purser Apr 26 '19 at 23:56
  • I said a lot but I would first figure out if GAD is creating a circular reference...just test your deinit. Check if adding or removing `interstitial.delegate = self` has any affect on the deinit being called after calling pop or dismiss. By the way, if your deinit should always be called after you pop or dismiss your VC. – Jacob Apr 27 '19 at 00:01
  • Ok so this is the error I got in deinit with the ``interstitial.delegate = self`` line. ``It is possible that this object was over-released, or is in the process of deallocation.`` – Paul Purser Apr 27 '19 at 00:05
  • I cannot see any direct issue in your code. But in my apps, I used a separate class as a singleton to manage all the AdMob ads(let say AdManager) both interstitial and banner. When called, this AdManager will open an interstitial or reward ad on the topmost ViewController. And all the objects are managed properly. You can try something like that. It will also help you to add ads to any project easily. – RJE Apr 27 '19 at 03:37
  • @RJE, I did exactly what you said. Placed my ad stuff in my app delegate and the memory goes up but it does come down when I leave the VC hosting the ad. When I exit/reopen the app, the memory goes down but jumps back up. The change helped but the memory has very bizarre spikes if the user closes the app and reopens it while I observe. And of course, all of this is in the app delegate. Can you show me an example of what you did so I can modify what I placed in the app delegate? That would be great if you can – Paul Purser Apr 27 '19 at 05:31

2 Answers2

0

I just want to say that this actually has been solved by me. I did as one of the above stated in the comments. I took my code that was on the view controller hosting the interstitial ad and moved it to the app delegate. I simply then just referenced the interstitial ad object whenever I need it in my project. This brought the memory back down to whatever it allocated upon visiting the controller hosting the ad. For those that want to see a very simple example of what this looks like in the app delegate:

import UIKit
import GoogleMobileAds

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GADInterstitialDelegate {

    var myInterstitial: GADInterstitial!

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        GADMobileAds.sharedInstance().start(completionHandler: nil)
        myInterstitial = createAndLoadInterstitial()
        return true
    }

    func createAndLoadInterstitial() -> GADInterstitial {
        let interstitial = GADInterstitial(adUnitID: "yourAdID")
        interstitial.delegate = self
        let request = GADRequest()
        interstitial.load(request)
        return interstitial
    }

    func interstitialDidDismissScreen(_ ad: GADInterstitial) {
        myInterstitial = createAndLoadInterstitial()
    }
0

I also had this issue and turns out I just needed a pod update.

In version 7.53.0 the update included 'Fixed the GADBlockSignalSource memory leak that occurred when loading ads' which may relate to the interstitial memory leak issue we were experiencing.

Release notes: https://developers.google.com/admob/ios/rel-notes