[src] Add a TransientCFString struct. (#19763)

In order to make it easier to pass C-style strings to P/Invokes, we
introduced
a TransientString struct some time ago.

This works quite well, so I implemented the same for CFStrings - a
TransientCFString struct - and started using it in a few places. The
idea
would be to slowly start migrating our codebase to this new pattern.

Instead of:

    var ptr = CFString.CreateNative ("somestring");
    try {
    	CallPInvoke (ptr);
    } finally {
    	CFString.ReleaseNative (ptr);
    }

we'll do:

    using var ptr = new TransientCFString ("somestring");
    CallPInvoke (ptr);
This commit is contained in:
Rolf Bjarne Kvinge 2024-01-11 09:39:27 +01:00 коммит произвёл GitHub
Родитель ef03fa8bca
Коммит 779f064d04
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 56 добавлений и 25 удалений

Просмотреть файл

@ -73,13 +73,9 @@ namespace CoreMidi {
MidiThruConnectionRef ret;
using (var data = connectionParams.WriteStruct ()) {
var retStr = CFString.CreateNative (persistentOwnerID);
try {
unsafe {
error = MIDIThruConnectionCreate (retStr, data.Handle, &ret);
}
} finally {
CFString.ReleaseNative (retStr);
using var retStr = new TransientCFString (persistentOwnerID);
unsafe {
error = MIDIThruConnectionCreate (retStr, data.Handle, &ret);
}
}
@ -155,13 +151,9 @@ namespace CoreMidi {
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (persistentOwnerID));
IntPtr ret;
var persistentOwnerIDHandle = CFString.CreateNative (persistentOwnerID);
try {
unsafe {
error = MIDIThruConnectionFind (persistentOwnerIDHandle, &ret);
}
} finally {
CFString.ReleaseNative (persistentOwnerIDHandle);
using var persistentOwnerIDHandle = new TransientCFString (persistentOwnerID);
unsafe {
error = MIDIThruConnectionFind (persistentOwnerIDHandle, &ret);
}
using (var data = Runtime.GetNSObject<NSData> (ret)) {
if (data is null)

Просмотреть файл

@ -0,0 +1,35 @@
using System;
using System.Runtime.InteropServices;
using CoreFoundation;
#nullable enable
namespace ObjCRuntime {
// a short-lived holder for a CFString/NSString-like string for native interop
// typical usage:
// using var cstring = new NativeCFString (str);
// SomePInvoke (cstring);
//
internal ref struct TransientCFString {
#if !COREBUILD
IntPtr ptr;
public TransientCFString (string? str)
{
ptr = CFString.CreateNative (str);
}
public void Dispose ()
{
if (ptr != IntPtr.Zero) {
CFString.ReleaseNative (ptr);
ptr = IntPtr.Zero;
}
}
public static implicit operator IntPtr (TransientCFString str) => str.ptr;
public static explicit operator string? (TransientCFString str) => CFString.FromHandle (str.ptr);
#endif
}
}

Просмотреть файл

@ -82,22 +82,18 @@ namespace UIKit {
[EditorBrowsable (EditorBrowsableState.Never)]
public static void Main (string []? args, string? principalClassName, string? delegateClassName)
{
var p = CFString.CreateNative (principalClassName);
var d = CFString.CreateNative (delegateClassName);
using var p = new TransientCFString (principalClassName);
using var d = new TransientCFString (delegateClassName);
Initialize ();
UIApplicationMain (args?.Length ?? 0, args, p, d);
CFString.ReleaseNative (d);
CFString.ReleaseNative (p);
}
public static void Main (string []? args, Type? principalClass, Type? delegateClass)
{
var p = principalClass is null ? NativeHandle.Zero : CFString.CreateNative (new Class (principalClass).Name);
var d = delegateClass is null ? NativeHandle.Zero : CFString.CreateNative (new Class (delegateClass).Name);
using var p = new TransientCFString (principalClass is null ? null : new Class (principalClass).Name);
using var d = new TransientCFString (delegateClass is null ? null : new Class (delegateClass).Name);
Initialize ();
UIApplicationMain (args?.Length ?? 0, args, p, d);
CFString.ReleaseNative (d);
CFString.ReleaseNative (p);
}
public static void Main (string []? args)

Просмотреть файл

@ -1932,6 +1932,7 @@ SHARED_CORE_SOURCES = \
ObjCRuntime/NativeAttribute.cs \
ObjCRuntime/NativeHandle.cs \
ObjCRuntime/NativeNameAttribute.cs \
ObjCRuntime/TransientCFString.cs \
ObjCRuntime/TransientString.cs \
ObjCRuntime/NFloat.cs \
ObjCRuntime/ObsoleteConstants.cs \

Просмотреть файл

@ -120,9 +120,16 @@ namespace Xamarin.Utils {
if (provider?.HasCustomAttributes != true)
return false;
foreach (var attrib in provider.CustomAttributes)
if (IsObsoleteAttribute (attrib))
return true;
foreach (var attrib in provider.CustomAttributes) {
if (!IsObsoleteAttribute (attrib))
continue;
// The compiler will emit a fake Obsolete attribute for ref structs. Ignore those Obsolete attributes, because the type isn't really obsolete.
if (attrib.HasConstructorArguments && attrib.ConstructorArguments [0].Value is string obsoleteMessage && obsoleteMessage == "Types with embedded references are not supported in this version of your compiler.")
continue;
return true;
}
return false;
}