Search code examples
c#iosmonoxamarin.iosinstruments

NSActionDispatcher is Garbage Collected when Instruments is attached


I am trying to add a timer to my iOS application, for this I use the following code:

using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;

namespace MyNamespace
{
    [Register ("AppDelegate")]
    public partial class AppDelegate : UIApplicationDelegate
    {
        UIWindow window;
        public static NavigationController MyNavigationController;

        public override bool FinishedLaunching (UIApplication app, NSDictionary options)
        {
            RectangleF bounds = UIScreen.MainScreen.Bounds;
            MyNavigationController = new NavigationController(bounds);
            window = new UIWindow (bounds);
            window.RootViewController = MyNavigationController;
            window.MakeKeyAndVisible ();
            return true;
        }
    }

    public class NavigationController : UINavigationController
    {
        private RectangleF _bounds;
        private RectangleF _viewPort;

        public NavigationController(RectangleF bounds)
        {
            _bounds = bounds;
            NavigationBar.BarStyle = UIBarStyle.Black;
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            _viewPort = new RectangleF(_bounds.Location, new SizeF(_bounds.Width, _bounds.Height - NavigationBar.Frame.Height));
            ShowFirstView(); // Default to showing FirstViewController
        }

        public void ShowFirstView()
        {
            PushViewController(new FirstViewController(_viewPort), true);
        }

        public void ShowSecondView()
        {
            PushViewController(new SecondViewController(_viewPort), true);
        }
    }

    public class FirstViewController : UIViewController
    {
        public FirstViewController(RectangleF bounds)
        {
            // Empty view
            View = new UIView(bounds);
            View.BackgroundColor = UIColor.White;
            Title = "First View";

            // Simple button to navigate to next view
            UIButton button = UIButton.FromType(UIButtonType.RoundedRect);
            button.Frame = new RectangleF(10,10,300,30);
            button.SetTitle("Show next view", UIControlState.Normal);
            button.TouchUpInside += (sender, e) => { AppDelegate.MyNavigationController.ShowSecondView(); };
            View.AddSubview(button);
        }
    }

    public class SecondViewController : UIViewController
    {
        private NSTimer _timer;

        public SecondViewController(RectangleF bounds)
        {
            // Empty View
            View = new UIView(bounds);
            View.BackgroundColor = UIColor.Blue;
            Title = "Second View";

            // Create timer
            _timer = NSTimer.CreateRepeatingScheduledTimer(2f, delegate { Console.WriteLine("Timer Fired"); });
        }
    }
}

When I run this code in the Simulator it works as expected, writing out "Timer Fired" to the console every 2 seconds.

However when I attach Apple Instruments, specifically the Allocations Instrument an exception is thrown at UIApplication.Main (args, null, "AppDelegate"); in Main.cs

Selector invoked from objective-c on a managed object of type MonoTouch.Foundation.NSActionDispatcher (0x10AA2D50) that has been GC'ed

Stack Trace:

System.Exception: Selector invoked from objective-c on a managed object of type MonoTouch.Foundation.NSActionDispatcher (0x14FB5AC0) that has been GC'ed ---> System.Exception: No constructor found for MonoTouch.Foundation.NSActionDispatcher::.ctor(System.IntPtr)
  at System.Activator.CreateInstance (System.Type type, BindingFlags bindingAttr, System.Reflection.Binder binder, System.Object[] args, System.Globalization.CultureInfo culture, System.Object[] activationAttributes) [0x000f1] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System/Activator.cs:280
  at System.Activator.CreateInstance (System.Type type, System.Object[] args, System.Object[] activationAttributes) [0x00000] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System/Activator.cs:234
  at System.Activator.CreateInstance (System.Type type, System.Object[] args) [0x00000] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System/Activator.cs:229
  at MonoTouch.ObjCRuntime.Runtime.ConstructNSObject (IntPtr ptr, IntPtr klass) [0x0000d] in /Developer/MonoTouch/Source/monotouch/src/ObjCRuntime/Runtime.cs:231
  --- End of inner exception stack trace ---
  at MonoTouch.ObjCRuntime.Runtime.ConstructNSObject (IntPtr ptr, IntPtr klass) [0x00045] in /Developer/MonoTouch/Source/monotouch/src/ObjCRuntime/Runtime.cs:236
  at MonoTouch.ObjCRuntime.Runtime.GetNSObject (IntPtr ptr) [0x0001f] in /Developer/MonoTouch/Source/monotouch/src/ObjCRuntime/Runtime.cs:280
  at MonoTouch.ObjCRuntime.Runtime.GetNSObjectWrapped (IntPtr ptr) [0x00000] in /Developer/MonoTouch/Source/monotouch/src/ObjCRuntime/Runtime.cs:297
  at (wrapper native-to-managed) MonoTouch.ObjCRuntime.Runtime:GetNSObjectWrapped (intptr)
  at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr)
  at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38
  at MyNamescape.Application.Main (System.String[] args) [0x00000] in Main.cs:17

Key things to note:

  • The app runs correctly in the Simulator, under normal circumstances (No instruments)
  • The issue only occurs when the Allocations Instrument is monitoring it. If another instrument is used (such as Leaks) the issue does not occur.
  • The issue only occurs if I attach the Allocation Instrument before the timer has been setup. If I attach the Allocation Instrument once the timer is setup, then it works as expected.
  • The Allocation Instrument works throughout the other view controllers of my application
  • I couldn't find much information about NSActionDispatcher in relation to this context, or otherwise in MonoTouch
  • I'm not currently able to test on an a physical device.

So I am not sure if this is an issue with the Allocations Instrument or whether I am doing something profoundly wrong in my code. Any advise on this issue is greatly appreciated.

EDIT: I have updated the code to show the simplest complete application use case that causes the issue. (I have updated my timer code as per PouPou's comment regarding the unnecessary use of the thread)

UPDATE (Jan 17 2013): I have tried this using the iPad & iPhone simulators versions 5.0, 5.1 and 6.0, all produce the same result.

UPDATE (Jan 18 2013): Added native stack frames as per request of Rolf.

2013-01-18 13:28:47.579 TestTimer[12196:c07]   at MonoTouch.ObjCRuntime.Runtime.ConstructNSObject (IntPtr ptr, IntPtr klass) [0x00045] in /Developer/MonoTouch/Source/monotouch/src/ObjCRuntime/Runtime.cs:236 
  at MonoTouch.ObjCRuntime.Runtime.GetNSObject (IntPtr ptr) [0x0001f] in /Developer/MonoTouch/Source/monotouch/src/ObjCRuntime/Runtime.cs:280 
  at MonoTouch.ObjCRuntime.Runtime.GetNSObjectWrapped (IntPtr ptr) [0x00000] in /Developer/MonoTouch/Source/monotouch/src/ObjCRuntime/Runtime.cs:297 
  at (wrapper native-to-managed) MonoTouch.ObjCRuntime.Runtime:GetNSObjectWrapped (intptr)
  at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr)
  at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38 
  at MyNamescape.Application.Main (System.String[] args) [0x00000] in Main.cs:16 
2013-01-18 13:28:47.623 TestTimer[12196:c07] 0   TestTimer                           0x00093a22 mono_handle_exception_internal_first_pass + 3058
1   TestTimer                           0x00095102 mono_handle_exception_internal + 1602
2   TestTimer                           0x00095c4f mono_handle_exception + 47
3   TestTimer                           0x000dda72 mono_x86_throw_exception + 306
4   ???                                 0x0b3e0f8f 0x0 + 188616591
at MonoTouch.ObjCRuntime.Runtime.GetNSObject (intptr) [0x0001f] in /Developer/MonoTouch/Source/monotouch/src/ObjCRuntime/Runtime.cs:280
at MonoTouch.ObjCRuntime.Runtime.GetNSObjectWrapped (intptr) [0x00000] in /Developer/MonoTouch/Source/monotouch/src/ObjCRuntime/Runtime.cs:297
at (wrapper native-to-managed) MonoTouch.ObjCRuntime.Runtime.GetNSObjectWrapped (intptr) <IL 0x00017, 0x00094>
8   TestTimer                           0x00224873 get_managed_object_for_ptr + 115
9   TestTimer                           0x00229700 monotouch_trampoline + 448
10  Foundation                          0x019b9b90 __NSFireTimer + 97
11  CoreFoundation                      0x01315376 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22
12  CoreFoundation                      0x01314e06 __CFRunLoopDoTimer + 534
13  CoreFoundation                      0x012fca82 __CFRunLoopRun + 1810
14  CoreFoundation                      0x012fbf44 CFRunLoopRunSpecific + 276
15  CoreFoundation                      0x012fbe1b CFRunLoopRunInMode + 123
16  GraphicsServices                    0x04b807e3 GSEventRunModal + 88
17  GraphicsServices                    0x04b80668 GSEventRun + 104
18  UIKit                               0x0273965c UIApplicationMain + 1211
at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) <IL 0x00056, 0x001f5>
at MonoTouch.UIKit.UIApplication.Main (string[],string,string) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38
at MyNamescape.Application.Main (string[]) [0x00000] in Main.cs:16
at (wrapper runtime-invoke) <Module>.runtime_invoke_void_object (object,intptr,intptr,intptr) <IL 0x00049, 0x0014e>
23  TestTimer                           0x00010252 mono_jit_runtime_invoke + 722
24  TestTimer                           0x0017478e mono_runtime_invoke + 126
25  TestTimer                           0x00178be4 mono_runtime_exec_main + 420
26  TestTimer                           0x00178f55 mono_runtime_run_main + 725
27  TestTimer                           0x0006ba65 mono_jit_exec + 149
28  TestTimer                           0x0021f65d main + 2013
29  TestTimer                           0x00003125 start + 53

Solution

  • I've (finally) tracked this down to be a bug in MonoTouch (Instruments intercepts the retain/release methods, which confuses MonoTouch). The bug has been fixed, and will be included in the next stable version (6.0.9).