Xamarin can be thought of as a C# wrapper of the Android Java SDK and the iOS Objective-C SDK. It works because each managed instance is connected to a corresponding native instance. The design is an impressive feat of engineering and works well in practice. However, there are implementation flaws which must be mitigated to successfully build non-trivial apps.

Xamarin.iOS leaks memory when you have a memory cycle that twice crosses the managed/native boundary. It is necessary to use weak reference wrappers to prevent anonymous delegate capture from swallowing up references to everything.

Instruments is how you find leaks; open it from the Xcode > Open Developer Tool > Instruments menu. Attach to your Xamarin.iOS app and use the memory leak profiling to detect leaks.

In the below example, TextField is a local variable and LocalState is a field on long-living object. The delegate capture implicitly takes references to TextField and LocalState. This seems to be enough to cause a memory leak.

var TextField = new UITextField();

TextField.EditingChanged += (Sender, Event) =>

{

LocalState.Value = TextField.Text;

};

I must confess to a lack of clarity when it comes to defining a reproduction of the issue. But I am clear on the shape of the problem: you must not allow implicit variable/field capture when handling an event on an iOS view. This can be avoided if you use weak references to pass variables to the body of your delegate. The solution is gross but effective:

var TextField = new UITextField();

TextField.EditingChanged += WeakHelpers.WeakEventHandlerWrapper(LocalState, TextField, (local, field, Sender, Event) =>

{

local.Value = field.Text;

};

How gross you ask? This is the helper method behind the scenes:

public static EventHandler WeakEventHandlerWrapper<T1, T2>(T1 o1, T2 o2, Action<T1, T2, object, EventArgs> Delegate)

where T1 : class

where T2 : class

{

var w1 = new WeakReference<T1>(o1);

var w2 = new WeakReference<T2>(o2);



return (sender, args) =>

{

if (!w1.TryGetTarget(out T1 s1) || !w2.TryGetTarget(out T2 s2))

return;

Delegate(s1, s2, sender, args);

};

}

The good news is this problem is handled internal to the Invention iOS implementation. Otherwise, you will need to mitigate this issue everywhere in your code to avoid out-of-memory crashing of your Xamarin.iOS app.