Using Unity 3.5.6f4 to build for the iPad, we would “randomly” see this error in the Xcode output:
System.String doesn’t implement interface System.Collections.IEnumerator
- Assertion: should not be reached at mini-trampolines.c:183
Looking at the stack trace in Xcode, I tracked down the offending statement to a foreach block in a UI framework assembly (DLL) that is used by our project. It turns out, that this is a very tricky problem to find and fix, I’m sharing on the forum for “the greater good”, since I have benefited many times from development forums on the Internet.
This problem only occurs on iOS because it is due to a limitation in the Ahead-Of-Time (AOT) compiler that is used to compile Mono byte code into native code. Thus, your app probably runs fine in the Editor, WebPlayer, Windows, Mac, etc. But, on iOS, it will crash, sometimes the crashes will appear to be random.
The reason this crash is occurring is because the IEnumerable.GetEnumerator() is being used instead of the non-generic IEnumerable.GetEnumerator() to get an enumerator object used by foreach iteration blocks. Since IEnumerable is a type of interface dispatch not supported by full AOT (see Redirecting…), the ‘enumerator’ returned by this method is actually a string object (I don’t know why), which results in a crash.
This problem may appear to be random because the ‘GetEnumerator()’ method “found” by foreach could be the System.Collections.IEnumerable (non-generic) interface implemented by the standard Mono/.NET collections, which works as expected. The ordering of the methods is not consistent, so sometimes the generic interface method is used, making the crash seem random.
I’m surprised that collections being iterated by foreach aren’t crashing left and right due to this issue, but I assume that System.Collections.IEnumerable (non-generic) is being found by foreach most of the time or perhaps it’s because when this code is in a separate assembly it gives the AOT compiler additional trouble resolving to the appropriate GetEnumerator() method.
Either way, here is an AOT-safe method to iterate over an IEnumerable collection. Replace the foreach statement(s) where the crash is occurring with this method.
using System;
using System.Linq;
using System.Collections;
using System.Reflection;
namespace Aperture
{
public class AotSafe
{
public static void ForEach<T>(object enumerable, Action<T> action)
{
if (enumerable == null)
{
return;
}
Type listType = enumerable.GetType().GetInterfaces().First(x => !(x.IsGenericType) x == typeof(IEnumerable));
if (listType == null)
{
throw new ArgumentException("Object does not implement IEnumerable interface", "enumerable");
}
MethodInfo method = listType.GetMethod("GetEnumerator");
if (method == null)
{
throw new InvalidOperationException("Failed to get 'GetEnumberator()' method info from IEnumerable type");
}
IEnumerator enumerator = null;
try
{
enumerator = (IEnumerator)method.Invoke(enumerable, null);
if (enumerator is IEnumerator)
{
while (enumerator.MoveNext())
{
action((T)enumerator.Current);
}
}
else
{
UnityEngine.Debug.Log(string.Format("{0}.GetEnumerator() returned '{1}' instead of IEnumerator.",
enumerable.ToString(),
enumerator.GetType().Name));
}
}
finally
{
IDisposable disposable = enumerator as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
}
}
}
Example usage:
AotSafe.ForEach<object>(ItemsSource, (item) =>
{
// Do something with item from collection ItemsSource
});
where ItemsSource is an IEnumerable container.