using System; using System.Runtime.InteropServices; using Unity.Jobs; namespace UnityEngine.XR.ARKit { /// /// An interface for Objective-C objects that derive from NSObject. /// interface INSObject { /// /// Gets the underlying Objective-C pointer. /// /// Returns the underlying Objective-C pointer. IntPtr AsIntPtr(); /// /// Sets the underlying native pointer for the object. /// /// /// Warning: This allows you to change this object into any arbitrary object, even one of a different type. /// Incorrect usage is undefined behavior and can cause your application to crash. /// /// The pointer to which the object's underlying pointer will be set. void SetUnderlyingNativePtr(IntPtr ptr); /// /// The of the type of this . /// Class staticClass { get; } } /// /// Utility methods for interacting with [NSObjects](https://developer.apple.com/documentation/objectivec/nsobject?language=objc). /// static class NSObject { /// /// May be used by constructors to specify the type of object initialization. /// public enum Initialization { /// /// Use for default constructors, since structs cannot have parameterless constructors. /// Default } public static string ToString(T instance) where T : struct, INSObject { using (var description = GetDescription(instance.AsIntPtr())) { return description.ToString(); } } public static bool ArePointersEqual(T? lhs, T? rhs) where T : struct, INSObject { // Both have a value, so compare values if (lhs.HasValue && rhs.HasValue) return lhs.Value.AsIntPtr() == rhs.Value.AsIntPtr(); // lhs has a value; rhs is null if (lhs.HasValue) return lhs.Value.AsIntPtr() == IntPtr.Zero; // rhs has a value; lhs is null if (rhs.HasValue) return rhs.Value.AsIntPtr() == IntPtr.Zero; // Neither has a value return true; } public static bool IsEqual(T lhs, U rhs) where T : struct, INSObject where U : struct, INSObject => IsEqual(lhs.AsIntPtr(), rhs.AsIntPtr()); public static int GetHashCode(T instance) where T : struct, INSObject => GetHash(instance.AsIntPtr()).GetHashCode(); public static int GetRetainCount(T obj) where T : struct, INSObject => CFGetRetainCount(obj.AsIntPtr()); public static void Release(IntPtr ptr) { if (ptr != IntPtr.Zero) { CFRelease(ptr); } } public static void Dispose(ref IntPtr ptr) { Release(ptr); ptr = IntPtr.Zero; } struct ReleaseJob : IJob { public IntPtr ptr; void IJob.Execute() { if (ptr != IntPtr.Zero) { CFRelease(ptr); } } } public static JobHandle Dispose(IntPtr ptr, JobHandle inputDependencies) => new ReleaseJob { ptr = ptr }.Schedule(inputDependencies); public static void Retain(IntPtr ptr) { if (ptr != IntPtr.Zero) { CFRetain(ptr); } } public static void Retain(T obj) where T : struct, INSObject => Retain(obj.AsIntPtr()); #if UNITY_EDITOR || !UNITY_XR_ARKIT_LOADER_ENABLED static void CFRetain(IntPtr obj) { } static void CFRelease(IntPtr obj) { } static int CFGetRetainCount(IntPtr obj) => default; static NSString GetDescription(IntPtr self) => default; static bool IsEqual(IntPtr self, IntPtr other) => default; static ulong GetHash(IntPtr self) => default; #else [DllImport("__Internal")] static extern void CFRetain(IntPtr obj); [DllImport("__Internal")] static extern void CFRelease(IntPtr obj); [DllImport("__Internal")] static extern int CFGetRetainCount(IntPtr obj); [DllImport("__Internal", EntryPoint = "NSObject_get_description")] static extern NSString GetDescription(IntPtr self); [DllImport("__Internal", EntryPoint = "NSObject_isEqual_")] static extern bool IsEqual(IntPtr self, IntPtr other); [DllImport("__Internal", EntryPoint = "NSObject_get_hash")] static extern ulong GetHash(IntPtr self); #endif } }