Cross Platform
Android
iOS
Mac
 

Linker

When building your application, Xamarin Studio or Visual Studio calls a tool called mtouch that includes a linker for managed code. It is used to remove from the class libraries the features that the application is not using. The goal is to reduce the size of the application, which will ship with only the necessary bits.

The linker uses static analysis to determine the different code paths that your application is susceptible to follow. It's a bit heavy as it has to go through every detail of each assembly, to make sure that nothing discoverable is removed. As such it is not enabled, by defaut, on the simulator builds. But since it produce smaller applications it can speed up AOT compilation and uploading to the device so, by default, all devices builds are using the linker.

As the linker is a static tool, it can not mark for inclusion types and methods that are called through reflection, or dynamically instantiated. Several options exists to workaround this limitation.

Linker Behavior

The linking process can be customized in different ways. The primary mechanism for controlling the linker in Xamarin Studio is the Linker Behavior drop-down in the iOS Build Xamarin Studio's Project Options dialog box. In Visual Studio, this is located in project Properties, under iOS Build. Three main options are offered:

Don't Link

Disabling linking will make sure that no assemblies are modified. For performance reasons this is the default setting when your IDE targets for the iOS simulator. For devices builds this should only be used as a workaround whenever the linker contains a bug that prevents your application to run. If your application only works with -nolink, please submit a bug report.

This correspond to the -nolink option when using the command-line tool mtouch.

Link SDK assemblies only

In this mode, the linker will leave your assemblies untouched, and will reduce the size of the SDK assemblies (i.e. what's shipped with Xamarin.iOS) by removing everything that your application doesn't use. This is the default setting when your IDE targets iOS devices.

This is the most simple option, as it does not require any change in your code. The difference with linking everything is that the linker can not perform a few optimizations in this mode, so it's a trade-off between the work needed to link everything and the final application size.

This correspond to the -linksdk option when using the command-line tool mtouch.

Link all assemblies

When linking everything, the linker can use the whole set of its optimizations to make the application as small as possible. It will modify user code, which may break whenever the code uses features in a way that the linker's static analysis cannot detect. In such cases, e.g. webservices, reflection, or serialization, some adjustements might be required in your application to link everything.

This correspond to the -linkall option when using the command-line tool mtouch.

Controlling the Linker

When you use the linker it will sometimes will remove code that you might have called dynamically, even indirectly. To cover those cases the linker provides a few features and options to allow you greater control on it's actions.

Preserving Code

When you use the linker it can sometimes remove code that you might have called dynamically either using System.Reflection.MemberInfo.Invoke, or by exporting your methods to Objective-C using the [Export] attribute and then invoking the selector manually.

In those cases, you can instruct the linker to consider either entire classes to be used or individual members to be preserved by applying the [Xamarin.iOS.Foundation.Preserve] attribute either at the class-level or the member-level. Every member that is not statically linked by the application is subject to be removed. This attribute is hence used to mark members that are not statically referenced, but that are still needed by your application.

For instance, if you instantiate types dynamically, you may want to preserve the default constructor of your types. If you use XML serialization, you may want to preserve the properties of your types.

You can apply this attribute on every member of a type, or on the type itself. If you want to preserve the whole type, you can use the syntax [Preserve (AllMembers = true)] on the type.

Sometimes you want to preserve certain members, but only if the containing type was preserved. In those cases, use [Preserve (Conditional=true)]

Skipping Assemblies

It is possible to specify assemblies that should not be excluded from the linker process, while allowing other assemblies to be linked normally. This is helpful if using [Preserve] on some assemblies is impossible (e.g. 3rd party code) or as a temporary workaround for a bug.

This correspond to the -linkskip option when using the command-line tool mtouch.

There is no user interface to use this option but it can be provided in the Xamarin Studio Project Options dialog or the Visual Studio project Properties pane, within the "Additional mtouch arguments" text field. (E.g. "--linkskip=mscorlib" would not link mscorlib.dll but would link other assemblies in the solution).

Disabling "Link Away"

The linker will remove code that is very unlikely to be used on devices, e.g. unsupported or disallowed. In rare occasion it is possible that an application or library depends on this (working or not) code. Since Xamarin.iOS 5.0.1 the linker can be instructed to skip this optimization.

This correspond to the -nolinkaway option when using the command-line tool mtouch.

There is no user interface to use this option but it can be provided in the Xamarin Studio Project Options dialog or the Visual Studio project Properties pane, within "Additional mtouch arguments" text field. (E.g. "--nolinkaway" would not remove the extra code (about 100kb)).

Custom Linking

If the default set of options is not enough, you can drive the linking process with an XML file that describes what you want from the linker.

You can provide extra definitions to the linker to ensure the type, methods and/or fields are not eliminated from your application. In your own code the preferred way is to use the [Preserve] attribute. However if you need some definitions from the SDK or product assemblies then using an XML file might be your best solution (versus adding code that will ensure the linker won't eliminate what you need).

To do this, you define an XML file with the top-level attribute <linker> which contains assembly nodes which in turn contain type nodes, which in turn contain method and field nodes.

Once you have this linke description file, you pass the --xml=FILE flag to the mtouch command.

The following example shows what the XML file looks like:

<linker>
        <assembly fullname="mscorlib">
                <type fullname="System.Environment">
                        <field name="mono_corlib_version" />
                        <method name="get_StackTrace" />
                </type>
        </assembly>
        <assembly fullname="My.Own.Assembly">
                <type fullname="Foo" preserve="fields" />
                        <method name=".ctor" />
                </type>
                <type fullname="Bar" />
                        <method signature="System.Void .ctor(System.String)" />
                        <field signature="System.String _blah" />
                </type>
        </assembly>
</linker>

In above example, the linker will read and apply the instructions on the mscorlib.dll (shipped with Xamarin.iOS) and My.Own.Assembly (user code) assemblies.

The first section, for mscorlib.dll, will ensure that the System.Environment type will preserve it's field named 'mono_corlib_version' and it's 'get_StackTrace' method. Note the getter and/or setter method names must be used as the linker works on IL and does not understand C# properties.

The second section, for My.Own.Assembly.dll, will ensure that the 'Foo' type will preserve all it's fields (i.e. the preserve="fields" attribute) and all it's constructors (i.e. all the methods named '.ctor' in IL). Also the 'Bar' type will preserve specific signatures (not names) for one constructor (that accept a single string parameter) and for a specific string field '_blah'.