Skip to content

[API Proposal]: Add API for getting memory for Objective-C object without creating GCHandle #128476

Closed

Description

@rolfbjarne

Background and motivation

The current ObjectiveCMarshal.CreateReferenceTrackingHandle API does two things:

  1. Creates a tracking GCHandle
  2. Returns tagged memory for the object in question.

I'm trying to improve memory usage and simplify code in dotnet/macios, and as part of that I want to store some flags in the tagged memory. These flags must exist before we have a tracking GCHandle (and also after any such handles have been freed), so I think it would make sense to have a separate API to just get the tagged memory, without creating a tracking GCHandle.

API Proposal

namespace System.Runtime.InteropServices.ObjectiveC;

public static class ObjectiveCMarshal
{
    /// <summary>
    /// Request a pointer to memory tagged for the supplied object.
    /// </summary>
    /// <param name="obj">The object whose tagged memory to return.</param>
    /// <returns>A pointer to memory tagged to the object.</returns>
    /// <exception cref="InvalidOperationException">Thrown if the ObjectiveCMarshal API has not been initialized.</exception>
    /// <remarks>
    /// The Initialize() must be called prior to calling this function.
    ///
    /// The <paramref name="obj"/> must have a type in its hierarchy marked with
    /// <see cref="ObjectiveCTrackedTypeAttribute"/>.
    ///
    /// The "Is Referenced" callback passed to <see cref="Initialize" />
    /// will be passed the memory returned from this function.
    /// The memory it points at is defined by the length in the <see cref="Span{IntPtr}"/> and
    /// will be zeroed out. It will be available until <paramref name="obj"/> is collected by the GC.
    /// The returned memory can be used for any purpose by the caller of this function and usable
    /// during the "Is Referenced" callback.
    ///
    /// Calling this function multiple times with the same <paramref name="obj"/> will
    /// return the same tagged memory. It is only guaranteed to be zero initialized on
    /// the first call.
    ///
    /// The return value is the same as the tagged memory returned from <see cref="CreateReferenceTrackingHandle" />.
    /// </remarks>
    public static Span<IntPtr> GetOrCreateTaggedMemory(object obj);
}

API Usage

This would be used somewhat like this:

var memory = ObjectiveCMarshal.GetOrCreateTaggedMemory (myObject);
fixed (void *ptr = memory)
    ((NSObjectData *) ptr)->flags = MyFlag;

Alternative Designs

Calling ObjectiveCMarshal.CreateReferenceTrackingHandle and just immediately freeing the GCHandle works fine:

public static Span<IntPtr> GetOrCreateTaggedMemory (object obj)
{
    var handle = ObjectiveCMarshal.CreateReferenceTrackingHandle (obj, out var span);
    handle.Free ();
    return span;
}

but this feels rather wasteful.

Risks

None that I can think of.

Activity

  1. added
    api-suggestionEarly API idea and discussion, it is NOT ready for implementation
    on May 22, 2026
  2. rolfbjarne commented on May 22, 2026

    @rolfbjarne
    MemberAuthor
  3. dotnet-policy-service commented on May 22, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @dotnet/interop-contrib
    See info in area-owners.md if you want to be subscribed.

  4. AaronRobinsonMSFT commented on May 22, 2026

    @AaronRobinsonMSFT
  5. AaronRobinsonMSFT commented on May 22, 2026

    @AaronRobinsonMSFT
    Member

    Oops, sorry. Please ignore #128476 (comment). I was looking at the JavaMarshal API not the ObjectiveCMarshal API surface area. I don't see a GetContext() on the ObjectiveCMarshal class. This does seem annoying. Let me review this again.

  6. AaronRobinsonMSFT commented on May 22, 2026

    @AaronRobinsonMSFT
    Member

    @rolfbjarne Okay, this API makes some more sense now that I'm looking at the Objective-C APIs.

    My only suggestion would be to update the name to be public Span<IntPtr> GetOrCreateTaggedMemory(object obj)

    The implementation would be simple and I don't see any real issues with adding this behavior since it would just be part of the "prestep" for the existing API. No new semantics appear to be introduced in this case. I agree with your documnetation as well keeping all the same invariants for this API as for CreateReferenceTrackingHandle.

    Apologies for the initial confusion.

  7. added this to the 11.0.0 milestone on May 22, 2026
  8. AaronRobinsonMSFT commented on May 22, 2026

    @AaronRobinsonMSFT
    Member

    Please update the API with the suggested name and then feel free to mark it with "api-ready-for-review".

  9. AaronRobinsonMSFT commented on May 22, 2026

    @AaronRobinsonMSFT
    Member
  10. changed the title [-][API Proposal]: Add ObjectiveCMarshal.GetTaggedMemory[/-] [+][API Proposal]: Add ObjectiveCMarshal.GetOrCreateTaggedMemory[/+] on May 25, 2026
  11. removed
    api-suggestionEarly API idea and discussion, it is NOT ready for implementation
    on May 26, 2026
  12. AaronRobinsonMSFT commented on Jun 1, 2026

    @AaronRobinsonMSFT
    Member
    • Renamed to GetOrCreateReferenceTrackingMemory due to "TaggedMemory" being new nomenclature that didn't seem necessary.
    namespace System.Runtime.InteropServices.ObjectiveC;
    
    public static class ObjectiveCMarshal
    {
        /// <summary>
        /// Request a pointer to referenced tracking memory for the supplied object.
        /// </summary>
        /// <param name="obj">The object whose tracking memory to return.</param>
        /// <returns>A pointer to tracking memory on the object.</returns>
        /// <exception cref="InvalidOperationException">Thrown if the ObjectiveCMarshal API has not been initialized.</exception>
        /// <remarks>
        /// The Initialize() must be called prior to calling this function.
        ///
        /// The <paramref name="obj"/> must have a type in its hierarchy marked with
        /// <see cref="ObjectiveCTrackedTypeAttribute"/>.
        ///
        /// The "Is Referenced" callback passed to <see cref="Initialize" />
        /// will be passed the memory returned from this function.
        /// The memory it points at is defined by the length in the <see cref="Span{IntPtr}"/> and
        /// will be zeroed out. It will be available until <paramref name="obj"/> is collected by the GC.
        /// The returned memory can be used for any purpose by the caller of this function and usable
        /// during the "Is Referenced" callback.
        ///
        /// Calling this function multiple times with the same <paramref name="obj"/> will
        /// return the same tracking memory. It is only guaranteed to be zero initialized on
        /// the first call of this or <see cref="CreateReferenceTrackingHandle" />.
        ///
        /// The return value is the same as the tracking memory returned from <see cref="CreateReferenceTrackingHandle" />.
        /// </remarks>
        public static Span<IntPtr> GetOrCreateReferenceTrackingMemory(object obj);
    }
  13. added
    api-approvedAPI was approved in API review, it can be implemented
    and removed
    api-ready-for-reviewAPI is ready for review, it is NOT ready for implementation
    on Jun 1, 2026
  14. changed the title [-][API Proposal]: Add ObjectiveCMarshal.GetOrCreateTaggedMemory[/-] [+][API Proposal]: Add API for getting memory for Objective-C object without creating GCHandle[/+] on Jun 1, 2026
  15. changed the title [-][API Proposal]: Add API for getting memory for Objective-C object without creating GCHandle[/-] [+][API Proposal]: Add API for getting memory for Objective-C object without creating `GCHandle`[/+] on Jun 1, 2026
  16. added a commit that references this issue on Jun 2, 2026
    2fa1dda
  17. locked and limited conversation to collaborators on Jul 3, 2026
  18. added a commit that references this issue on Jul 15, 2026
    1c67c0c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

  • Status
    No status

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions