Memory management

ChristerNordvikChristerNordvik NOMember
edited October 2012 in Xamarin.iOS

We are having two crash logs from our users that have the same error, namely operations on objects that have been GCed. I am having a hard time resolving the issue since it isn't possible to reproduce. We are not using the SGEN garbage collector since it has yet to be marked as stable. So basically my questions are:

  1. Are there any examples out there on how to use iAd on MonoTouch in a GC friendly manner? Our main bug report is due to an iAd banner that is shown on a details viewcontroller. We are keeping a reference to the banner as a class variable to avoid it being GCed, so really not sure on how to track this down. Maybe add the banner as a static variable would be possible, or is that a bad idea?

  2. Is SGEN ready for release soon and could that help in the situation above? We have a lot of users and I don't want to release a version with sgen if there are known issues with it :-)

The tutorials on this site are great, but it would really be nice on a tutorial on memory management. Like: How do you have events on buttons inside a UITableCell without the buttons being GCed?

Device info: iPod touch - Unknown - 6.0, timezone=0,
22:18:29: Selector invoked from objective-c on a managed object of type MonoTouch.iAd.ADBannerView+_ADBannerViewDelegate (0x54AE810) that has been GC'ed -  
 at MonoTouch.ObjCRuntime.Runtime.ConstructNSObject (IntPtr ptr, IntPtr klass) [0x00000] in <filename unknown>:0
 at MonoTouch.ObjCRuntime.Runtime.GetNSObject (IntPtr ptr) [0x00000] in <filename unknown>:0
 at MonoTouch.ObjCRuntime.Runtime.GetNSObjectWrapped (IntPtr ptr) [0x00000] in <filename unknown>:0
 at (wrapper native-to-managed) MonoTouch.ObjCRuntime.Runtime:GetNSObjectWrapped (intptr)
 at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x00000] in <filename unknown>:0
 at NDC2010.NDC2010Application.Main (System.String[] args) [0x00000] in <filename unknown>:0

Posts

  • MigueldeIcazaMigueldeIcaza USXamarin Team Xamurai

    It would be useful to look at the source code for this particular problem.

  • NicWiseNicWise NZMember, Insider, Beta mod

    The usual answer is to keep a reference around for it, but why not create and destroy the ad object (View) each time? So when you load the view that you want the ad to show on, you make a new one. When the view goes away, you kill the ad.

    (that may not be how ads work tho! I've not played with them for ages.)

    The other option is to maybe have a manager class, and you load it onto a View (eg, your detail view) by register it with the manager, and the manager keeps a reference to the view - and then de-registering when the view goes away. That way, you could also (eg if you are the top of your stack) tell the manager to dump all the views, remove the ad unit etc.

    But generally, this error is caused by the GC releasing the managed side of the object, the unmanaged side still being there. When it's re-activated, it tries to make a new managed one.

  • Here are the relevant parts of the code. I am toggling with an admob banner but it is only the iAd banner that throws exceptions. I saw a post on SO that indicated I had to explicitly release the iAd banner, but should I call dispose on it then when getting the ViewDidDisappear event?
    http://stackoverflow.com/questions/5961037/why-does-iad-still-load-data-after-it-has-been-removed-from-its-superview-and

        MyViewController: UIViewController{
        private ADBannerView iAdBannerView;
        void ViewDidLoad()
        {
                    //iAd Setup
                iAdBannerView = new ADBannerView (); 
                NSMutableSet nsM = new NSMutableSet (); 
                if(ADBannerView.SizeIdentifierPortrait != null)             
                    nsM.Add (new NSString (ADBannerView.SizeIdentifierPortrait));                               
                iAdBannerView.AdLoaded += new EventHandler (m_adBannerView_AdLoaded); 
                iAdBannerView.FailedToReceiveAd += new EventHandler<AdErrorEventArgs> (m_adBannerView_FailedToReceiveAd); 
                iAdBannerView.Frame = GuiUtils.GetFrameForAd (View);
                View.AddSubview (iAdBannerView); 
                iAdBannerView.Hidden = true;    
    
                //Admob setup
                GADAdSize gsize = new GADAdSize()
                {
                    flags = 0,
                    size = new System.Drawing.SizeF(320, 50)
                };
                admobBanner = new GADBannerView (gsize, GuiUtils.GetFrameForAd(View).Location);
                admobBanner.AdUnitID = "topsecret";
                admobBanner.RootViewController = this;
                admobBanner.BackgroundColor = UIColor.Clear;
                admobBanner.Hidden = true;
    
                admobBanner.DidFailToReceiveAdWithError += delegate(object sender, GADBannerViewDidFailWithErrorEventArgs error) {
                    tableView.Frame = new RectangleF (0, 0, View.Bounds.Width, lineupTableview.Frame.Height);
                    isAdmobAdPresent = false;
                };
    
                admobBanner.DidReceiveAd += delegate(object sender, EventArgs e) {
                    if(iAdBannerView != null)
                    if(!(iAdBannerView as ADBannerView).Hidden){
                        Logging.Debug("Already showing iAd! Quit...");
                        admobBanner.Hidden = true;
    
                        isAdmobAdPresent = false;
                        return;
                    }   
    
                    isIAdPresent = false;
                    isAdmobAdPresent = !isIAdPresent;
                    admobBanner.Hidden = false;
                    tableView.Frame = new RectangleF (0, 0, View.Bounds.Width, lineupTableview.Frame.Height - 50);              
                };
                View.AddSubview (admobBanner);
                var adReq = new GADRequest ();              
                admobBanner.LoadRequest (adReq);
    
    
        void m_adBannerView_AdLoaded (object sender, EventArgs e)
        { 
            if (iAdBannerView == null)
                return;
            tableView.Frame = new RectangleF (0, 0, View.Bounds.Width, lineupTableview.Frame.Height - 50);              
            if(admobBanner != null)
                admobBanner.Hidden = true;
            (iAdBannerView as ADBannerView).Hidden = false;
            isIAdPresent = true;
            isAdmobAdPresent = !isIAdPresent;
        }   
        void m_adBannerView_FailedToReceiveAd (object sender, AdErrorEventArgs e)
        {
            if(admobBanner == null)
                return;
            if (iAdBannerView == null)
                return;
    
            (iAdBannerView as ADBannerView).Hidden = true; 
            isIAdPresent = false;
            if(!isAdmobAdPresent)
            {
                admobBanner.LoadRequest (new GADRequest());
            }
    
  • NicWiseNicWise NZMember, Insider, Beta mod

    I'd be inclined to remove it from it's parent view on ViewDidDisappear / ViewDidUnload (one of those went away in iOS6...), and maybe dispose it, too.

    iAdBannerView.RemoveFromParentView();

    should be enough.

  • vbelletvbellet USMember, Beta ✭✭
    edited October 2012

    viewDidUnload delegate is no more called in iOS 6. In case of a memory warning, the system now take care that for you by removing the linked CALayer from your UIView automatically. If your app need it again it will call the redraw methods and recreate the CAlayer.

    So in that context, i will go for viewDidDisapear.

Sign In or Register to comment.