After updating our app to use Xamarin's Unified API and the recent SR release, I started getting object ref crashes with grid.Theme == null where previously it had been populated. I tracked it down to the code below and was wondering if this is a defect or if you could explain the behavior. I've got a workaround for the problem (setting the theme again inside the task) but I don't understand why the property is getting nulled to begin with.
private IGGridView grid; private List<Section> CreateSections(CoreGraphics.CGRect bounds, UIView topView) { mainView = new UIView { Frame = new CoreGraphics.CGRect(0, 0, bounds.Width, bounds.Height), AutoresizingMask = UIViewAutoresizing.None, BackgroundColor = UIColor.Clear }; grid = CreateGridView(gridFrame); grid.Theme = new PatientListGridTheme(); // custom Theme that inherits from IGGridViewThemeDefinition LoadGridData(); mainView.Add(grid); return new List<Section> { main }; } private void LoadGridData() // Invoked from UI thread { Console.WriteLine(string.Format("Theme : {0}", grid.Theme == null ? "NULL" : grid.Theme.GetType().ToString()));
//prints Theme : PatientListGridTheme Task.Run(async () => { await Task.Run(() => { Console.WriteLine(string.Format("Theme : {0}", grid.Theme == null ? "NULL" : grid.Theme.GetType().ToString()));
//prints Theme : NULL !!!!!!!!
InvokeOnMainThread(() => { Console.WriteLine(string.Format("Theme : {0}", grid.Theme == null ? "NULL" : grid.Theme.GetType().ToString()));
//prints Theme : NULL grid.Theme = new PatientListGridTheme(); Console.WriteLine(string.Format("Theme : {0}", grid.Theme == null ? "NULL" : grid.Theme.GetType().ToString()));
//prints Theme : PatientListGridTheme }); } ); InvokeOnMainThread(() => HideLoadingView()); }); }
After further testing, it appears that accessing the property in any way within the first Task prevents a null exception. Not sure if this is an IG problem or something with Xamarin's GC, but the property doesn't seem to survive two hops.
private void LoadGridData(PatientFilterTypes filter) { var searchText = searchControl.Text; Task.Run(async () => { bool isNull = grid.Theme == null; // accessing the property here prevents null await Task.Run(() => { // isNull now returns false. } ); InvokeOnMainThread(() => HideLoadingView()); }); }
Hi Chase,
If i had to guess, i'd say that with the Unified API Xamarin might be adhering to weak properties.
The theme property is actually weak, and in Objective-C you would have to have a global reference to the theme so that it doesn't disappear on you. From what you're describing, its sounds like the same behavior.
Try creating a global private field to your class that holds on to the theme, and then set that field to the grid's theme property.
If that works, then its just seems to be a fundamental change to how you might have to handle weak objects in Xamarin.
-SteveZ
Yep, that did the trick:
private static IGGridViewThemeDefinition GridTheme = new PatientListGridTheme();
......
gridView.Theme = GridTheme;
Is there a way to tell from the api which properties are weak obj-c objects? I'm used to the WeakReference C# class, but not so much obj-c.
Thanks!
In obj-c you can tell b/c it has the weak attribute on it. However in the Xamarin API's that gets lost.
However, in all of our controls it comes down to just a few property types. Essentially anyone that takes a protocol(interface) as a property. These fall under basically 4 categories.
All Datasource properties: for example using any of our DataSourceHelpers
All Delegate propreties: so anything you listen for notifications/events
All Theme properties:
All Strings properties. We have properties on all controls that have some predefined text. All of these properties are literally called "Strings". They allow you to specify an object that overrides the custom strings, or to localize them if you need to.
Hope this helps