Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[iOS13] NSIndexPath Dispose Clarification #7206

Closed
projectgoav opened this issue Oct 10, 2019 · 7 comments
Closed

[iOS13] NSIndexPath Dispose Clarification #7206

projectgoav opened this issue Oct 10, 2019 · 7 comments

Comments

@projectgoav
Copy link

Previous advice I've recieved from the Xamarin team is to dispose of Xamarin objects as soon as possible after use. I believe it will sever the connections into the Obj-C land and reduces the work required by GC and the Obj-C runtime.

Our app has quite a number of table and collection views which have many sections and are backed by a single collection. We do a fair bit of work to translate NSIndexPath Row/Section to an index path in the form CollectionIndex/0.

We have a number of table and collection view helper classes but the rough code follows something like the following:

// UITableViewSource subclass...
public void GetCell(UITableView tableView, NSIndexPath indexPath)
{
    var itemAtIndex = GetItemAt(indexPath);

    // Issue Occurs
    var cell = tableView.DequeueCell(tableView, indexPath);

    cell.ConfigureWithItem(itemAtIndex);

    return cell;
}


private object ItemAtIndex(NSIndexPath indexPath)
{
    //Turns Row/Section to Row/0 
    int rowIndex = GetRowIndexFromIndexPath(indexPath);

    using (var actualIndexPath = NSIndexPath.FromRowSection(rowIndex, 0))
    {
        return base.ItemAtIndex(actualIndexPath);
    }
}

This code was working fine until the release of iOS 13. Whenever this runs the line marked //IssueOccurs asserts with the message UITableView dequeue cell with 'nil' index path.

On further investigation it shows that any index path created regardless of of Row/Section provided will have the same underlying ClassHandle property. Therefore, any index path that is disposed, disposes of the underlying index path class.

With iOS13, Apple appear to have tightened up the UITableView API to assert when given a disposed index path.

If the recommended way is to dispose of Xamarin objects is there a bug in how Xamarin are surfacing NSIndexPath indexes?

I'd have expected that each index path would have a different class handle. It makes sense to me you might recieve the same backing class handle when requesting an index path with the same row/section value in the same scope. It surprises me that all index paths are backed by the same handle regardless of row/section value.

In this case, if I dispose of actualIndexPath I would expect that only that index path is disposed and the original index path would remain untouched.

Sample Application (Zip below)

I've attached a sample application annotated and demonstrating what I've seen.

If you run the app on iOS 12 or earlier, the app should run showing a TableView with 1 (empty) cell.

If you run the app on iOS 13 the app will assert and crash before the view appears. If you remove the using statement around the construction of the seperate index path everything works as I would expect.

Note: UICollectionView doesn't assert when given a disposed index path in the same way UITableView does.

Environment

=== Visual Studio Community 2019 for Mac ===

Version 8.3.2 (build 32)
Installation UUID: aeb76010-1b95-49ec-b566-bd70d1159c6b
	GTK+ 2.24.23 (Raleigh theme)
	Xamarin.Mac 5.16.1.24 (d16-3 / 08809f5b)

	Package version: 604000208

=== Mono Framework MDK ===

Runtime:
	Mono 6.4.0.208 (2019-06/07c23f2ca43) (64-bit)
	Package version: 604000208

=== NuGet ===

Version: 5.3.0.6192

=== .NET Core SDK ===

SDK: /usr/local/share/dotnet/sdk/3.0.100/Sdks
SDK Versions:
	3.0.100
	2.1.701
	2.1.700
	2.1.505
	2.1.504
	2.1.503
	2.1.302
	2.1.301
	2.1.4
	2.0.0
MSBuild SDKs: /Library/Frameworks/Mono.framework/Versions/6.4.0/lib/mono/msbuild/Current/bin/Sdks

=== .NET Core Runtime ===

Runtime: /usr/local/share/dotnet/dotnet
Runtime Versions:
	3.0.0
	2.1.13
	2.1.12
	2.1.11
	2.1.9
	2.1.8
	2.1.7
	2.1.2
	2.1.1
	2.0.5
	2.0.0

=== Xamarin.Profiler ===

Version: 1.6.11.16
Location: /Applications/Xamarin Profiler.app/Contents/MacOS/Xamarin Profiler

=== Updater ===

Version: 11

=== Xamarin.Android ===

Version: 10.0.3.0 (Visual Studio Community)
Commit: xamarin-android/d16-3/4d45b41
Android SDK: /Users/ewan/Library/Developer/Xamarin/android-sdk-macosx
	Supported Android versions:
		None installed

SDK Tools Version: 26.1.1
SDK Platform Tools Version: 28.0.2
SDK Build Tools Version: 28.0.3

Build Information: 
Mono: mono/mono/2019-06@5608fe0abb3
Java.Interop: xamarin/java.interop/d16-3@5836f58
LibZipSharp: grendello/LibZipSharp/d16-3@71f4a94
LibZip: nih-at/libzip/rel-1-5-1@b95cf3fd
ProGuard: xamarin/proguard/master@905836d
SQLite: xamarin/sqlite/3.27.1@8212a2d
Xamarin.Android Tools: xamarin/xamarin-android-tools/d16-3@cb41333

=== Microsoft Mobile OpenJDK ===

Java SDK: /Users/ewan/Library/Developer/Xamarin/jdk/microsoft_dist_openjdk_1.8.0.25
1.8.0-25
Android Designer EPL code available here:
https://github.com/xamarin/AndroidDesigner.EPL

=== Android SDK Manager ===

Version: 1.4.0.65
Hash: c33b107
Branch: remotes/origin/d16-3
Build date: 2019-09-19 20:42:44 UTC

=== Android Device Manager ===

Version: 1.2.0.115
Hash: 724ea69
Branch: remotes/origin/d16-3
Build date: 2019-09-19 20:43:06 UTC

=== Apple Developer Tools ===

Xcode 11.1 (15405)
Build 11A1027

=== Xamarin.Mac ===

Version: 6.2.0.47 (Visual Studio Community)
Hash: c2cbd3480
Branch: d16-3
Build date: 2019-09-30 22:37:32-0400

=== Xamarin.iOS ===

Version: 13.2.0.47 (Visual Studio Community)
Hash: c2cbd3480
Branch: d16-3
Build date: 2019-09-30 22:37:32-0400

=== Xamarin Designer ===

Version: 16.3.0.247
Hash: 52eac1a9e
Branch: remotes/origin/d16-3
Build date: 2019-10-03 23:04:28 UTC

=== Xamarin Inspector ===

Version: 1.4.3
Hash: db27525
Branch: 1.4-release
Build date: Mon, 09 Jul 2018 21:20:18 GMT
Client compatibility: 1

=== Build Information ===

Release ID: 803020032
Git revision: cac56af22091d80a4c339532595ed90228d87eb0
Build date: 2019-10-04 06:43:01+00
Build branch: release-8.3
Xamarin extensions: 2fc34e1d5c594f52abf0e0e9e14f4945adec6c7f

=== Operating System ===

Mac OS X 10.15.0
Darwin 19.0.0 Darwin Kernel Version 19.0.0
    Wed Sep 25 21:13:50 PDT 2019
    root:xnu-6153.11.26~4/RELEASE_X86_64 x86_64

Example Project (If Possible)

NilIndexPath.zip

@spouliot
Copy link
Contributor

Some quick (not executed the test case yet), semi-related notes:

ClassHandle is the handle of the Class (so the type in .net). So it should be identical across all instance of the same type. Sometime it's not when multiple internal types are exposed as one (e.g. what Apple expose NSString is called a class cluster, many internal types exposed as one). Ideally ClassHandle would be static - but .net does not let us override static members.

What you want to look is more likely Handle property which is different for each instance. Again that's not always the case since some API can cache instances, e.g. if you create a several NSString ("") then they will likely all point to the same, shared, instance (and will have the same Handle value).

@spouliot
Copy link
Contributor

I do have a theory (basically the caching I mentioned earlier) but the attached sample does not crash for me (but it does for someone else on the team).

Just to be sure we see the exact same thing, can you copy the full abort message (it's generally followed by a lot of things) and also attach the crash report to the issue ? thanks!

@spouliot
Copy link
Contributor

2019-10-10 16:44:48.052718-0400 NilIndexPath[49869:20760530] IndexPath > -3709106215576712453
2019-10-10 16:44:48.052970-0400 NilIndexPath[49869:20760530] AnotherIndexPath > -3709106215576712453
2019-10-10 16:44:48.053093-0400 NilIndexPath[49869:20760530] TotallyDifferentIndexPath > -3709106215551546629

It still does not crash (for me) but both IndexPath and AnotherIndexPath have the same Handle value, implying caching - which might be something new in iOS 13.x

@spouliot
Copy link
Contributor

Looks similar to https://bugzilla.xamarin.com/show_bug.cgi?id=25511

Adding

Console.WriteLine ($"ReferenceEquals: {Object.ReferenceEquals (indexPath, anotherIndexPath)}");

prints

2019-10-10 16:50:19.973904-0400 NilIndexPath[49904:20767828] ReferenceEquals: True

So the value is identical and iOS (like only 13+ to be confirmed) return a cached value. Many API, like NSIndexPath.FromRowSection (indexPathForRow:inSection: in ObjC) do not guarantee you to return a new instance.

Initializes an index path with the indexes of a specific row and section in a table view.

Return Value
An NSIndexPath object.

https://developer.apple.com/documentation/foundation/nsindexpath/1614934-indexpathforrow?language=objc

Since Xamarin.iOS already know that handle it assume it's the same instance and returns it. That's normal since the ObjC code could be id get (id a) { return a; } and that would require the same instance to be returned (not a new one).

Anyhow the problem manifest itself when your code proceed to dispose it. The using clause calls Dispose, which makes us set Handle to IntPtr.Zero. Then the code calls DequeueReusableCell when the same instance (but a different variable) and that is now, in ObjC, a nil instance -> BANG!

Previous advice I've recieved from the Xamarin team is to dispose of Xamarin objects as soon as possible after use.

It's generally a good advice but:

  • be sure to dispose them after use [1];

  • only dispose objects that you own. In this case you did not really own it [1];

[1] I realize that, due to caching, it's not easy/foolproof to know if you can dispose of something :(

  • focus on disposing large (memory wise) objects. Those are generally not cached (too costly) and freeing them immediately has most impact. Let the GC handle the smaller ones.

@projectgoav
Copy link
Author

projectgoav commented Oct 11, 2019

Thanks for looking into this. I've looked into your comments this morning.

What you want to look is more likely Handle property which is different for each instance.

I can confirm that that following code operates the same on iOS 10 -> 13.

var ip1 = NSIndexPath.FromRowSection(0, 0);    //handle: AAAAA
var ip2 = NSIndexPath.FromRowSection(0, 0);    //handle: AAAAA
var ip3 = NSIndexPath.FromRowSection(1, 0);    //handle: ZZZZZ

var s1 = new NSString(string.Empty);    //handle: BBBBB
var s2 = new NSString(string.Empty);    //handle: BBBBB
var s3 = new NSString(string.Empty);    //handle: YYYYY

ip1 & ip2 have the same handles. ip3 has a different handle.
s1 & s2 has the same handles. s3 has a different handle.

This makes sense to me and I can understand the reason that s1 / ip1 and s2 / ip2 can share a handle.

if I then run the following:

s.1Dispose();

// s1 Handle:
// s2 Handle: BBBBB
// s3 Handle: ZZZZZ

ip1.Dispose();

// ip1 Handle: 
// ip2 Handle:
// ip3 Handle: ZZZZZ

I don't understand the result for this.

For the NSString instances only the handle for s1 is changed. s2 & s3 still have their handles set to something other than 0.

For the NSIndexPath the handles for both ip1 and ip2 have changed. I don't believe this is correct.

be sure to dispose them after use

In the snippet below I would say I am disposing of the index path after it has been used. Unfortunately it has the side effect of disposing the index path passed into the function. We then pass this index path to the call to DequeueCell which means we're essentially passing in nil for the index path.

Apple seem to have tighted up this API call in iOS13 causing the application to assert on a nil index path and thus has brought this to our attention, Previous iOS verisons accepted this quite happily so we were unware of it.

/* 'indexPath' surfaced to us from Xamarin by the Obj-C runtime requesting a cell
 * from the table view. */
public void GetItemAt(NSIndexPath indexPath) 
{
    int rowIndex = GetRowIndexFromIndexPath(indexPath);

    // 'anotherIndexPath is created for use...
    using (var anotherIndexPath = NSIndexPath.FromRowSection(rowIndex, 0))
    {
        return base.GetItemAt(anotherIndexPath);
    }

    /* 'anotherIndexPath' has been used and so we dispose it. We still want to 
     * use 'indexPath' but the act of disposing THIS index path instance also 
     * disposes the one given to us from Xamarin. */    
}

A similar example in Swift does not show this. Should swift do the same thing, I'd expect the second lot of memory addresses printed out to have ip1 as nil.

weak var ip1 = NSIndexPath(row: 1,  section: 1);
var ip2 = NSIndexPath(row: 1,  section: 1);
var ip3 = NSIndexPath(row: 12, section: 1);
        
let addr_ip1_1 = Unmanaged.passUnretained(ip1!).toOpaque();
let addr_ip2_1 = Unmanaged.passUnretained(ip2).toOpaque();
let addr_ip3_1 = Unmanaged.passUnretained(ip3).toOpaque();
        
debugPrint("ip1 Handle: \(addr_ip1_1)");
debugPrint("ip2 Handle: \(addr_ip2_1)");
debugPrint("ip3 Handle: \(addr_ip3_1)");
        
debugPrint("> Mock GetItemAt...");
        
indexPathVoodoo(ip: &ip1!);
        
debugPrint("< Mock GetItemAt...");
        
let addr_ip1_2 = Unmanaged.passUnretained(ip1!).toOpaque();
let addr_ip2_2 = Unmanaged.passUnretained(ip2).toOpaque();
let addr_ip3_2 = Unmanaged.passUnretained(ip3).toOpaque();
        
debugPrint("ip1 Handle: \(addr_ip1_2)");
debugPrint("ip2 Handle: \(addr_ip2_2)");
debugPrint("ip3 Handle: \(addr_ip3_2)");
        
debugPrint("Done.");


func indexPathVoodoo(ip : inout NSIndexPath)
{
    let another_ip = NSIndexPath(row: ip.row, section: 1);
    
    let addr_ip = Unmanaged.passUnretained(ip).toOpaque();
    let addr_aip = Unmanaged.passUnretained(another_ip).toOpaque();
    
    debugPrint(" ip handle: \(addr_ip)");
    debugPrint("aip handle: \(addr_aip)");

    // another_ip goes out of scope so Swift will clean it up
}

With output:

"ip1 Handle: AAAAA"
"ip2 Handle: AAAAA"
"ip3 Handle: ZZZZZ"
"> Mock GetItemAt..."
" ip handle: AAAAA"
"aip handle: AAAAA"
"< Mock GetItemAt..."
"ip1 Handle: AAAAA"
"ip2 Handle: AAAAA"
"ip3 Handle: ZZZZZ"

Many API, ... do not guarantee you to return a new instance.

I'd have suspected any API that does this would have it explicitly mentioned in the Apple documentation, such as UIImage:imageNamed:(...).

https://developer.apple.com/documentation/uikit/uiimage/1624146-imagenamed?language=objc

Just to be sure we see the exact same thing, can you copy the full abort message (it's generally followed by a lot of things) and also attach the crash report to the issue ? thanks!

The exception reported to us by our crash reporting system:

Foundation.MonoTouchException: Objective-C exception thrown.  Name: NSInternalInconsistencyException Reason: Attempted to dequeue a cell for a nil index path

We've had customer reports of this from the following iOS verisons:

  • 13.1
  • 13.1.2
  • 13.2 (Beta)

Output from Visual Studio Mac:

2019-10-11 09:29:48.923469+0100 NilIndexPath[7826:612297] IndexPath Class > -8214870247095573637
2019-10-11 09:29:48.923738+0100 NilIndexPath[7826:612297] AnotherIndexPath Class > -8214870247095573637
2019-10-11 09:29:48.923869+0100 NilIndexPath[7826:612297] TotallyDifferentIndexPath Class > -8214870247120739461
2019-10-11 09:29:48.926814+0100 NilIndexPath[7826:612297] *** Assertion failure in -[UITableView _dequeueReusableCellWithIdentifier:forIndexPath:usingPresentationValues:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore_Sim/UIKit-3899.22.15/UITableView.m:8602

=================================================================
	Native Crash Reporting
=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries 
used by your application.
=================================================================

=================================================================
	Native stacktrace:
=================================================================
	0x100ea6f95 - /Users/ewan/Library/Developer/CoreSimulator/Devices/58D66388-AB1B-43B6-A766-7BDCF881F683/data/Containers/Bundle/Application/AE5AA7EE-E5F2-47A0-803F-B03A70AA12EF/NilIndexPath.app/NilIndexPath : mono_dump_native_crash_info
	0x100e9b5e5 - /Users/ewan/Library/Developer/CoreSimulator/Devices/58D66388-AB1B-43B6-A766-7BDCF881F683/data/Containers/Bundle/Application/AE5AA7EE-E5F2-47A0-803F-B03A70AA12EF/NilIndexPath.app/NilIndexPath : mono_handle_native_crash
	0x100ead7a1 - /Users/ewan/Library/Developer/CoreSimulator/Devices/58D66388-AB1B-43B6-A766-7BDCF881F683/data/Containers/Bundle/Application/AE5AA7EE-E5F2-47A0-803F-B03A70AA12EF/NilIndexPath.app/NilIndexPath : mono_sigsegv_signal_handler_debug
	0x7fff518c9b1d - /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/system/libsystem_platform.dylib : _sigtramp
	0x100f45fd9 - /Users/ewan/Library/Developer/CoreSimulator/Devices/58D66388-AB1B-43B6-A766-7BDCF881F683/data/Containers/Bundle/Application/AE5AA7EE-E5F2-47A0-803F-B03A70AA12EF/NilIndexPath.app/NilIndexPath : mono_class_get_checked
	0x100e9a69e - /Users/ewan/Library/Developer/CoreSimulator/Devices/58D66388-AB1B-43B6-A766-7BDCF881F683/data/Containers/Bundle/Application/AE5AA7EE-E5F2-47A0-803F-B03A70AA12EF/NilIndexPath.app/NilIndexPath : mono_handle_exception_internal
	0x100e99779 - /Users/ewan/Library/Developer/CoreSimulator/Devices/58D66388-AB1B-43B6-A766-7BDCF881F683/data/Containers/Bundle/Application/AE5AA7EE-E5F2-47A0-803F-B03A70AA12EF/NilIndexPath.app/NilIndexPath : mono_handle_exception
	0x100e1d87f - /Users/ewan/Library/Developer/CoreSimulator/Devices/58D66388-AB1B-43B6-A766-7BDCF881F683/data/Containers/Bundle/Application/AE5AA7EE-E5F2-47A0-803F-B03A70AA12EF/NilIndexPath.app/NilIndexPath : mono_amd64_throw_exception
	0x1034925b0 - Unknown
	0x1010cf6c3 - /Users/ewan/Library/Developer/CoreSimulator/Devices/58D66388-AB1B-43B6-A766-7BDCF881F683/data/Containers/Bundle/Application/AE5AA7EE-E5F2-47A0-803F-B03A70AA12EF/NilIndexPath.app/NilIndexPath : _ZL17exception_handlerP11NSException
	0x7fff23baa64d - /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation : __handleUncaughtException
	0x7fff50864d76 - /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libobjc.A.dylib : _ZL15_objc_terminatev
	0x7fff4f6dfe97 - /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libc++abi.dylib : _ZSt11__terminatePFvvE
	0x7fff4f6df8fe - /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libc++abi.dylib : __cxa_get_exception_ptr
	0x7fff4f6df8c5 - /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libc++abi.dylib : _ZN10__cxxabiv1L22exception_cleanup_funcE19_Unwind_Reason_CodeP17_Unwind_Exception
	0x7fff50864c44 - /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libobjc.A.dylib : _ZL26_objc_exception_destructorPv
	0x1010d0de0 - /Users/ewan/Library/Developer/CoreSimulator/Devices/58D66388-AB1B-43B6-A766-7BDCF881F683/data/Containers/Bundle/Application/AE5AA7EE-E5F2-47A0-803F-B03A70AA12EF/NilIndexPath.app/NilIndexPath : xamarin_process_nsexception_using_mode
	0x1010d0cc7 - /Users/ewan/Library/Developer/CoreSimulator/Devices/58D66388-AB1B-43B6-A766-7BDCF881F683/data/Containers/Bundle/Application/AE5AA7EE-E5F2-47A0-803F-B03A70AA12EF/NilIndexPath.app/NilIndexPath : xamarin_process_nsexception
	0x1010e11a9 - /Users/ewan/Library/Developer/CoreSimulator/Devices/58D66388-AB1B-43B6-A766-7BDCF881F683/data/Containers/Bundle/Application/AE5AA7EE-E5F2-47A0-803F-B03A70AA12EF/NilIndexPath.app/NilIndexPath : xamarin_dyn_objc_msgSend
	0x10664faee - Unknown

=================================================================
	Basic Fault Address Reporting
=================================================================
Memory around native instruction pointer (0x100f86064):0x100f86054  47 20 5d c3 0f 1f 84 00 00 00 00 00 55 48 89 e5  G ].........UH..
0x100f86064  8b 47 20 a9 00 00 00 02 75 0c a9 00 00 00 10 75  .G .....u......u
0x100f86074  26 48 8b 07 5d c3 48 8d 3d 26 c4 1b 00 48 8d 35  &H..].H.=&...H.5
0x100f86084  a3 bf 1b 00 48 8d 0d 17 c0 1b 00 ba 79 03 00 00  ....H.......y...

=================================================================
	Managed Stacktrace:
=================================================================
	  at <unknown> <0xffffffff>
	  at ObjCRuntime.Messaging:IntPtr_objc_msgSend_IntPtr_IntPtr <0x0011d>
	  at UIKit.UITableView:DequeueReusableCell <0x002ea>
	  at Source:GetCell <0x00108>
	  at <Module>:runtime_invoke_object__this___object_object <0x001d5>
	  at <unknown> <0xffffffff>
	  at UIKit.UIApplication:UIApplicationMain <0x00251>
	  at UIKit.UIApplication:Main <0x000b2>
	  at UIKit.UIApplication:Main <0x00132>
	  at NilIndexPath.Application:Main <0x00092>
	  at <Module>:runtime_invoke_void_object <0x00198>
=================================================================

@spouliot
Copy link
Contributor

Typo for

var s3 = new NSString(string.Empty);    //handle: YYYYY

? you have a different handle so it's likely not string.Empty again

The difference there is that your code is doing a new, so there's a different managed instance for each NSString and they can be disposed independently.

A call to NSIndexPath.FromRowSection does not necessarily mean a new (managed) instance will be created (by Xamarin.iOS). We'll return an existing instance if it exists.

Think of native code doing

static id foo = constant;
id FromRowSection () { return foo; }

If you call FromRowSection a 1000 times you'll end up, in native code, with a single instance. If Xamarin.iOS assumed a new instance comes out of every API call then you would have 1000 managed instances (and would run out of memory very quickly).

Xamarin.iOS does not know how each native method works, but if the same handle is returned then we use the existing managed instance. If not then we create a new one.

@projectgoav
Copy link
Author

Typo for

Yes, indeed. Sorry about that...

if the same handle is returned then we use the existing managed instance. If not then we create a new one.

That makes a lot of sense. Thanks again for looking into this and providing some clarification :)

@ghost ghost locked as resolved and limited conversation to collaborators May 1, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants