close
Skip to content

[ObjCRuntime] Provide a way to remove an object from the object map.#25166

Merged
rolfbjarne merged 4 commits into
mainfrom
dev/rolf/object-map-removal
Apr 17, 2026
Merged

[ObjCRuntime] Provide a way to remove an object from the object map.#25166
rolfbjarne merged 4 commits into
mainfrom
dev/rolf/object-map-removal

Conversation

@rolfbjarne

Copy link
Copy Markdown
Member

Problem: empty NSStrings (@"") are singletons, and trying to create one always returns the same handle.

This causes problems for the following sequence of events:

  • Such a singleton is exposed to managed code, and a corresponding managed wrapper is created.
  • The managed wrapper is manually disposed.
  • The singleton surfaces again, and now we can end up returning the existing (disposed) wrapper from the object map instead of a new managed instance.

For empty NSStrings in particular:

  1. There's a managed static NSString.Empty field, with a managed wrapper for a native @"" singleton.
  2. Tests can create managed wrappers for @"", and if those managed wrappers are disposed, the managed NSString.Empty field now points to a disposed managed wrapper.

The MonoTouchFixtures.Foundation.BundleTest.GetLocalizedString test in particular disposes of strings, and can dispose NSString.Empty. This is from a modified runtime where an exception is thrown if NSString.Empty is disposed:

MonoTouchFixtures.Foundation.BundleTest
    [FAIL] GetLocalizedString : System.InvalidOperationException : This Foundation.NSString instance was unexpectedly disposed
       at Foundation.NSObject.Dispose(Boolean disposing) in /Users/rolf/work/dotnet/macios/main/macios/src/Foundation/NSObject2.cs:line 1108
       at Foundation.NSObject.Dispose() in /Users/rolf/work/dotnet/macios/main/macios/src/Foundation/NSObject2.cs:line 365
       at MonoTouchFixtures.Foundation.BundleTest.GetLocalizedString()
       at MonoTouchFixtures.Foundation.BundleTest.GetLocalizedString()
       at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
       at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
       at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
       at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

Fixes this test failure:

MonoTouchFixtures.Foundation.StringTest
    [FAIL] ReleaseEmptyString :   RetainCount
    Expected: 18446744073709551615
    But was:  0
        at MonoTouchFixtures.Foundation.StringTest.ReleaseEmptyString()

because this test calls release a number of times on the static NSString.Empty field, and expects a certain retainCount afterwards, but that fails because NSString.Empty has been disposed, and then RetainCount returns 0 because it's being called on a null native object.

Problem: empty NSStrings (@"") are singletons, and trying to create one always returns the same handle.

This causes problems for the following sequence of events:

* Such a singleton is exposed to managed code, and a corresponding managed wrapper is created.
* The managed wrapper is manually disposed.
* The singleton surfaces again, and now we can end up returning the existing (disposed) wrapper from the object map instead of a new managed instance.

For empty NSStrings in particular:

1. There's a managed static NSString.Empty field, with a managed wrapper for a native @"" singleton.
2. Tests can create managed wrappers for @"", and if those managed wrappers are disposed, the managed NSString.Empty field now points to a disposed managed wrapper.

The MonoTouchFixtures.Foundation.BundleTest.GetLocalizedString test in particular disposes of strings, and can dispose NSString.Empty. This is from a modified runtime where an exception is thrown if NSString.Empty is disposed:

    MonoTouchFixtures.Foundation.BundleTest
        [FAIL] GetLocalizedString : System.InvalidOperationException : This Foundation.NSString instance was unexpectedly disposed
           at Foundation.NSObject.Dispose(Boolean disposing) in /Users/rolf/work/dotnet/macios/main/macios/src/Foundation/NSObject2.cs:line 1108
           at Foundation.NSObject.Dispose() in /Users/rolf/work/dotnet/macios/main/macios/src/Foundation/NSObject2.cs:line 365
           at MonoTouchFixtures.Foundation.BundleTest.GetLocalizedString()
           at MonoTouchFixtures.Foundation.BundleTest.GetLocalizedString()
           at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
           at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
           at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
           at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

Fixes this test failure:

    MonoTouchFixtures.Foundation.StringTest
        [FAIL] ReleaseEmptyString :   RetainCount
        Expected: 18446744073709551615
        But was:  0
            at MonoTouchFixtures.Foundation.StringTest.ReleaseEmptyString()

because this test calls release a number of times on the static `NSString.Empty` field, and expects a certain `retainCount` afterwards, but that fails because `NSString.Empty` has been disposed, and then `RetainCount` returns 0 because it's being called on a null native object.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an internal mechanism to remove specific managed wrappers from the ObjCRuntime object map to avoid reusing disposed wrappers for native singleton/constant handles (notably the @"" empty NSString singleton).

Changes:

  • Add Runtime.RemoveFromObjectMap (NSObject obj) to remove a specific wrapper from the runtime object map.
  • Add an internal NSObject.RemoveFromObjectMap setter hook to invoke the runtime removal from object-initializer syntax.
  • Mark NSString.Empty to be removed from the object map immediately after creation to prevent it from being returned/disposed via handle lookups.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/ObjCRuntime/Runtime.cs Introduces RemoveFromObjectMap API and related documentation near object-map lookup logic.
src/Foundation/NSObject2.cs Adds an internal setter-only hook to trigger object-map removal on an instance.
src/Foundation/NSString.cs Updates NSString.Empty initialization to remove it from the object map.

Comment thread src/ObjCRuntime/Runtime.cs
Comment thread src/Foundation/NSObject2.cs
Comment thread src/ObjCRuntime/Runtime.cs Outdated
rolfbjarne and others added 2 commits April 17, 2026 00:19
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@rolfbjarne rolfbjarne enabled auto-merge (squash) April 16, 2026 23:32
@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@dalexsoto dalexsoto left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoa really interesting issue 👍

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
Collaborator

✅ [CI Build #85d5962] Build passed (Build packages) ✅

Pipeline on Agent
Hash: 85d5962c0487ffb9aad38a0ee79bd5c868fa29e6 [PR build]

@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
Collaborator

✅ [PR Build #85d5962] Build passed (Detect API changes) ✅

Pipeline on Agent
Hash: 85d5962c0487ffb9aad38a0ee79bd5c868fa29e6 [PR build]

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
Collaborator

✅ API diff for current PR / commit

NET (empty diffs)

✅ API diff vs stable

NET (empty diffs)

ℹ️ Generator diff

Generator Diff: vsdrops (html) vsdrops (raw diff) gist (raw diff) - Please review changes)

Pipeline on Agent
Hash: 85d5962c0487ffb9aad38a0ee79bd5c868fa29e6 [PR build]

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
Collaborator

✅ [CI Build #85d5962] Build passed (Build macOS tests) ✅

Pipeline on Agent
Hash: 85d5962c0487ffb9aad38a0ee79bd5c868fa29e6 [PR build]

@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
Collaborator

🚀 [CI Build #85d5962] Test results 🚀

Test results

✅ All tests passed on VSTS: test results.

🎉 All 156 tests passed 🎉

Tests counts

✅ cecil: All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (iOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (MacCatalyst): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (macOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (Multiple platforms): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (tvOS): All 1 tests passed. Html Report (VSDrops) Download
✅ framework: All 2 tests passed. Html Report (VSDrops) Download
✅ fsharp: All 4 tests passed. Html Report (VSDrops) Download
✅ generator: All 5 tests passed. Html Report (VSDrops) Download
✅ interdependent-binding-projects: All 4 tests passed. Html Report (VSDrops) Download
✅ introspection: All 6 tests passed. Html Report (VSDrops) Download
✅ linker: All 44 tests passed. Html Report (VSDrops) Download
✅ monotouch (iOS): All 11 tests passed. Html Report (VSDrops) Download
✅ monotouch (MacCatalyst): All 15 tests passed. Html Report (VSDrops) Download
✅ monotouch (macOS): All 12 tests passed. Html Report (VSDrops) Download
✅ monotouch (tvOS): All 11 tests passed. Html Report (VSDrops) Download
✅ msbuild: All 2 tests passed. Html Report (VSDrops) Download
✅ sharpie: All 1 tests passed. Html Report (VSDrops) Download
✅ windows: All 3 tests passed. Html Report (VSDrops) Download
✅ xcframework: All 4 tests passed. Html Report (VSDrops) Download
✅ xtro: All 1 tests passed. Html Report (VSDrops) Download

macOS tests

✅ Tests on macOS Monterey (12): All 5 tests passed. Html Report (VSDrops) Download
✅ Tests on macOS Ventura (13): All 5 tests passed. Html Report (VSDrops) Download
✅ Tests on macOS Sonoma (14): All 5 tests passed. Html Report (VSDrops) Download
✅ Tests on macOS Sequoia (15): All 5 tests passed. Html Report (VSDrops) Download
✅ Tests on macOS Tahoe (26): All 5 tests passed. Html Report (VSDrops) Download

Linux Build Verification

Linux build succeeded

Pipeline on Agent
Hash: 85d5962c0487ffb9aad38a0ee79bd5c868fa29e6 [PR build]

@vs-mobiletools-engineering-service2

This comment has been minimized.

@rolfbjarne rolfbjarne merged commit ba7f211 into main Apr 17, 2026
48 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants