Bug 51966 - Unrelated NSIndexPath objects change when calling Dispose
Summary: Unrelated NSIndexPath objects change when calling Dispose
Alias: None
Product: iOS
Classification: Xamarin
Component: BCL Class Libraries ()
Version: XI 10.3 (iOS 10.2)
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Bugzilla
Depends on:
Reported: 2017-01-30 18:45 UTC by Daniel Burton
Modified: 2017-01-31 15:19 UTC (History)
4 users (show)

Is this bug a regression?: No
Last known good build:

Xcode Screenshot (295.04 KB, image/png)
2017-01-31 01:48 UTC, Alex Soto [MSFT]

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:

Description Daniel Burton 2017-01-30 18:45:22 UTC
NSIndexPath objects that refer to a row/section will be changed when some other NSIndexPath that has the same row/section is disposed.

Sample code:

var someOtherIndexPath = NSIndexPath.FromRowSection(1, 1);
var indexPath = NSIndexPath.FromRowSection(1, 1);
Console.WriteLine($"section = {indexPath.Section} row = {indexPath.Row}");
Console.WriteLine($"section = {indexPath.Section} row = {indexPath.Row}");


section = 1 row = 1
section = 0 row = 0

Note: the value of indexPath should not have changed.

Also the following code:

var one = NSIndexPath.FromRowSection(1, 2);
var two = NSIndexPath.FromRowSection(1, 2);

has the following output:

Comment 1 Alex Soto [MSFT] 2017-01-31 01:48:56 UTC
Created attachment 19652 [details]
Xcode Screenshot

Hello Daniel

TL;DR: The unmanaged land is returning the same object.
Comment 2 Alex Soto [MSFT] 2017-01-31 02:03:17 UTC
This is not a bug in Xamarin.iOS, what is happening is that the ObjC selector indexPathForRow:inSection: is actually returning the same instance, you can verify this by creating an Xcode project and call that selector and verify using lldb (I have attached a screenshot showing this on comment #1).

NSIndexPath.FromRowSection is actually calling indexPathForRow:inSection: underneath so we actually check if we have a managed object already wrapping the returned object from unmanaged land, if we have it we return it to you, if we don't we create a new managed object. This is why if you dispose one of the two instances the other gets disposed too, we can't really have two managed objects pointing to the same unmanaged object because imagine if both want to dispose the unmanaged resources that would blow at some point.

On why Apple is doing this with indexPathForRow:inSection: I am not sure, docs are not clear on this I guess some sort of catching is happening behind the scenes since this Object IIRC is mostly meant to be used by collections like UITableView/UICollectionView.

Hope this helps!
Comment 3 Daniel Burton 2017-01-31 10:46:00 UTC
ObjC with ARC off:

NSIndexPath *path1 = [NSIndexPath indexPathForRow:1 inSection:1];
NSIndexPath *path2 = [NSIndexPath indexPathForRow:1 inSection:1];

printf("%p %p\n", path1, path2);
printf("row = %ld section = %ld\n", (long)path1.row, path1.section);

[path2 release];
printf("%p %p\n", path1, path2);
printf("row = %ld section = %ld\n", (long)path1.row, path1.section);

0xc000000000200116 0xc000000000200116
row = 1 section = 1
0xc000000000200116 0xc000000000200116
row = 1 section = 1

It should not matter what is happening under the covers. If I ask for two objects, even if they have the same value, changing one object should never change the other object.
Comment 4 Daniel Burton 2017-01-31 10:53:12 UTC
OK, I'll take back, "changing one object should never change the other object." but in this case it shouldn't.
Comment 5 Daniel Burton 2017-01-31 15:19:51 UTC
"I guess some sort of catching is happening behind the scenes..."
Apparently not. If you look at the values returned from various NSIndexPaths it is easy to see that in fact it does not return a reference to an object but is simply a packed struct. 

    NSIndexPath *path11 = [NSIndexPath indexPathForRow:1 inSection:1];
    NSIndexPath *path13 = [NSIndexPath indexPathForRow:1 inSection:2];
    NSIndexPath *path15 = [NSIndexPath indexPathForRow:1 inSection:5];
    NSIndexPath *path16 = [NSIndexPath indexPathForRow:1 inSection:6];
    NSIndexPath *path17 = [NSIndexPath indexPathForRow:1 inSection:7];
    NSIndexPath *path18 = [NSIndexPath indexPathForRow:1 inSection:8];
    NSIndexPath *path19 = [NSIndexPath indexPathForRow:1 inSection:3];
    NSIndexPath *path110 = [NSIndexPath indexPathForRow:2 inSection:1];
    NSIndexPath *path121 = [NSIndexPath indexPathForRow:2 inSection:2];
    NSIndexPath *path122 = [NSIndexPath indexPathForRow:2 inSection:5];
    NSIndexPath *path123 = [NSIndexPath indexPathForRow:2 inSection:6];
    NSIndexPath *path124 = [NSIndexPath indexPathForRow:2 inSection:7];
    NSIndexPath *path125 = [NSIndexPath indexPathForRow:2 inSection:8];
    NSIndexPath *path126 = [NSIndexPath indexPathForRow:2 inSection:3];

path11	NSIndexPath *	0xc000000000200116
path13	NSIndexPath *	0xc000000000200216
path15	NSIndexPath *	0xc000000000200516
path16	NSIndexPath *	0xc000000000200616
path17	NSIndexPath *	0xc000000000200716
path18	NSIndexPath *	0xc000000000200816
path19	NSIndexPath *	0xc000000000200316
path110	NSIndexPath *	0xc000000000400116
path121	NSIndexPath *	0xc000000000400216
path122	NSIndexPath *	0xc000000000400516
path123	NSIndexPath *	0xc000000000400616
path124	NSIndexPath *	0xc000000000400716
path125	NSIndexPath *	0xc000000000400816
path126	NSIndexPath *	0xc000000000400316