Compartir a través de


Reglas para administrar recuentos de referencias

El uso de un recuento de referencias para administrar la duración de un objeto permite a varios clientes obtener y liberar el acceso a un único objeto sin tener que coordinarse entre sí al administrar la duración del objeto. Siempre que el objeto de cliente se ajuste a ciertas reglas de uso, el objeto, en efecto, proporciona esta administración. Estas reglas especifican cómo administrar las referencias entre objetos. (COM no especifica implementaciones internas de objetos, aunque estas reglas son un punto de partida razonable para una directiva dentro de un objeto).

Conceptualmente, los punteros de interfaz se pueden considerar como residentes dentro de variables de puntero que incluyen todo el estado de cálculo interno que contiene un puntero de interfaz. Esto incluiría variables en ubicaciones de memoria, en registros de procesador internos y variables generadas por el programador y generadas por el compilador. La asignación o inicialización de una variable de puntero implica la creación de una nueva copia de un puntero ya existente. Donde había una copia del puntero en alguna variable (el valor usado en la asignación o inicialización), ahora hay dos. Una asignación a una variable de puntero destruye la copia del puntero actualmente en la variable, al igual que la destrucción de la propia variable. (Es decir, el ámbito en el que se encuentra la variable, como el marco de pila, se destruye).

Desde la perspectiva de un cliente COM, el recuento de referencias siempre se realiza para cada interfaz. Los clientes nunca deben suponer que un objeto usa el mismo contador para todas las interfaces.

El caso predeterminado es que se debe llamar a AddRef para cada nueva copia de un puntero de interfaz y release debe llamarse para cada destrucción de un puntero de interfaz, excepto cuando las reglas siguientes permitan lo contrario:

  • Parámetros de in-out para funciones. El autor de la llamada debe llamar a AddRef en el parámetro porque se liberará (con una llamada a Release) en el código de implementación cuando el valor out se almacene encima de él.
  • Captura de una variable global. Al crear una copia local de un puntero de interfaz a partir de una copia existente del puntero en una variable global, debe llamar a AddRef en la copia local porque otra función podría destruir la copia en la variable global mientras la copia local sigue siendo válida.
  • nuevos punteros sintetizados de "aire fino". Función que sintetiza un puntero de interfaz mediante conocimientos internos especiales en lugar de obtenerlo de otro origen debe llamar a AddRef inicialmente en el puntero recién sintetizado. Entre los ejemplos importantes de estas rutinas se incluyen rutinas de creación de instancias, implementaciones de QueryInterface, etc.
  • Recuperar una copia de un puntero almacenado internamente. Cuando una función recupera una copia de un puntero almacenado internamente por el objeto llamado, el código del objeto debe llamar a AddRef en el puntero antes de que la función devuelva. Una vez recuperado el puntero, el objeto de origen no tiene ninguna otra manera de determinar cómo se relaciona su duración con la de la copia almacenada internamente del puntero.

Las únicas excepciones al caso predeterminado requieren que el código de administración conozca las relaciones de las duraciones de dos o más copias de un puntero a la misma interfaz en un objeto y simplemente asegúrese de que el objeto no se destruye al permitir que su recuento de referencias vaya a cero. En general, hay dos casos, como se indica a continuación:

  • Cuando ya existe una copia de un puntero y se crea posteriormente un segundo y, a continuación, se destruye mientras la primera copia todavía existe, se pueden omitir las llamadas a AddRef y Release para la segunda copia.
  • Cuando existe una copia de un puntero y se crea una segunda y, a continuación, se destruye la primera antes de la segunda, se pueden omitir las llamadas a AddRef para la segunda copia y a Release para la primera copia.

A continuación se muestran ejemplos específicos de estas situaciones, las dos primeras son especialmente comunes:

  • En parámetros para funciones. La duración de la copia de un puntero de interfaz que se pasa como parámetro a una función está anidada en la del puntero usado para inicializar el valor, por lo que no es necesario contar con una referencia independiente en el parámetro .
  • Salida de parámetros de funciones, incluidos los valores devueltos. Para establecer el parámetro out, la función debe tener una copia estable del puntero de interfaz. A cambio, el autor de la llamada es responsable de liberar el puntero. Por lo tanto, el parámetro out no necesita un recuento de referencias independiente.
  • Variables locales. Una implementación de método tiene control de las duraciones de cada una de las variables de puntero asignadas en el marco de pila y puede usarla para determinar cómo omitir los pares redundantes AddRef/Release.
  • Backpointers. Algunas estructuras de datos contienen dos objetos, cada uno con un puntero al otro. Si se sabe que la duración del primer objeto contiene la duración del segundo, no es necesario tener un recuento de referencias en el puntero del segundo objeto al primer objeto. A menudo, evitar este ciclo es importante para mantener el comportamiento de desasignación adecuado. Sin embargo, los punteros sin contar deben usarse con extrema precaución porque la parte del sistema operativo que controla el procesamiento remoto no tiene forma de saber sobre esta relación. Por lo tanto, en casi todos los casos, tener el backpointer ver un segundo objeto "friend" del primer puntero (evitando así la circularidad) es la solución preferida. Por ejemplo, la arquitectura de objetos conectables de COM usa este enfoque.

Al implementar o usar objetos con recuento de referencias, puede resultar útil aplicar recuentos de referencias artificiales, lo que garantiza la estabilidad del objeto durante el procesamiento de una función. Al implementar un método de una interfaz, puede llamar a funciones que tienen la posibilidad de disminuir el recuento de referencias en un objeto, lo que provoca una liberación prematura del objeto y un error de la implementación. Una forma sólida de evitar esto es insertar una llamada a AddRef al principio de la implementación del método y emparejarla con una llamada a Release justo antes de que el método devuelva.

En algunas situaciones, los valores devueltos de AddRef y Release pueden ser inestables y no deben confiarse; solo se deben usar con fines de depuración o diagnóstico.

administrar las duraciones de objetos mediante el recuento de referencias