{"id":3663,"date":"2013-03-20T18:36:53","date_gmt":"2013-03-21T01:36:53","guid":{"rendered":"http:\/\/blog.xamarin.com\/?p=3663"},"modified":"2019-07-03T10:12:52","modified_gmt":"2019-07-03T17:12:52","slug":"producing-better-bindings-1-constructors","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/xamarin\/producing-better-bindings-1-constructors\/","title":{"rendered":"Producing Better Bindings #1: Constructors"},"content":{"rendered":"<p>\t\t\t\tThis blog post is about producing better .NET bindings for using Objective-C libraries on\u00a0<a href=\"http:\/\/xamarin.com\/monotouch\">Xamarin.iOS<\/a> and <a href=\"http:\/\/xamarin.com\/mac\">Xamarin.Mac<\/a>. Read the series <a href=\"\/producing-better-bindings-for-xamarin.ios-and-xamarin.mac\/\">introduction<\/a>\u00a0to get a better idea of why this is important and how it can save you time and headaches.<\/p>\n<p><strong>What can go wrong ?<\/strong><\/p>\n<p>It&#8217;s not immediately obvious, but Objective-C object <a href=\"http:\/\/developer.apple.com\/library\/ios\/#documentation\/general\/conceptual\/CocoaEncyclopedia\/Initialization\/Initialization.html\">initialization<\/a>\u00a0(<code>init...<\/code> methods) differ quite a bit from .NET constructors. Those differences have an important impact on bindings.<\/p>\n<p>In Objective-C:<\/p>\n<ul>\n<li><code>init...<\/code> can return <code>nil<\/code>, in fact they generally do so if initialization fails;<\/li>\n<li><code>init...<\/code> methods are inherited, the base types\u00a0<code>init...<\/code> method will be used;<\/li>\n<li>Documentation (at least Apple&#8217;s) is rarely clear on wether you can (or can&#8217;t) call the default <code>init<\/code> to create an instance. This might work, return <code>nil<\/code>, throw or crash.<\/li>\n<\/ul>\n<p>In .NET:<\/p>\n<ul>\n<li>Constructors cannot return <code>null<\/code>. If the instance cannot be created, it will throw an exception;<\/li>\n<li>Constructors are not inherited, they must be re-defined in each type (and call the base type). This makes it clear if a constructor is available or not, except for one case&#8230;<\/li>\n<li>A default (no parameters) <code>public<\/code> constructor is added to (non-static) types unless you define one with less visibility (e.g. <code>private<\/code>).<\/li>\n<\/ul>\n<p>Mixing those two different feature sets can create some weird cases. E.g.<\/p>\n<ul>\n<li>If <code>init...<\/code> returns <code>null<\/code> then a constructor will create a managed instance with an\u00a0<code>IntPtr.Zero<\/code> handle. That instance won&#8217;t be usable, except as a <code>null<\/code> value;<\/li>\n<li>You can end up with a default constructor for Objective-C types that should not be created, e.g. abstract, singletons&#8230; but they can <em>partially<\/em> and unpredictably work (hard to spot);<\/li>\n<li>You might be missing some constructors because you overlooked the base class <code>init...<\/code>. That might make some types impossible to create from a .NET application;<\/li>\n<\/ul>\n<p><strong>What can we check for ?<\/strong><\/p>\n<p>A test can reflect each <code>NSObject<\/code>-derived type from a binding assembly, find its default constructor, create an instance and validate that it&#8217;s <code>Handle<\/code> property is valid (not <code>IntPtr.Zero<\/code>). That would spot:<\/p>\n<ul>\n<li>Types that are abstract (and should not be created);<\/li>\n<li>Types that are singleton (and should be used differently);<\/li>\n<li>Types that do not support being created using the default &#8220;<code>- (id)init<\/code>&#8221; signature.<\/li>\n<\/ul>\n<p>In addition, the same test can also check that the <code>ToString<\/code> method is implemented correctly. The default <code>NSObject.ToString()<\/code> <a href=\"https:\/\/github.com\/mono\/maccore\/blob\/master\/src\/Foundation\/NSObject2.cs#L151\">implementation<\/a> calls the <code>description<\/code> selector. That call might crash due to uninitialized native members\u2014even if the instance is fine.<\/p>\n<p>Finally, by calling the <code>Dispose()<\/code> method, the test also ensures that the type can be released correctly. If all of this can be done without failure then the type is likely consumable.<\/p>\n<p><strong>Why is this important ?<\/strong><\/p>\n<p>Default constructors will be shown in your IDE code completion and binding consumers will assume it&#8217;s a valid way to create an object instance. If it results in an invalid\/unusable instance\u2014or worse, a crash\u2014then it makes the binding much more complex to use (e.g. remember what constructors are valid). In other words, any public API should be safe to call.<\/p>\n<p>The <code>ToString<\/code> method is used by the debugger to show you a description of an object (e.g. in the watch pad). If calling <code>ToString<\/code>\u00a0causes an application to crash, then debugging your application will cause it to crash. This can be hard to diagnose (and often irreproducible) because it will be the debugger that will cause this, depending on it&#8217;s state (e.g. watches), not the code that&#8217;s being executed.<\/p>\n<p>The <code>Dispose<\/code> check does not often fail. When it does, it generally means that creating an instance has special rules (hopefully documented) or that something else is invalid (somehow a bad instance was created but has not failed the previous checks). One should be aware of such special requirements before using the type. You might be able to document it, or even hide it from the binding consumers.<\/p>\n<p><strong>How to fix issues ?<\/strong><\/p>\n<p>In most cases, the test will fail (or crash) because a handle is invalid. The library&#8217;s documentation (or source code, when available) will often tell you if there are other ways to create an instance of that particular type. In general, the fix will be to add the <code>[DisableDefaultCtor]<\/code> attribute to such types in your bindings. For example:<\/p>\n<pre class=\"lang:csharp decode:true\">\n\t[BaseType (typeof (CCActionInterval))]\n\t[DisableDefaultCtor] \/\/ Objective-C exception thrown.  Name: NSInternalInconsistencyException Reason: IntervalActionInit: Init not supported. Use InitWithDuration\n\tinterface CCSequence {\n\t\t\/\/ ...\n\t}\n<\/pre>\n<p>There might be cases where creating an instance only works if some other action has been done previously (e.g. initialization). You can add such steps in your fixture constructor.<\/p>\n<pre class=\"lang:csharp decode:true\">\n\t[TestFixture]\n\tpublic class BindingCtorTest : ApiCtorInitTest\t{\n\t\tpublic BindingCtorTest ()\n\t\t{\n\t\t\t\/\/ fictional example where binding won't work without\n\t\t\t\/\/ some specific initialization code\n\t\t\tMyLibrary.SharedConfiguration.Server = &quot;127.0.0.1&quot;;\n\t\t}\n\t}\n<\/pre>\n<p>Creating some types might also require something external to the library (e.g. specific hardware or an API key). In such cases, you might need to skip the type by overriding the fixture&#8217;s <code>Skip(Type)<\/code> method and returning\u00a0<code>true<\/code> for this type.<\/p>\n<pre class=\"lang:csharp decode:true\">\n\t[TestFixture]\n\tpublic class BindingCtorTest : ApiCtorInitTest\t{\n\t\tpublic BindingCtorTest ()\n\t\t{\n\t\t\t\/\/ fictional example where binding won't work without\n\t\t\t\/\/ an API key\n\t\t\tMyLibrary.ApiKey = &quot;foo&quot;;\n\t\t}\n\n\t\tprotected override bool Skip (Type type)\n\t\t{\n\t\t\t\/\/ fictional example where a specific type won't work without\n\t\t\t\/\/ some some hardware device, you can skip those\n\t\t\tif (type.FullName == &quot;MyLibrary.MyDevice&quot;)\n\t\t\t\treturn true;\n\t\t\treturn base.Skip (type);\n\t\t}\n\t}\n<\/pre>\n<p>If <code>ToString<\/code> fails, but the instance is valid, then you might want to add a manual override for it (e.g. inside <code>Extra.cs<\/code>), providing some useful data about the state or simply returning it&#8217;s fully qualified name.<\/p>\n<pre class=\"lang:csharp decode:true\">\n\tpublic partial class MyTypeCrashingOnDescription {\n\t\t\/\/ when `description` fails we prefer returning something useful (not null)\n\t\t\/\/ so the debugging session won't be interupted by a native crash\n\t\tpublic override string ToString ()\n\t\t{\n\t\t\treturn GetType ().FullName;\n\t\t}\n\t}\n<\/pre>\n<p>If an instance cannot be disposed, then you should override the fixture&#8217;s <code>Dispose(NSObject,Type)<\/code> method and keep a reference to it. That will allow the test execution to continue.<\/p>\n<pre class=\"lang:csharp decode:true\">\n\t[TestFixture]\n\tpublic class BindingCtorTest : ApiCtorInitTest\t{\n\t\tstatic List do_not_dispose = new List ();\n\n\t\tprotected override void Dispose (NSObject obj, Type type)\n\t\t{\n\t\t\tswitch (type.FullName) {\n\t\t\t\/\/ this crash the application after test completed their execution so we keep it alive\n\t\t\tcase &quot;MyLibrary.MyTypeThatCannotBeDisposed&quot;:\n\t\t\t\tdo_not_dispose.Add (obj);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbase.Dispose (obj, type);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n<\/pre>\n<p>Most of the above (except adding <code>[DisableDefaultCtor])<\/code>\u00a0are uncommon, but useful to know. For example, the fixture <a href=\"https:\/\/github.com\/mono\/monotouch-bindings\/blob\/master\/cocos2d\/binding-test\/BindingCtorTest.cs\">implementation<\/a> to test Cocos2d did <strong>not<\/strong> require any overrides (besides specifying the <code>Assembly<\/code> to test).<\/p>\n<p><strong>What&#8217;s missing ?<\/strong><\/p>\n<p>There&#8217;s no easy way to test for missing constructors, e.g. the ones available in base classes that are not inherited. That would require static analysis (not introspection) in order to be useful. Keep an eye on types where you add <code>[DisableDefaultCtor]<\/code>. If they do not have any <code>Constructor<\/code> methods, then they might be expected to use the base type <code>init...<\/code> methods. Quite a few constructors were added to Cocos2d.<\/p>\n<p>Finally, to make it easier to review untested types\u2014those without a default constructor\u2014you can set the <code>LogUntestedTypes<\/code> property to <code>true<\/code> and re-execute the tests.<\/p>\n<pre class=\"lang:csharp decode:true\">\n\t[TestFixture]\n\tpublic class BindingCtorTest : ApiCtorInitTest\t{\n\t\tpublic BindingCtorTest ()\n\t\t{\n\t\t\t\/\/ Useful to know which types are being skipped for lack of a default ctor\n\t\t\tLogUntestedTypes = true;\n\t\t}\n\t}\n<\/pre>\n<p><b>Read the rest of the series:<\/b><\/p>\n<ul>\n<li><a href=\"\/producing-better-bindings-for-xamarin.ios-and-xamarin.mac\/\">Introduction<\/a><\/li>\n<li><a href=\"\/producing-better-bindings-1-constructors\/\">Part 1: Constructors<\/a><\/li>\n<li><a href=\"\/producing-better-bindings-2-fields\/\">Part 2: Fields<\/a><\/li>\n<li><a href=\"\/producing-better-bindings-3-selectors\/\">Part 3: Selectors<\/a><\/li>\n<li><a href=\"\/producing-better-bindings-4-signatures\/\">Part 4: Signatures<\/a><\/li>\n<\/ul>\n<p><a href=\"http:\/\/forums.xamarin.com\/discussion\/2433\/producing-better-bindings\">Discuss this post on the Xamarin forums<\/a>\t\t<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This blog post is about producing better .NET bindings for using Objective-C libraries on\u00a0Xamarin.iOS and Xamarin.Mac. Read the series introduction\u00a0to get a better idea of why this is important and how it can save you time and headaches. What can go wrong ? It&#8217;s not immediately obvious, but Objective-C object initialization\u00a0(init&#8230; methods) differ quite a [&hellip;]<\/p>\n","protected":false},"author":5741,"featured_media":39167,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[303],"tags":[4],"class_list":["post-3663","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ios","tag-xamarin-platform"],"acf":[],"blog_post_summary":"<p>This blog post is about producing better .NET bindings for using Objective-C libraries on\u00a0Xamarin.iOS and Xamarin.Mac. Read the series introduction\u00a0to get a better idea of why this is important and how it can save you time and headaches. What can go wrong ? It&#8217;s not immediately obvious, but Objective-C object initialization\u00a0(init&#8230; methods) differ quite a [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/3663","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/users\/5741"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/comments?post=3663"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/3663\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media\/39167"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media?parent=3663"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/categories?post=3663"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/tags?post=3663"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}