Parece bastante claro que se supone que debe configurar las cosas.
- ¿Cuándo se ejecuta exactamente?
- ¿Por qué hay dos paréntesis?
- Es __attribute__una función? Una macro? ¿Sintaxis?
- ¿Funciona esto en C? C ++?
- ¿La función con la que funciona debe ser estática?
- ¿Cuándo __attribute__((destructor))corre?
__attribute__((constructor))
static void initialize_navigationBarImages() {
  navigationBarImages = [[NSMutableDictionary alloc] init];
}
__attribute__((destructor))
static void destroy_navigationBarImages() {
  [navigationBarImages release];
}fuente

#define __attribute__(x)). Si tiene varios atributos, por ejemplo,__attribute__((noreturn, weak))sería difícil "eliminar" si solo hubiera un conjunto de paréntesis..init/.fini. (Puede tener válidamente múltiples constructores y destructores en una sola unidad de traducción, no importa múltiples en una sola biblioteca, ¿cómo funcionaría?) En cambio, en plataformas que usan formato binario ELF (Linux, etc.), se hace referencia a los constructores y destructores en las secciones.ctorsy.dtorsdel encabezado. Es cierto que en los viejos tiempos, las funciones nombradasinityfinise ejecutarían en la carga y descarga de la biblioteca dinámica si existieran, pero eso está en desuso ahora, reemplazado por este mejor mecanismo.__attribute__es si no está usando gcc, ya que también es una extensión de gcc..init/.finino está en desuso. Todavía es parte del estándar ELF y me atrevería a decir que será para siempre. El código en.init/.finies ejecutado por el cargador / runtime-linker cuando el código se carga / descarga. Es decir, en cada código ELF de carga (por ejemplo, una biblioteca compartida).initse ejecutará. Todavía es posible usar ese mecanismo para lograr casi lo mismo que con__attribute__((constructor))/((destructor)). Es de la vieja escuela pero tiene algunos beneficios..ctors/.dtorsmecanismo, por ejemplo, requiere soporte de system-rtl / loader / linker-script. Esto está lejos de ser seguro para estar disponible en todos los sistemas, por ejemplo, sistemas profundamente integrados donde el código se ejecuta en metal desnudo. Es decir, incluso si__attribute__((constructor))/((destructor))es compatible con GCC, no es seguro que se ejecutará, ya que depende del vinculador organizarlo y del cargador (o, en algunos casos, del código de arranque) ejecutarlo. Para usar.init/ en su.finilugar, la forma más fácil es usar banderas de enlace: -init & -fini (es decir, desde la línea de comandos de GCC, la sintaxis sería-Wl -init my_init -fini my_fini).En el sistema que admite ambos métodos, un beneficio posible es que el código
.initse ejecuta antes.ctorsy el código.finidespués.dtors. Si el orden es relevante, esa es al menos una forma cruda pero fácil de distinguir entre las funciones init / exit.Una desventaja importante es que no puedes tener más de uno
_inity uno_finifunción por cada módulo cargable y probablemente tendría que fragmentar el código más.soque motivado. Otra es que cuando se usa el método de enlace descrito anteriormente, uno reemplaza las_finifunciones _init y predeterminadas originales (proporcionadas porcrti.o). Aquí es donde generalmente se produce todo tipo de inicialización (en Linux aquí es donde se inicializa la asignación de variable global). Aquí se describe una forma de evitarloObserve en el enlace anterior que
_init()no se necesita una conexión en cascada con el original, ya que todavía está en su lugar. Elcallde la línea de montaje es sin embargo x86-mnemotécnica y llamar a una función del conjunto se vería completamente diferente para muchas otras arquitecturas (como ARM, por ejemplo). Es decir, el código no es transparente..initLos mecanismos /.finiy.ctors/.detorsson similares, pero no del todo. El código en.init/ se.finiejecuta "tal cual". Es decir, puede tener varias funciones en.init/.fini, pero AFAIK es sintácticamente difícil ponerlas allí de forma totalmente transparente en C puro sin romper el código en muchos.soarchivos pequeños ..ctors/.dtorsestán organizados de manera diferente que.init/.fini..ctorsLas.dtorssecciones / son solo tablas con punteros a funciones, y el "llamador" es un bucle proporcionado por el sistema que llama a cada función indirectamente. Es decir, la llamada de bucle puede ser específica de la arquitectura, pero como es parte del sistema (si es que existe), no importa.El siguiente fragmento agrega nuevos punteros de función a la
.ctorsmatriz de funciones, principalmente de la misma manera que lo__attribute__((constructor))hace (el método puede coexistir con__attribute__((constructor))).También se pueden agregar los punteros de función a una sección autoinventada completamente diferente. Un script enlazador modificado y una función adicional que imita el cargador
.ctors/.dtorstal caso, se necesita bucle. Pero con él se puede lograr un mejor control sobre el orden de ejecución, agregar argumentos y manejo de código de retorno eta (en un proyecto de C ++, por ejemplo, sería útil si necesita algo que se ejecute antes o después de los constructores globales).Preferiría
__attribute__((constructor))/((destructor))siempre que sea posible, es una solución simple y elegante, incluso se siente como hacer trampa. Para codificadores de metal desnudo como yo, esto no siempre es una opción.Alguna buena referencia en el libro Linkers & loaders .
fuente
__attribute__((constructor))/((destructor))el destructor no se ejecuta. Intenté algunas cosas, como agregar una entrada a .dtor como se muestra arriba. Pero no hay éxito. El problema es fácil de duplicar ejecutando el código con numactl. Por ejemplo, suponga que test_code contiene el destructor (agregue un printf a las funciones de constructor y desctructor para depurar el problema). Entonces correLD_PRELOAD=./test_code numactl -N 0 sleep 1. Verá que el constructor se llama dos veces pero el destructor solo una vez.Esta página proporciona una gran comprensión sobre
constructorydestructorimplementación de atributos y las secciones dentro de dentro de ELF que les permitan trabajar. Después de digerir la información proporcionada aquí, compilé un poco de información adicional y (tomando prestado el ejemplo de la sección de Michael Ambrus anterior) creé un ejemplo para ilustrar los conceptos y ayudar a mi aprendizaje. Esos resultados se proporcionan a continuación junto con la fuente de ejemplo.Como se explica en este hilo, los atributos
constructorydestructorcrean entradas en la sección.ctorsy.dtorsdel archivo de objeto. Puede colocar referencias a funciones en cualquier sección de una de tres maneras. (1) usando elsectionatributo; (2)constructorydestructoratributos o (3) con una llamada de ensamblaje en línea (como se hace referencia al enlace en la respuesta de Ambrus).El uso de
constructory losdestructoratributos le permiten asignar adicionalmente una prioridad al constructor / destructor para controlar su orden de ejecución antes de quemain()se llame o después de que regrese. Cuanto menor sea el valor de prioridad dado, mayor será la prioridad de ejecución (las prioridades más bajas se ejecutan antes de las prioridades más altas antes de main () - y después de las prioridades más altas después de main ()). Los valores de prioridad que proporcione deben ser mayores que100cuando el compilador reserva valores de prioridad entre 0-100 para la implementación. Aconstructorodestructorespecificado con prioridad se ejecuta antes de aconstructorodestructorespecificado sin prioridad.Con el atributo 'section' o con el ensamblaje en línea, también puede colocar referencias de funciones en la sección de código
.inity.finiELF que se ejecutará antes de cualquier constructor y después de cualquier destructor, respectivamente. Cualquier función llamada por la referencia de función colocada en la.initsección, se ejecutará antes de la referencia de función en sí (como de costumbre).He tratado de ilustrar cada uno de esos en el siguiente ejemplo:
salida:
El ejemplo ayudó a cimentar el comportamiento del constructor / destructor, con suerte también será útil para otros.
fuente
MAX_RESERVED_INIT_PRIORITY), y que eran los mismos que C ++ (init_priority) 7.7 C ++: atributos específicos de variables, funciones y tipos . Luego probé con99:warning: constructor priorities from 0 to 100 are reserved for the implementation [enabled by default] void construct0 () __attribute__ ((constructor (99)));.Aquí hay un ejemplo "concreto" (y posiblemente útil ) de cómo, por qué y cuándo usar estas construcciones prácticas pero antiestéticas ...
Xcode utiliza un "valor predeterminado de usuario" "global" para decidir qué
XCTestObserverclase arroja su corazón a la consola asediada .En este ejemplo ... cuando implícitamente cargo esta psuedo-biblioteca, llamémosla ...
libdemure.a, a través de una bandera en mi objetivo de prueba á la ..Quiero..
En la carga (es decir, cuando
XCTestcarga mi paquete de prueba), anule laXCTestclase "predeterminada" "observador" ... (a través de laconstructorfunción) PD: Por lo que puedo decir ... cualquier cosa que se haga aquí podría hacerse con un efecto equivalente dentro de mi+ (void) load { ... }método de claseejecutar mis pruebas ... en este caso, con menos verbosidad en los registros (implementación a pedido)
Regrese la
XCTestObserverclase "global" a su estado original ... para no estropear otrasXCTestcarreras que no se han subido al carro (aliaslibdemure.a. Supongo que esto se hizo históricamente endealloc... pero no voy a comenzar a jugar con esa vieja bruja.Entonces...
Sin la bandera de enlace ... (La policía de la moda enjambre Cupertino exigiendo represalias , sin embargo, el defecto de Apple prevalece, como se desea, aquí )
CON la
-ldemure.abandera del vinculador ... (Resultados comprensibles, jadeo ... "graciasconstructor/destructor" ... Multitud aplaude )fuente
Aquí hay otro ejemplo concreto: es para una biblioteca compartida. La función principal de la biblioteca compartida es comunicarse con un lector de tarjetas inteligentes. Pero también puede recibir 'información de configuración' en tiempo de ejecución a través de udp. El udp es manejado por un hilo que DEBE iniciarse en el momento de inicio.
La biblioteca fue escrita en c.
fuente