Bug 923 - UISegmentedControl: -[NSCFString BridgeSelector]: unrecognized selector sent to instance 0xe084860
Summary: UISegmentedControl: -[NSCFString BridgeSelector]: unrecognized selector sent ...
Status: RESOLVED FIXED
Alias: None
Product: iOS
Classification: Xamarin
Component: XI runtime ()
Version: 4.x
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Sebastien Pouliot
URL:
Depends on:
Blocks:
 
Reported: 2011-09-20 05:41 UTC by René Ruppert
Modified: 2011-09-22 15: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 René Ruppert 2011-09-20 05:41:21 UTC
Monotouch 4.2 beta

I have a UISegmentedControl with two images in a UIToolbar. I try to get the selected segment in an anoymous delegate. Clicking the control results in

-[NSCFString BridgeSelector]: unrecognized selector sent to instance 0xe084860

Sample code:
UISegmentedControl oBookmarkSwitch = new UISegmentedControl(new UIImage[] { UIImage.FromFile("./images/index.png"), UIImage.FromFile("./images/bookmark_grey.png") });
oBookmarkSwitch.ValueChanged += delegate {
Console.WriteLine(oBookmarkSwitch.SelectedSegment);
};
oBookmarkSwitch.Frame = new RectangleF(0, 0, 100, 30);
oBookmarkSwitch.ControlStyle = UISegmentedControlStyle.Bar;
aBarButtons.Add(new UIBarButtonItem(oBookmarkSwitch));
Comment 1 René Ruppert 2011-09-20 05:42:59 UTC
Note: making the UISegmentedControl a class member solves the isse. So I assum GC but it is unclear to me why it gets collected. I'm using managed code only IMHO.
Comment 2 Sebastien Pouliot 2011-09-20 08:43:33 UTC
The root issue is that adding a delegate to an object does not add a reference back to the original object. This is not Mono[Touch] or [un]managed specific and discussed in several forums, e.g. [1].

So if you're using a local variable to hold a UISegmentedControl then it can be collected, by the GC, once you return from that method (or even earlier if its not used anymore). 

Moving the UISegmentedControl declaration into a field will create a reference for as long as the type instance is alive (i.e. not itself collected). Since UI elements are generally parent/child related this often solve the issue (i.e. the holder/form references all child/ui-elements until it's itself freed).

[1] http://stackoverflow.com/questions/371109/garbage-collection-when-using-anonymous-delegates-for-event-handling

We're looking at ways to better identify such cases (see bug #848) and provide feedback to users (some cases are already reported). However there are multiple ways this can happen and not everyone of them offers a simple way to report the issue.
Comment 3 René Ruppert 2011-09-20 08:48:16 UTC
But in this case the UISegmentedControl is added to a UIToolbar's array of items. The toolbar is added as a subview to the view. Shouldn't adding the toolbar items create a reference? It seems to do so for UITabBarItem and UIButtons wrapped in UITabBarItem but fails immediately for UISegmentedControl.
Comment 4 Sebastien Pouliot 2011-09-20 09:00:33 UTC
It should be but I can't say from the sample code (e.g. I assumed both 'oBookmarkSwitch ' and 'aBarButtons' were local variables). It's possible that some code path inside MonoTouch forgets to keep a reference somewhere where it should (allowing the GC to collect the instance).

We'll need a small, self-contained, test case to check on this (to make sure we're debugging the exact same code that reproduce the issue). Thanks!
Comment 5 René Ruppert 2011-09-20 09:28:35 UTC
I investigated a bit more and I'm unsure now if it is a bug or not. See example code. It does not hold a reference to the UIButton that gets wrapped inside a UIBarButtonItem and FAILS if the TouchUpInside delegate is supposed to be called.
Making a non-anonymous delegate does not help. Only making the UIButton a class member solves the issue.

But to my understanding the logic is: the UIButton is wrapped in a UIBarButtonItem, the UIBarButtonItem gets added to the UIToolbar, the toolbar has a class member reference and the toolbar gets added to the window object.

To me this means, there SHOULD be a valid reference to the button and it may not be collected as we have a chain from the UIButton up to the referenced UIToolbar.

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

namespace UISegmentDemo
{
	public class Application
	{
		static void Main (string[] args)
		{
			UIApplication.Main (args);
		}
	}
	
	// The name AppDelegate is referenced in the MainWindow.xib file.
	public partial class AppDelegate : UIApplicationDelegate
	{
		// This method is invoked when the application has loaded its UI and its ready to run
		public override bool FinishedLaunching (UIApplication app, NSDictionary options)
		{
			window.MakeKeyAndVisible ();
			
			this.oToolbar = new UIToolbar();
			this.oToolbar.AutoresizingMask = UIViewAutoresizing.FlexibleWidth;
			this.oToolbar.Frame = new RectangleF(0, 20, window.Bounds.Width, 40);
			this.window.AddSubview(this.oToolbar);
			
			UIButton oBtn = UIButton.FromType(UIButtonType.RoundedRect);
			oBtn.Frame = new RectangleF(0, 0, 100, 30);
			oBtn.SetTitle("Button", UIControlState.Normal);
			oBtn.TouchUpInside += delegate {
				Console.WriteLine("Button touched");
			};
			UIBarButtonItem oBtnItem = new UIBarButtonItem(oBtn);
			
			this.oToolbar.SetItems(new UIBarButtonItem[] { oBtnItem }, false);
	
			return true;
		}
		
		private UIToolbar oToolbar;
	}
}
Comment 6 Sebastien Pouliot 2011-09-20 09:37:25 UTC
hmm... there was a very similar bug wrt SetItems([], boolean) that was fixed recently. I'll check to see if it missed 4.2. IIRC (I'll check it asap) the issue did not affect using the Items property.

what happens if you use something like:

this.oToolbar.Items = new UIBarButtonItem[] { oBtnItem };

note: I'll try your sample code later today, thanks for providing it!
Comment 7 René Ruppert 2011-09-20 10:06:53 UTC
Same issue when accessing .Items directly.
Comment 8 Sebastien Pouliot 2011-09-22 14:18:24 UTC
I can duplicate the issue.
Comment 9 Sebastien Pouliot 2011-09-22 14:37:46 UTC
It seems the problem is earlier than [Set]Items. The binding for initWithCustomView: which is used for:

    UIBarButtonItem oBtnItem = new UIBarButtonItem(oBtn);

does not retain a managed reference to 'oBtn' which allows the GC to collect it - i.e. it's a bug, monotouch (bindings) should hide this requirement from user code.
Comment 10 Sebastien Pouliot 2011-09-22 15:23:39 UTC
Fixed in master and monotouch-4.2 branch.
Thanks for the test case!