HOWTO: Partially Linking iOS Apps with Xamarin
The latest release of MonoTouch (6.0.9) introduces two new attributes that allow you to customize how the linker will process your application’s assemblies. That makes it easier to go beyond the default behavior of “Link SDK assemblies only” without investing as much time as you would need to validate that “Link all assemblies” works for your application(s).
This first attribute allows you to mark your assemblies as “safe to link”. When the attribute is present, the linker—if enabled—will process the assembly even if you’re using the “Link SDK assemblies only” option, which is the default for device builds.
The attribute is useful when:
- You write reusable components. The consumers will benefit from smaller application sizes without any additional work or configuration on their part
- You know that some code can be linked (e.g. no reflection) but are not sure about the rest of the application
- You are in the process of ensuring that your application can be fully linked, one assembly at the time
Objective-C binding assemblies are good candidates to be linked, because most applications are unlikely to use every API inside them—an easy gain. Since they are mostly generated code and do not use reflection, there is not much that can go wrong—a safe gain. One thing to look for is any
struct you are adding, because the linker could remove unused field members from them. Simply adding
[Preserve] to the structures will protect them from being modified by the linker.
[assembly: LinkerSafe] attribute to the cocos2d bindings and rebuilding the Jumpy sample will reduce the application total size by nearly 200KB (Jumpy.app release size goes from 6.6MB to 6.4MB). Most of the size reduction comes from the binding assembly itself, but the size of monotouch.dll also goes down since many of the
Messaging.* methods (used by the bindings) won’t be required anymore.
There are other advantages, besides size, to linking a binding assembly. First, it reduces the number of selectors (from the
[Export]) that must be looked up. Each one has a small cost (both time and memory), but there can be a lot of them. Also, the linker is smart about bindings—among other things, it will remove unneeded backing fields, which will lower the memory footprint of each managed instances of those types.
While this attribute already existed before, it could not be applied at the assembly level. You can now decorate your assemblies with it so they will never be linked—i.e. nothing will be removed from them, even if you use “Link all assemblies” in your project options.
The attribute is useful when:
- Creating “contract” assemblies used for serialization
- Creating unit test assemblies, where each fixture is found using reflection
Is that really new ?
Not totally. You could already do this using project options,
[assembly:Preserve]. However, it was a bit cumbersome to select “Link All” and then use
--linkskip on all but one of your application assemblies. Project options also have a few drawbacks, because they:
- Must be copied/updated in every build configuration (Debug, Release…)
- Must be copied to every project that uses the same assembly (code sharing)
Now you have a better way to express how you want the linker to work on your application. Let’s start shrinking!