Bug 21457 - NullReferenceException thrown when Custom ViewRenderer (iOS) is Disposed
Summary: NullReferenceException thrown when Custom ViewRenderer (iOS) is Disposed
Status: RESOLVED FIXED
Alias: None
Product: Forms
Classification: Xamarin
Component: Forms ()
Version: 1.2.2
Hardware: Macintosh Mac OS
: Normal normal
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2014-07-19 07:18 UTC by Herman
Modified: 2017-05-31 09:09 UTC (History)
13 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description Herman 2014-07-19 07:18:34 UTC
I have a MasterDetailPage as the root page of my app. When the Detail Page is swapped the contents are disposed by the Xamrin.Forms framework, and so is the ViewRenderer<Video,UIView> that i've made. However during the call to Dispose of the  ViewRenderer a NullReferenceException occurs.

I noticed this before overriding the Dispose method and I reproduced it by calling base.Dispose of my VideoRenderer : ViewRenderer<Video,UIView>.

Currently my workaround is to catch the NullReferenceException in the override, however after this exception is catched once, the MasterDetailPage does not allow swiping from the left edge to open the menu anymore.

On iOS create a ViewRenderer<Something,UIView> and dispose the Page that uses the Something.

protected override void Dispose (bool disposing)
{
	try {
		base.Dispose (disposing);	
	} catch(NullReferenceException) {
		// Random bug in Dispose method
	}
}
Comment 1 Herman 2014-07-19 07:22:55 UTC
Stacktrace:

  at Xamarin.Forms.Platform.iOS.ViewRenderer`2[Emando.Vantage.Practice.FormsApp.Modules.Video.Video,MonoTouch.UIKit.UIView].Dispose (Boolean disposing) [0x00000] in <filename unknown>:0 
  at MonoTouch.Foundation.NSObject.Dispose () [0x00000] in /Developer/MonoTouch/Source/maccore/src/Foundation/NSObject2.cs:119 
  at Xamarin.Forms.Platform.iOS.Platform.DisposeModelAndChildrenRenderers (Xamarin.Forms.Element view) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.Platform.iOS.Platform.HandleChildRemoved (System.Object sender, Xamarin.Forms.ElementEventArgs e) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.Element.OnDescendantRemoved (Xamarin.Forms.Element child) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.Element.OnChildRemoved (Xamarin.Forms.Element child) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.Page.OnInternalRemoved (Xamarin.Forms.VisualElement view) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.Page.InternalChildrenOnCollectionChanged (System.Object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) [0x00000] in <filename unknown>:0 
  at System.Collections.ObjectModel.ObservableCollection`1[Xamarin.Forms.Element].OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedEventArgs e) [0x00014] in /Developer/MonoTouch/Source/mono/mcs/class/System/System.Collections.ObjectModel/ObservableCollection.cs:161 
  at System.Collections.ObjectModel.ObservableCollection`1[Xamarin.Forms.Element].RemoveItem (Int32 index) [0x0001a] in /Developer/MonoTouch/Source/mono/mcs/class/System/System.Collections.ObjectModel/ObservableCollection.cs:182 
  at System.Collections.ObjectModel.Collection`1[Xamarin.Forms.Element].Remove (Xamarin.Forms.Element item) [0x00011] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Collections.ObjectModel/Collection.cs:134 
  at Xamarin.Forms.MasterDetailPage.set_Detail (Xamarin.Forms.Page value) [0x00000] in <filename unknown>:0 
  at Emando.Vantage.Practice.FormsApp.RootPage.NavigateTo (IModule module, Emando.Vantage.Practice.FormsApp.MenuItem item) [0x00086] in /Projects/Vantage/Forms/Emando.Vantage.Practice.FormsApp/Emando.Vantage.Practice.FormsApp/RootPage.cs:73 
  at Emando.Vantage.Practice.FormsApp.RootPage+<getListView>c__AnonStorey1.<>m__0 (System.Object sender, Xamarin.Forms.SelectedItemChangedEventArgs e) [0x00036] in /Projects/Vantage/Forms/Emando.Vantage.Practice.FormsApp/Emando.Vantage.Practice.FormsApp/RootPage.cs:46 
  at (wrapper delegate-invoke) System.EventHandler`1<Xamarin.Forms.SelectedItemChangedEventArgs>:invoke_void_object_TEventArgs (object,Xamarin.Forms.SelectedItemChangedEventArgs)
  at Xamarin.Forms.ListView.OnSelectedItemChanged (Xamarin.Forms.BindableObject bindable, System.Object oldValue, System.Object newValue) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.BindableObject.SetValueActual (Xamarin.Forms.BindableProperty property, System.Object value, Boolean currentlyApplying, Boolean clearBindings, Boolean raiseOnEqual) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.BindableObject.SetValueCore (Xamarin.Forms.BindableProperty property, System.Object value, Boolean clearBindings, Boolean raiseOnEqual, Boolean checkaccess) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.ListView.NotifyRowTapped (Int32 groupIndex, Int32 inGroupIndex) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.Platform.iOS.ListViewRenderer+ListViewDataSource.RowSelected (MonoTouch.UIKit.UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath) [0x00000] in <filename unknown>:0 
  at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr)
  at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:39 
  at Emando.Vantage.Practice.FormsApp.iOS.Application.Main (System.String[] args) [0x00008] in /Projects/Vantage/Forms/Emando.Vantage.Practice.FormsApp/iOS/Main.cs:17
Comment 2 Ram Chandra 2014-08-21 06:06:59 UTC
I have tried this issue but I am unable to reproduce this issue.

Could you please provide sample application and environment information? So, that we can reproduce this issue.

You can get environment info from here => Xamarin Studio =>About Xamarin Studio => Show details => Copy Information.

Environment Info:

=== Xamarin Studio ===

Version 5.3 (build 429)
Installation UUID: 6ea47b0d-1852-4aaf-808d-373ff0a5002b
Runtime:
	Mono 3.8.0 ((no/62a857e)
	GTK+ 2.24.23 (Raleigh theme)

	Package version: 308000007

=== Apple Developer Tools ===

Xcode 5.1 (5084)
Build 5B130a

=== Xamarin.iOS ===

Version: 7.4.0.104 (Business Edition)
Hash: 63b403b
Branch: 
Build date: 2014-08-19 16:20:25-0400

=== Xamarin.Android ===

Version: 4.16.0 (Business Edition)
Android SDK: /Users/jatin66/Desktop/Backup/android-sdk-macosx
	Supported Android versions:
		1.6   (API level 4)
		2.1   (API level 7)
		2.2   (API level 8)
		2.3   (API level 10)
		3.0   (API level 11)
		3.1   (API level 12)
		3.2   (API level 13)
		4.0   (API level 14)
		4.0.3 (API level 15)
		4.1   (API level 16)
		4.2   (API level 17)
		4.3   (API level 18)
		4.4   (API level 19)
Java SDK: /usr
java version "1.7.0_25"
Java(TM) SE Runtime Environment (build 1.7.0_25-b15)
Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode)

=== Xamarin.Mac ===

Version: 1.10.0.9 (Business Edition)

=== Build Information ===

Release ID: 503000429
Git revision: ad1470467446b532ed54124a36bf1381a7471020
Build date: 2014-08-19 12:31:31-04
Xamarin addins: 3d041b1639f938107c5e1c71c54d9a346b30c660

=== Operating System ===

Mac OS X 10.9.3
Darwin Jatin66s-iMac.local 13.2.0 Darwin Kernel Version 13.2.0
    Thu Apr 17 23:03:13 PDT 2014
    root:xnu-2422.100.13~1/RELEASE_X86_64 x86_64
Comment 3 James 2014-08-23 16:53:01 UTC
I had this same issue but it was because I wasn't calling SetNativeControl in the OnElementChanged method of my custom view renderer.

This resulted in the Control base property being null for the lifetime of the renderer. Then when the Dispose method of ViewRenderer is called it will throw as it does Control.Dispose() without a null check.

I fixed it by just doing a SetNativeControl(new UIControl()); but I didn't need to render anything as was just using the renderer to do custom gestures so this is a hacky workaround.
Comment 4 Herman 2014-08-24 05:58:20 UTC
As James discovered, and as I mentioned, something is null. James has now reported something that I will soon check-out and see if it fixes my issue.

However this happens a lot (NRE's) and this is frustrating. We can't see what we do wrong and the docs lack proper examples or guides. How should we figure out to call SetNativeControl if this is not documented? I would suggest you guys add try.. catches everywhere and tell your consumer-devs what they are doing wrong. 

The things is: we don't know what is causing the NRE, so creating a minimal proof of concept can be cumbersome, while you guys have the power to just look through the Forms code to see what could be null. If you just posted the 3/4 lines the custom renderers Dispose-method contained we could have figured this out. Of course, you could also just take a look there and ask us: "is X not set? is Y not set?"

Way to go guys.
Comment 5 uwantfries 2015-01-10 06:03:40 UTC
I would like to chip in. I have a master/detail and one of the details I go off and nav to another few pages and then popaync back to the start page. Upon selecting another detail page the old page stack is disposed and onElement changed is called. The view is now null at this point so the custom renderer fails in its internals. 

Should onElementChanged really be called in dispose? 

Xamarin.Forms.Labs.Controls.ExtendedEntry2Renderer.SetFont (view=(null)) in /Users/vikkikeane/Projects/ersV4/iOS/Renderers/ExtendedEntry2Renderer.cs:123
Xamarin.Forms.Labs.Controls.ExtendedEntry2Renderer.OnElementChanged (e={Xamarin.Forms.Platform.iOS.ElementChangedEventArgs<Xamarin.Forms.Entry>}) in /Users/vikkikeane/Projects/ersV4/iOS/Renderers/ExtendedEntry2Renderer.cs:43
Xamarin.Forms.Platform.iOS.VisualElementRenderer<Xamarin.Forms.Entry>.SetElement (element=(null)) in 
Xamarin.Forms.Platform.iOS.VisualElementRenderer<Xamarin.Forms.Entry>.Dispose (disposing=true) in 
Xamarin.Forms.Platform.iOS.ViewRenderer<Xamarin.Forms.Entry,MonoTouch.UIKit.UITextField>.Dispose (disposing=true) in 
Xamarin.Forms.Platform.iOS.EntryRenderer.Dispose (disposing=true) in 
MonoTouch.Foundation.NSObject.Dispose () in /Developer/MonoTouch/Source/maccore/src/Foundation/NSObject2.cs:127
Xamarin.Forms.Platform.iOS.Platform.DisposeModelAndChildrenRenderers (view={Xamarin.Forms.NavigationPage}) in 
Xamarin.Forms.Platform.iOS.Platform.HandleChildRemoved (sender={ersV4.HomeView}, e={Xamarin.Forms.ElementEventArgs}) in 
Xamarin.Forms.Element.OnDescendantRemoved (child={Xamarin.Forms.NavigationPage}) in 
Xamarin.Forms.Element.OnChildRemoved (child={Xamarin.Forms.NavigationPage}) in 
Xamarin.Forms.Page.OnInternalRemoved (view={Xamarin.Forms.NavigationPage}) in 
Xamarin.Forms.Page.InternalChildrenOnCollectionChanged (sender=Count=1, e={System.Collections.Specialized.NotifyCollectionChangedEventArgs}) in 
System.Collections.ObjectModel.ObservableCollection<Xamarin.Forms.Element>.OnCollectionChanged (e=Value not available) in ///Library/Frameworks/Xamarin.iOS.framework/Versions/8.4.0.47/src/mono/mcs/class/System/System.Collections.ObjectModel/ObservableCollection.cs
System.Collections.ObjectModel.ObservableCollection<Xamarin.Forms.Element>.RemoveItem (index=Value not available) in ///Library/Frameworks/Xamarin.iOS.framework/Versions/8.4.0.47/src/mono/mcs/class/System/System.Collections.ObjectModel/ObservableCollection.cs
System.Collections.ObjectModel.Collection<Xamarin.Forms.Element>.Remove (item=Value not available) in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Collections.ObjectModel/Collection.cs
Xamarin.Forms.MasterDetailPage.set_Detail (value={Xamarin.Forms.NavigationPage}) in 
ersV4.HomeView.AnonymousMethod__0 (menuType=ersV4.MenuType.Twitter) in /Users/vikkikeane/Projects/ersV4/ersV4/Views/HomeView.cs:67
ersV4.HomeMasterView.set_PageSelection (value={ersV4.TwitterView}) in /Users/vikkikeane/Projects/ersV4/ersV4/Views/HomeView.cs:88
ersV4.HomeMasterView.HomeMasterView.AnonymousMethod__0 (sender={Xamarin.Forms.ListView}, args={Xamarin.Forms.SelectedItemChangedEventArgs}) in /Users/vikkikeane/Projects/ersV4/ersV4/Views/HomeView.cs:171
Xamarin.Forms.ListView.OnSelectedItemChanged (bindable={Xamarin.Forms.ListView}, oldValue={ersV4.HomeMenuItem}, newValue={ersV4.HomeMenuItem}) in 
Xamarin.Forms.BindableObject.SetValueActual (property=SelectedItem, context={Xamarin.Forms.BindableObject.BindablePropertyContext}, value={ersV4.HomeMenuItem}, currentlyApplying=false, attributes=Xamarin.Forms.BindableObject+SetValueFlags.ClearDynamicResource|Xamarin.Forms.BindableObject+SetValueFlags.ClearOneWayBindings|Xamarin.Forms.BindableObject+SetValueFlags.RaiseOnEqual, silent=false) in 
Xamarin.Forms.BindableObject.SetValueCore (property=SelectedItem, value={ersV4.HomeMenuItem}, attributes=Xamarin.Forms.BindableObject+SetValueFlags.ClearDynamicResource|Xamarin.Forms.BindableObject+SetValueFlags.ClearOneWayBindings|Xamarin.Forms.BindableObject+SetValueFlags.RaiseOnEqual, privateAttributes=Xamarin.Forms.BindableObject+SetValuePrivateFlags.CheckAccess) in 
Xamarin.Forms.BindableObject.SetValueCore (property=SelectedItem, value={ersV4.HomeMenuItem}, attributes=Xamarin.Forms.BindableObject+SetValueFlags.ClearDynamicResource|Xamarin.Forms.BindableObject+SetValueFlags.ClearOneWayBindings|Xamarin.Forms.BindableObject+SetValueFlags.RaiseOnEqual) in 
Xamarin.Forms.ListView.NotifyRowTapped (groupIndex=0, inGroupIndex=1) in 
Xamarin.Forms.Platform.iOS.ListViewRenderer.ListViewDataSource.RowSelected (tableView=, indexPath=) in 
MonoTouch.UIKit.UIApplication.UIApplicationMain () in 
MonoTouch.UIKit.UIApplication.Main (Parameters=) in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:62
MonoTouch.UIKit.UIApplication.Main (Parameters=) in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:45
ersV4.iOS.Application.Main (Parameters=) in /Users/vikkikeane/Projects/ersV4/iOS/Main.cs:17
Comment 6 Ed Sahakian 2015-03-25 13:44:09 UTC
I have the same problem with my custom Layout Renderer... At the moment I am hacking it this way but I am sure its not proper... might be a memory leak..

protected override void Dispose (bool disposing)
{
	//base.Dispose (disposing);
}

Simply override Dispose and not call the base... Prevents the crash for now...

this.Control is always null in the Renderer unlike other custom renderers I have made... This is the first time I am actually doing the Layout Renderer to control how to draw its background... Everything works except at the end during Dispose...

Here is a question for you... I am trying to do a Custom Layout Renderer to override the rendering of the AbsoluteLayout... Is the ViewRenderer the proper way to do it...

[assembly: ExportRenderer (typeof (TxAbsoluteLayout), typeof (TxAbsoluteLayoutRenderer))]

namespace Tx.iOS
{
	public class TxAbsoluteLayoutRenderer : ViewRenderer
......
}

I extending the layout to render images and gradients and background colors with rounded corers and borders in the background... Everything works great so far except again for the crash...

Thanks,
Comment 7 Ed Sahakian 2015-03-25 14:30:47 UTC
And this works too but now I have to override the background color of the control so I can paint my own backgrounds as needed...

protected override void OnElementChanged (ElementChangedEventArgs<View> e)
{
	base.OnElementChanged (e);

	if (Control == null) {
		SetNativeControl(new UIControl());
	}
}
Comment 8 Dwadwa 2015-11-05 10:45:11 UTC
I had the same issue today, working on custom renderer for a listview.

The problem comes from OnDispose. When OnDispose is called, it sets elements to null so OnElementChanged triggers.

e.NewElement will be null, so SetNativeControl will return NullException.

The workarround I found :

protected override void OnElementChanged (ElementChangedEventArgs<View> e)
{
    base.OnElementChanged (e);

    if (e.NewElement != null) 
    {
        // Do your stuff
        SetNativeControl(youuicontrol);
    }
}
Comment 9 Adam 2015-12-22 14:24:54 UTC
Looks like the issue I opened is a duplicate of this one.

https://bugzilla.xamarin.com/show_bug.cgi?id=37219
Comment 10 Jason Smith [MSFT] 2016-03-15 19:26:21 UTC
Thank you for taking the time to submit this report. After reviewing the description of this bug, we no longer believe it affects the current version of Xamarin.Forms. If you are still experiencing the issue after updating your packages, please reopen this report with an attached reproduction.
 
For your convenience, we have created some reproduction best practices viewable here: https://gist.github.com/jassmith/92405c300e54a01dcc6d

Warm regards,
Xamarin Forms Team
Comment 11 Nicolai Henriksen 2016-06-01 06:57:21 UTC
I am running Xamarin.Forms 2.2.0.31 and ran into this exact problem. I use the workaround from Herman (original reporter) with catching NullReferenceException in Dispose. My complete renderer looks like this:

    public class LoginPageRenderer : PageRenderer
    {
        private IPlatformParameters _platformParameters;

        protected override void Dispose (bool disposing)
        {
            try 
            {
                base.Dispose (disposing);   
            }
            catch(NullReferenceException) 
            {
                // Random bug in Dispose method
                // https://bugzilla.xamarin.com/show_bug.cgi?id=21457
            }
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
        
            _platformParameters = new PlatformParameters(this);
        }

        public override void ViewWillAppear(bool animated)
        {
            base.ViewWillAppear(animated);

            var auth = SimpleIoc.Default.GetInstance<IAuthenticationService>();
            var azureAuth = auth as AzureB2CADAuthenticationService;
            azureAuth.PlatformParameters = _platformParameters;
        }

        public override void ViewWillDisappear(bool animated)
        {
            base.ViewWillDisappear(animated);

            var auth = SimpleIoc.Default.GetInstance<IAuthenticationService>();
            var azureAuth = auth as AzureB2CADAuthenticationService;
            azureAuth.PlatformParameters = _platformParameters;
        }
    }