Bug 23 - System.MissingMethodException: No constructor found for MonoTouch.AVFoundation.InternalAVAudioPlayerDelegate::.ctor(System.IntPtr)
Summary: System.MissingMethodException: No constructor found for MonoTouch.AVFoundatio...
Status: RESOLVED FIXED
Alias: None
Product: iOS
Classification: Xamarin
Component: Xamarin.iOS.dll ()
Version: 4.x
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Chris Toshok
URL:
Depends on:
Blocks:
 
Reported: 2011-07-20 03:57 UTC by ml+mt
Modified: 2011-07-25 21:23 UTC (History)
3 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description ml+mt 2011-07-20 03:57:03 UTC
In a ViewController I call the following code in ViewDidLoad(): 

AudioSession.Initialize(); 
AudioSession.Category = AudioSessionCategory.MediaPlayback; 
AudioSession.SetActive(true); 

Then in response to some button presses I do: 

NSError err; 
var ap = AVAudioPlayer.FromUrl(new NSUrl("mysound.caf"), out err); 
ap.FinishedPlaying += delegate { ap.Dispose(); }; 
ap.Play(); 

And this works great, pretty responsive etc. 

Except sometimes, seemingly randomly, I get the following unhandled 
exception in release mode on the device (iPhone 3GS): 

System.MissingMethodException: No constructor found for 
MonoTouch.AVFoundation.InternalAVAudioPlayerDelegate::.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 
/Users/plasma/Source/iphone/monotouch/ObjCRuntime/Runtime.cs:154 
  at MonoTouch.ObjCRuntime.Runtime.GetNSObject (IntPtr ptr) [0x0001f] 
in /Users/plasma/Source/iphone/monotouch/ObjCRuntime/Runtime.cs:197 
  at MonoTouch.ObjCRuntime.Runtime.GetNSObjectWrapped (IntPtr ptr) 
[0x00000] in /Users/plasma/Source/iphone/monotouch/ObjCRuntime/Runtime.cs:214 
  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) 
[0x00038] in /Users/plasma/Source/iphone/monotouch/UIKit/UIApplication.cs:26 
  at MonoTouch.UIKit.UIApplication.Main (System.String[] args) 
[0x00000] in /Users/plasma/Source/iphone/monotouch/UIKit/UIApplication.cs:31 
  at MyNamespace.Application.Main (System.String[] args) [0x00016] in 
/Users/tf/Projects/MyApp/trunk/MyGuiProject/Main.cs:25 

Workaround:
The AudioSession code is required to get volume control working. 
Without that, my volume buttons change the ringer volume, not the 
media volume. 

I have to call AudioSession.SetActive(true); every time I create an 
AVAudioPlayer, otherwise I risk an AudioSessionException with message 
"Application Audio Session is not active". 

I removed the anonymous method from FinishedPlaying, and instead do 
the following. 

I have a class-scoped List<AVAudioPlayer> called toDispose. 

On button press: 

{ 
    // call Play on a freshly instantiated AVAudioPlayer ap: 
    var ap = AVAudioPlayer.FromUrl(url, out err); 
    ap.Play(); 

    // next I check to see if toDispose has a bunch of AVAudioPlayers 
piled up, and if so, dispose all but the last couple in the list (my 
samples are only 0.15 seconds long so it's unlikely I'll dispose one 
that's currently playing this way) 
    // code snipped for brevity 

    // add the current AVAudioPlayer to the list of players to keep track of 
    toDispose.Add(ap); 
} 

then finally in ViewWillDisappear() I dispose any remaining 
AVAudioPlayers in toDispose, and remove the disposed player from the 
list. 

Horrible but stable with many short samples being played, unlike the 
sample code on the MonoTouch website. 

See the following URL for discussion:

http://monotouch.2284126.n4.nabble.com/System-MissingMethodException-No-constructor-found-for-MonoTouch-AVFoundation-InternalAVAudioPlayerD-td3676678.html
Comment 1 Miguel de Icaza [MSFT] 2011-07-22 12:15:20 UTC
Would you mind providing us with a self-contained test case?

We have a few theories as to what it could be, but we need the actual sample that is crashing to fix it.
Comment 2 Chris Toshok 2011-07-25 21:23:39 UTC
So, we've been doing a lot of thinking and hacking around the issue, and the only workable solution we can come up with at this point is (unfortunately) "don't do that."

You'll need to keep a strong ref to the player around or else both managed objects (the AVAudioPlayer and the InternalAVAudioPlayerDelegate) are free to be collected by the GC.  The reason it's not 100% reproducible is just an example of how it's difficult to reason about timing of code execution and object lifetimes when a GC is involved.

you can maybe make your toDispose solution a little more exact by using player.IsPlaying.

  var ap = AVAudioPlayer.FromUrl (...);

  ap.Finished += delegate {
    if (we haven't registered a callback to clear up the list) {
      use NSObject.PerformSelector to register Register_FlushList();
    }
  };

  ap.Play ();
  toDispose.Add (ap);

  void FlushList () {
    here you iterate over toDispose, removing/disposing players that have IsPlaying = false
  }

I say "maybe" above because I'm not sure offhand if IsPlaying is set to true immediately upon calling ap.Play().  Also this presumably won't work if you're using ap.PlayAtTime.

We have made the exception message a little better in this situation - it will show the following now:

System.Exception: Selector invoked from objective-c on a managed object that has been GC'ed ---> System.MissingMethodException: No constructor found for MonoTouch.AVFoundation.InternalAVAudioPlayerDelegate::.ctor(System.IntPtr)