Red de conocimientos sobre prescripción popular - Conocimiento dental - Cómo utilizar UnsafeMutablePointer en Swift

Cómo utilizar UnsafeMutablePointer en Swift

Si crea un proyecto Swift, cuando crea un archivo C y luego agrega algunos métodos, como un método que devuelve un tipo char* (ya puenteado), cuando escriba este método definido por C en Swift, xcode lo hará. se convertirá automáticamente al tipo en Swift. Char* corresponde a UnsafeMutablePointer, const char* es UnsafePointer.

Está bien, empezaré de forma sencilla. Lea el contenido principal del artículo.

En la mayoría de los casos, los punteros del lenguaje C se pueden importar a Swift de dos maneras:

UnsafePointer ltT gt

UnsafeMutablePointer ltT gt

Aquí está el tipo Swift equivalente al tipo c. Los punteros declarados como constantes se importan como UnsafePointer y los punteros no constantes se importan como UnsafeMutablePoinger.

Aquí tienes algunos ejemplos:

C:

void my function(const int * myconstinpointer);

Swift:

func mi función(myconstinpointer: puntero inseguro lt; Int32 >)

C:

void myOtherFunction(unsigned int * myunsignedint pointer);

Swift:

func myOtherFunction(myunsignedint pointer: unsafmutablepointer lt; UInt32 gt)

C:

void itakeavoidepointer(void *avoidepointer);

Swift:

función para evitar puntero (evitar puntero: puntero insafmutable lt; Void gt)

Si no conoce el tipo de puntero, como un puntero declarado como prefijo, utilice COpaquePointer.

C:

Construir algo;

void iTakeAnOpaquePointer(struct SomeThing * SomeThing);

Swift:

func iTakeAnOpaquePointer(someThing:COpaquePointer)

Pasar un puntero a un objeto Swift

En muchos casos, pasar un puntero a un objeto Swift es tan simple como usar el operador inout, que Similar al operador and en lenguaje C.

Swift:

Establecer myInt: = 42

Mi función (ampmyInt)

var myUnsignedInt: UInt = 7

Mis otras funciones (ampmyUnsignedInt)

Aquí hay dos detalles muy importantes pero que fácilmente se pasan por alto.

1. Cuando se utiliza el operador inout, las variables declaradas con var y las constantes declaradas con let se convierten a UnsafePointer y UnsafeMutablePoinger respectivamente. Es fácil cometer errores si no prestas atención a los tipos en el código original. Puede intentar pasar un UnsafeMutablePoinger al lugar inseguro original y el compilador informará un error.

2. Este operador solo tiene efecto cuando los valores y referencias Swift se pasan como contexto de los parámetros de la función. Los parámetros de la función solo aceptan dos tipos: UnsafePointer y UnsafeMutablePoinger. No se pueden obtener estos consejos en otros contextos. Por ejemplo, el siguiente código no es válido y devolverá un error de compilación.

Swift:

Sea x = 42

Sea y = ampx

Es posible que necesite interoperar con la API de vez en cuando . Obtiene o devuelve un puntero nulo en lugar de un tipo explícito. Desafortunadamente, esta práctica es tan común en C que es imposible especificar un tipo genérico.

C:

void tomaAnObject(void * the object);

Si está seguro de qué tipo de parámetros necesita obtener la función, puede usar withUnsafePointer y unsafeBitCast para convertir el objeto en puntero nulo. Por ejemplo, supongamos que TakeAnObject necesita obtener un puntero a un int.

var test = 42

with unsafepointer( amp; test, {(ptr: unsafe pointer lt; Int gt) - gt; invalidate

var void ptr : puntero inseguro lt; Void gt = unsafeBitCast(ptr, UnsafePointer ltVoid gt. self)

takesAnObject(voidPtr)

})

Para convertirlo, nosotros Primero debe llamar a UnsafeMutablePointer, que contiene dos parámetros.

El primer parámetro es el operador inout de tipo t y el segundo parámetro es (puntero inseguro)->; La función llama al cierre con un puntero al primer argumento, que luego se pasa como único argumento del cierre y, finalmente, la función devuelve el resultado del cierre. En el ejemplo anterior, el tipo de cierre se establece en Anulado, por lo que no se devuelve ningún valor. Un ejemplo del valor de retorno es el siguiente:

let ret = withounsafepointer( amp;test, {(ptr:unsafe pointer lt;Int gt)-gt;Int32 pulgadas

var void ptr:puntero inseguro lt; Void gt = unsafeBitCast(ptr, UnsafePointer ltVoid gt. self)

return takeAnObjectAndReturnsAnInt(void ptr)

})

println (ret)

Nota: Debe modificar el puntero usted mismo y completar la modificación mediante la variante con unsafemutablepointer.

Para mayor comodidad, Swift también incluye una variante para pasar dos punteros:

var x: Int = 7

var y: Double = 4

p>

con punteros inseguros( amp; var void ptr 1: puntero inseguro lt; Void gt = unsafeBitCast(ptr1, UnsafePointer ltVoid gt. self)

var void ptr 2: puntero inseguro lt; Void gt= unsafeBitCast(ptr2, UnsafePointer ltVoid gt. self) )

takesTwoPointers(void ptr 1, voidPtr2)

})

Acerca de unsafeBitCast

UnsafeBitCast es una operación extremadamente peligrosa. La documentación lo describe como "forzar que algo tenga la misma longitud que otra cosa". La razón por la que podemos usarlo de forma segura en el código anterior es porque simplemente estamos convirtiendo punteros de diferentes tipos y la longitud de estos punteros es la misma. Es por eso que primero debemos llamar a withUnsafePointer para obtener UnsafePointer y luego convertirlo a UnsafePointer.

Esto puede resultar confuso al principio, especialmente cuando se trata del mismo tipo que un puntero, como int en Swift (la longitud de un puntero es un carácter en todas las plataformas disponibles, y la longitud de un Int también es un personaje).

Por ejemplo, es fácil cometer el siguiente error:

var x: Int = 7

Let xPtr = unsafeBitCast(x, UnsafePointer ltVoid gt. self)

p>

La intención de este código es obtener un puntero y pasarlo a x, lo que provocará malentendidos. Aunque la compilación se puede pasar y ejecutar, provocará errores inesperados. Esto se debe a que en lugar de obtener un puntero y pasarlo a X, la API de C recibe un puntero en 0x7 o algo así.

Debido a que unsafeBitCast requiere que los tipos tengan la misma longitud, no es tan insidioso al intentar convertir un Int8 o un entero de bytes.

var x: Int8 = 7

Let xPtr = unsafeBitCast(x, UnsafePointer ltVoid gt. self)

Este código solo hará que unsafeBitCast arroje una excepción y bloquear el programa.

Interactuar con estructuras en C

Demostremos esta parte con un ejemplo práctico. Si desea recuperar información del sistema sobre una computadora que se está ejecutando, existe un CAPI: UNAME (2) que funciona. Recibe un puntero a una estructura de datos y llena el objeto proporcionado con información del sistema, como el nombre y la versión del sistema operativo o el identificador de hardware. Pero hay un problema.

La estructura importada a Swift es la siguiente:

Nombre de la estructura {

var sysname: (Int8, Int8,...253 veces...Int8)

var nombre de nodo: (Int8, Int8,...253 veces...Int8)

versión var: (Int8, Int8,...253 veces...Int8)

versión var (Int8, Int8,...253 veces...Int8)

máquina var: (Int8, Int8,...253 veces...Int8)

}

Swift importa literales de matriz desde C como tuplas, y el inicializador predeterminado requiere que cada campo tenga un valor, por lo que si lo hace de la manera habitual en Swift, se convierte en:

var nombre = utsname(sysname: (0, 0, 0,..., 0), nombre de nodo: (0, 0, 0,... 0), etc.)

utsname( amp ;nombre)

var máquina =nombre.máquina

impresora

Este no es un buen enfoque. Hay otra pregunta. Debido a que el campo de la máquina en utsname es una tupla, cuando use println, se generará un Int8 de 256 bits, pero de hecho solo necesitamos los primeros valores ASCII de la cadena.

Entonces, ¿cómo solucionar este problema?

UnsafeMutablePointer en Swift proporciona dos métodos, alloc(Int) y dealloc(Int), que se utilizan para asignar y desasignar los parámetros cuantificados de la plantilla T respectivamente. Podemos usar estas API para simplificar nuestro código:

let name = unsafmutablepointer lt;utsname gt. asignar(1)

uname(nombre)

let machine = withounsafepointer(&name.memory.machine, {(ptr)->string? in

let int8Ptr = unsafeBitCast(ptr, UnsafePointer ltInt8>. self)

return String.fromCString(int8Ptr)

})

nombre.dealloc( 1)

Si m = máquina {

Longitud de impresión (metros)

}

El primer paso es llamar a withUnsafePointer y poner una tupla de máquinas se le pasa, informándole que nuestro cierre devolverá una cadena adicional.

En el cierre, convertimos el puntero en un UnsafePointer, que es la expresión más equivalente del valor. Además, String de Swift contiene un método de clase que inicializa un UnsafePointer, donde CChar es un alias para el tipo Int8, por lo que podemos pasar nuestro nuevo puntero al inicializador y devuelve la información requerida.

Después de obtener el resultado de withUnsafePointer, podemos probar si es una declaración condicional de let e imprimir el resultado. Para este ejemplo, genera el campo esperado "x86_64".

Resumen

Finalmente, un descargo de responsabilidad. El uso de API no seguras en Swift debe considerarse un último recurso debido a su potencial inseguridad. A medida que convertimos el código C y Objective-C heredado a Swift, es muy probable que sigamos necesitando que estas API sean compatibles con las herramientas existentes.

Sin embargo, cuando utilizamos punteros inseguros y unsafeBitCast como primera opción, siempre debemos ser escépticos y buscar otras soluciones mejores.

El nuevo código debe ajustarse a los modismos del idioma tanto como sea posible y no utilizar API no seguras en el código Swift. Como desarrollador de software, debe saber cómo utilizar sus herramientas, dónde utilizarlas y dónde no utilizarlas. Swift aporta modernidad al desarrollo de OS X e iOS, y debemos respetar sus ideales.