Referencias fuertes y débiles de C++/WinRTStrong and weak references in C++/WinRT

Windows Runtime es un sistema con recuento de referencias y en este tipo de sistemas es importante conocer el significado de referencias fuertes y débiles y la diferencia entre ellas (y referencias que no son ninguna de ellas, como el puntero implícito this).The Windows Runtime is a reference-counted system; and in such a system it's important for you to know about the significance of, and distinction between, strong and weak references (and references that are neither, such as the implicit this pointer). Como verás en este tema, saber cómo administrar correctamente estas referencias puede significar la diferencia entre un sistema confiable que funciona sin problemas y otro que se bloquea de forma impredecible.As you'll see in this topic, knowing how to manage these references correctly can mean the difference between a reliable system that runs smoothly, and one that crashes unpredictably. Al proporcionar funciones auxiliares que cuentan con compatibilidad completa en la proyección del lenguaje, C+++/WinRT se encuentra a medio camino en su trabajo de crear sistemas más complejos de forma sencilla y correcta.By providing helper functions that have deep support in the language projection, C++/WinRT meets you halfway in your work of building more complex systems simply and correctly.

Acceso de forma segura al puntero this en una corrutina de miembro de claseSafely accessing the this pointer in a class-member coroutine

Para obtener más información sobre las corrutinas y ejemplos de código, consulta Operaciones simultáneas y asincrónicas con C++/WinRT.For more info about coroutines, and code examples, see Concurrency and asynchronous operations with C++/WinRT.

En la lista de código siguiente se muestra un ejemplo típico de una corrutina que es una función miembro de una clase.The code listing below shows a typical example of a coroutine that's a member function of a class. Puedes copiar y pegar este ejemplo en los archivos especificados en un nuevo proyecto de la aplicación de consola Windows (C++/WinRT).You can copy-paste this example into the specified files in a new Windows Console Application (C++/WinRT) project.

// pch.h
#pragma once
#include <iostream>
#include <winrt/Windows.Foundation.h>

// main.cpp : Defines the entry point for the console application.
#include "pch.h"

using namespace winrt;
using namespace Windows::Foundation;
using namespace std::chrono_literals;

struct MyClass : winrt::implements<MyClass, IInspectable>
{
    winrt::hstring m_value{ L"Hello, World!" };

    IAsyncOperation<winrt::hstring> RetrieveValueAsync()
    {
        co_await 5s;
        co_return m_value;
    }
};

int main()
{
    winrt::init_apartment();

    auto myclass_instance{ winrt::make_self<MyClass>() };
    auto async{ myclass_instance->RetrieveValueAsync() };

    winrt::hstring result{ async.get() };
    std::wcout << result.c_str() << std::endl;
}

MyClass::RetrieveValueAsync pasa algún tiempo trabajando, y, finalmente, devuelve una copia del miembro de datos MyClass::m_value.MyClass::RetrieveValueAsync spends some time working, and eventually it returns a copy of the MyClass::m_value data member. Una llamada a RetrieveValueAsync hace que se cree un objeto asincrónico, y ese objeto tiene un puntero this implícito (a través del cual, eventualmente, se accede a m_value).Calling RetrieveValueAsync causes an asynchronous object to be created, and that object has an implicit this pointer (through which, eventually, m_value is accessed).

Recuerde que, en una corrutina, la ejecución es sincrónica hasta el primer punto de suspensión, donde el control se devuelve al autor de la llamada.Remember that, in a coroutine, execution is synchronous up until the first suspension point, where control is returned to the caller. En RetrieveValueAsync, la primera co_await es el primer punto de suspensión.In RetrieveValueAsync, the first co_await is the first suspension point. Para cuando se reanude la corrutina (unos cinco segundos más tarde, en este caso), podría haber ocurrido algo implícito con el obtjeto this del puntero que usamos para obtener acceso a m_value.By the time the coroutine resumes (around five seconds later, in this case), anything might have happened to the implicit this pointer through which we access m_value.

Esta es la secuencia completa de eventos.Here's the full sequence of events.

  1. En main, se crea una instancia de MyClass (myclass_instance).In main, an instance of MyClass is created (myclass_instance).
  2. Se crea el objeto async, que señala (mediante el puntero this) a myclass_instance.The async object is created, pointing (via its this) to myclass_instance.
  3. La función winrt::Windows::Foundation::IAsyncAction::get alcanza su primer punto de suspensión, se bloquea durante unos segundos y luego devuelve el resultado RetrieveValueAsync.The winrt::Windows::Foundation::IAsyncAction::get function hits its first suspension point, blocks for a few seconds, and then returns the result of RetrieveValueAsync.
  4. RetrieveValueAsync devuelve el valor de this->m_value.RetrieveValueAsync returns the value of this->m_value.

El paso 4 es seguro solo mientras este siga siendo válido.Step 4 is safe only as long as this remains valid.

Pero, ¿qué ocurre si se destruye la instancia de clase antes de que finalice la operación asincrónica?But what if the class instance is destroyed before the async operation completes? Hay todo tipo de formas en las que la instancia de clase podría estar fuera del ámbito de aplicación antes de que el método asíncrono se haya completado.There are all kinds of ways the class instance could go out of scope before the asynchronous method has completed. Aún así, podemos simularla al establecer la instancia de clase en nullptr.But we can simulate it by setting the class instance to nullptr.

int main()
{
    winrt::init_apartment();

    auto myclass_instance{ winrt::make_self<MyClass>() };
    auto async{ myclass_instance->RetrieveValueAsync() };
    myclass_instance = nullptr; // Simulate the class instance going out of scope.

    winrt::hstring result{ async.get() }; // Behavior is now undefined; crashing is likely.
    std::wcout << result.c_str() << std::endl;
}

Después del punto en el que se destruye la instancia de clase, parece que no se hace referencia directamente a ella de nuevo.After the point where we destroy the class instance, it looks like we don't directly refer to it again. Pero por supuesto el objeto asincrónico tiene un puntero this a ella e intenta usarlo para copiar el valor almacenado dentro de la instancia de clase.But of course the asynchronous object has a this pointer to it, and tries to use that to copy the value stored inside the class instance. La corrutina es una función miembro, y espera que pueda usar su puntero this con impunidad.The coroutine is a member function, and it expects to be able to use its this pointer with impunity.

Con este cambio en el código, nos encontramos con un problema en el paso 4, porque se ha destruido la instancia de clase y el puntero this ya no es válido.With this change to the code, we run into a problem in step 4, because the class instance has been destroyed, and this is no longer valid. Tan pronto como el objeto asincrónico intenta obtener acceso a la variable dentro de la instancia de clase, se bloqueará (o hará algo totalmente indefinido).As soon as the asynchronous object attempts to access the variable inside the class instance, it will crash (or do something entirely undefined).

La solución consiste en proporcionar a la operación asincrónica (la corrutina) su propia referencia fuerte a la instancia de clase.The solution is to give the asynchronous operation—the coroutine—its own strong reference to the class instance. Como está escrito actualmente, la corrutina contiene efectivamente un puntero this básico a la instancia de clase; pero eso no es suficiente para mantener activa la instancia de clase.As currently written, the coroutine effectively holds a raw this pointer to the class instance; but that's not enough to keep the class instance alive.

Para mantenerla activa, cambie la implementación de RetrieveValueAsync por la que se muestra a continuación.To keep the class instance alive, change the implementation of RetrieveValueAsync to that shown below.

IAsyncOperation<winrt::hstring> RetrieveValueAsync()
{
    auto strong_this{ get_strong() }; // Keep *this* alive.
    co_await 5s;
    co_return m_value;
}

Una clase C++/WinRT se deriva directa o indirectamente de la plantilla winrt::implements.A C++/WinRT class directly or indirectly derives from the winrt::implements template. Por eso, el objeto C++/WinRT puede llamar a su función miembro protegida implements::get_strong para recuperar una referencia fuerte al puntero this.Because of that, the C++/WinRT object can call its implements::get_strong protected member function to retrieve a strong reference to its this pointer. Ten en cuenta que no hay necesidad de usar la variable strong_this en el ejemplo de código anterior; simplemente al llamar a get_strong, se incrementa el recuento de referencias del objeto C++/WinRT, y mantiene su puntero this implícito válido.Note that there's no need to actually use the strong_this variable in the code example above; simply calling get_strong increments the C++/WinRT object's reference count, and keeps its implicit this pointer valid.

Importante

Dado que get_trong es una función miembro de la plantilla de estructura winrt::implements, puedes llamarla solo desde una clase que derive directa o indirectamente de winrt::implements, como por ejemplo una clase C++/WinRT.Because get_strong is a member function of the winrt::implements struct template, you can call it only from a class that directly or indirectly derives from winrt::implements, such as a C++/WinRT class. Para más información acerca de cómo derivar desde winrt::implements y ver ejemplos, consulta Crear API con C++/WinRT.For more info about deriving from winrt::implements, and examples, see Author APIs with C++/WinRT.

Esto resuelve el problema que teníamos anteriormente cuando llegamos al paso 4.This resolves the problem that we previously had when we got to step 4. Incluso si todas las demás referencias a la instancia de clase desaparecen, la corrutina ha tomado la precaución de garantizar que sus dependencias sean estables.Even if all other references to the class instance disappear, the coroutine has taken the precaution of guaranteeing that its dependencies are stable.

Si una referencia fuerte no es apropiada, entonces puedes llamar a implements::get_weak para recuperar una referencia débil al puntero this.If a strong reference isn't appropriate, then you can instead call implements::get_weak to retrieve a weak reference to this. Solo tienes que confirmar que puedes recuperar una referencia fuerte antes de acceder al puntero this.Just confirm that you can retrieve a strong reference before accessing this. De nuevo, get_weak es una función miembro de la plantilla de estructura winrt::implements.Again, get_weak is a member function of the winrt::implements struct template.

IAsyncOperation<winrt::hstring> RetrieveValueAsync()
{
    auto weak_this{ get_weak() }; // Maybe keep *this* alive.

    co_await 5s;

    if (auto strong_this{ weak_this.get() })
    {
        co_return m_value;
    }
    else
    {
        co_return L"";
    }
}

En el ejemplo anterior, la referencia débil no impide que la instancia de clase se destruya cuando no quedan referencias fuertes.In the example above, the weak reference doesn't keep the class instance from being destroyed when no strong references remain. Pero le da una manera de comprobar si se puede adquirir una referencia fuerte antes de acceder a la variable miembro.But it gives you a way of checking whether a strong reference can be acquired before accessing the member variable.

Acceso de forma segura al puntero this con un delegado de control de eventosSafely accessing the this pointer with an event-handling delegate

El escenarioThe scenario

Para obtener información general sobre el control de eventos, consulta Control de eventos mediante delegados en C++/WinRT.For general info about event-handling, see Handle events by using delegates in C++/WinRT.

En la sección anterior se resaltan los posibles problemas de duración de las áreas de corrutinas y simultaneidad.The previous section highlighted potential lifetime issues in the areas of coroutines and concurrency. Sin embargo, si controlas un evento con la función miembro de un objeto o desde dentro de una función lambda dentro de la función miembro de un objeto, debes tener en cuenta las duraciones relativas del destinatario del evento (el objeto que controla el evento) y el origen del evento (el objeto que genera el evento).But, if you handle an event with an object's member function, or from within a lambda function inside an object's member function, then you need to think about the relative lifetimes of the event recipient (the object handling the event) and the event source (the object raising the event). Echemos un vistazo a algunos ejemplos de código.Let's look at some code examples.

En la lista de códigos que aparece a continuación se define en primer lugar una clase sencilla EventSource, que provoca un evento genérico que controlan los delegados que se hayan agregado a él.The code listing below first defines a simple EventSource class, which raises a generic event that's handled by any delegates that have been added to it. Este ejemplo de evento utiliza el tipo de delegado Windows::Foundation::EventHandler, pero los problemas y soluciones se aplican aquí a todos los tipos de delegado.This example event happens to use the Windows::Foundation::EventHandler delegate type, but the issues and remedies here apply to any and all delegate types.

Después, la clase EventRecipient proporciona un controlador para el evento EventSource::Event en forma de una función lambda.Then, the EventRecipient class provides a handler for the EventSource::Event event in the form of a lambda function.

// pch.h
#pragma once
#include <iostream>
#include <winrt/Windows.Foundation.h>

// main.cpp : Defines the entry point for the console application.
#include "pch.h"

using namespace winrt;
using namespace Windows::Foundation;

struct EventSource
{
    winrt::event<EventHandler<int>> m_event;

    void Event(EventHandler<int> const& handler)
    {
        m_event.add(handler);
    }

    void RaiseEvent()
    {
        m_event(nullptr, 0);
    }
};

struct EventRecipient : winrt::implements<EventRecipient, IInspectable>
{
    winrt::hstring m_value{ L"Hello, World!" };

    void Register(EventSource& event_source)
    {
        event_source.Event([&](auto&& ...)
        {
            std::wcout << m_value.c_str() << std::endl;
        });
    }
};

int main()
{
    winrt::init_apartment();

    EventSource event_source;
    auto event_recipient{ winrt::make_self<EventRecipient>() };
    event_recipient->Register(event_source);
    event_source.RaiseEvent();
}

El patrón es que el receptor del evento tiene un controlador de eventos lambda con dependencias en su puntero this.The pattern is that the event recipient has a lambda event handler with dependencies on its this pointer. Cada vez que el destinatario del evento sobrevive al origen del evento, sobrevive a esas dependencias.Whenever the event recipient outlives the event source, it outlives those dependencies. Y en esos casos, que son comunes, el patrón funciona bien.And in those cases, which are common, the pattern works well. Algunos de estos casos son evidentes, por ejemplo, cuando una página de interfaz de usuario controla un evento generado por un control que se encuentra en la página.Some of these cases are obvious, such as when a UI page handles an event raised by a control that's on the page. La página sobrevive al botón, por lo que el controlador también lo sobrevive.The page outlives the button—so, the handler also outlives the button. Esto es válido siempre que el destinatario posea el origen (como un miembro de datos, por ejemplo), o cada vez que el destinatario y el origen estén relacionados o pertenezcan directamente a otro objeto.This holds true any time the recipient owns the source (as a data member, for example), or any time the recipient and the source are siblings and directly owned by some other object.

Cuando estés seguro de que tienes un caso en el que el controlador no sobrevivirá al objeto this del que depende, puedes capturar this de forma normal, sin tener en cuenta una duración segura o no segura.When you're sure you have a case where the handler won't outlive the this that it depends on, then you can capture this normally, without consideration for strong or weak lifetime.

Pero todavía hay casos donde this no sobrevive a su uso en un controlador (incluidos los controladores para eventos de finalización y progreso generados por acciones y operaciones asincrónicas) y es importante saber cómo lidiar con ellos.But there are still cases where this doesn't outlive its use in a handler (including handlers for completion and progress events raised by asynchronous actions and operations), and it's important to know how to deal with them.

  • Cuando un origen de eventos genera sus eventos sincrónicamente, puedes revocar el controlador y estar seguro de que no recibirás más eventos.When an event source raises its events synchronously, you can revoke your handler and be confident that you won't receive any more events. Pero para los eventos asincrónicos, incluso después de la revocación (y especialmente al revocar dentro del destructor), un evento en curso podría alcanzar el objeto después de que se haya iniciado la destrucción.But for asynchronous events, even after revoking (and especially when revoking within the destructor), an in-flight event might reach your object after it has started destructing. Buscar un lugar para cancelar la suscripción antes de la destrucción puede mitigar el problema, pero sigue leyendo para conocer una solución más estable.Finding a place to unsubscribe prior to destruction might mitigate the issue, but continue reading for a robust solution.
  • Si vas a crear una corrutina para implementar un método asincrónico, entonces es posible.If you're authoring a coroutine to implement an asynchronous method, then it's possible.
  • En raras ocasiones con ciertos objetos del marco de la interfaz de usuario XAML (SwapChainPanel, por ejemplo) es posible, siempre que se haya finalizado el destinatario sin anular el registro del origen del evento.In rare cases with certain XAML UI framework objects (SwapChainPanel, for example), then it's possible, if the recipient is finalized without unregistering from the event source.

El problemaThe issue

Esta versión de la función main simula lo que sucede cuando el destinatario del evento se destruye (tal vez se salga de ámbito) mientras el origen del evento sigue generando eventos.This next version of the main function simulates what happens when the event recipient is destroyed (perhaps it goes out of scope) while the event source is still raising events.

int main()
{
    winrt::init_apartment();

    EventSource event_source;
    auto event_recipient{ winrt::make_self<EventRecipient>() };
    event_recipient->Register(event_source);
    event_recipient = nullptr; // Simulate the event recipient going out of scope.
    event_source.RaiseEvent(); // Behavior is now undefined within the lambda event handler; crashing is likely.
}

Se destruye el destinatario del evento, pero el controlador del evento lambda dentro de él todavía sigue suscrito al evento Event.The event recipient is destroyed, but the lambda event handler within it is still subscribed to the Event event. Cuando se produce ese evento, la expresión lambda intenta desreferenciar el puntero this, que no es válido en ese momento.When that event is raised, the lambda attempts to dereference the this pointer, which is at that point invalid. Por lo tanto, se produce una infracción de acceso desde el código en el controlador (o en la continuación de una corrutina) que intenta utilizarlo.So, an access violation results from code in the handler (or in a coroutine's continuation) attempting to use it.

Importante

Si encuentras una situación como esta, tendrás que pensar en la duración del objeto this; y si el objeto this capturado sobrevive o no a la captura.If you encounter a situation like this, then you'll need to think about the lifetime of the this object; and whether or not the captured this object outlives the capture. Si no es así, captúralo con una referencia fuerte o débil, como te mostramos a continuación.If it doesn't, then capture it with a strong or a weak reference, as we'll demonstrate below.

O bien—si tiene sentido para tu escenario y si las consideraciones de subprocesos lo hacen posible—, otra opción es revocar el controlador una vez hecho el destinatario con el evento o en el destructor del destinatario.Or—if it makes sense for your scenario, and if threading considerations make it even possible—then another option is to revoke the handler after the recipient is done with the event, or in the recipient's destructor. Consulta Revocación de un delegado registrado.See Revoke a registered delegate.

Así es cómo registramos el controlador.This is how we're registering the handler.

event_source.Event([&](auto&& ...)
{
    std::wcout << m_value.c_str() << std::endl;
});

La expresión lambda captura automáticamente las variables locales por referencia.The lambda automatically captures any local variables by reference. Así que, para este ejemplo, podríamos haber escrito esto de forma equivalente.So, for this example, we could equivalently have written this.

event_source.Event([this](auto&& ...)
{
    std::wcout << m_value.c_str() << std::endl;
});

En ambos casos, solo estamos capturando el puntero this básico.In both cases, we're just capturing the raw this pointer. Y esto no tiene ningún efecto en el recuento de referencias, por lo que nada impide que el objeto actual se destruya.And that has no effect on reference-counting, so nothing is preventing the current object from being destroyed.

La soluciónThe solution

La solución consiste en capturar una referencia fuerte (o, como veremos, una referencia débil si es más adecuada).The solution is to capture a strong reference (or, as we'll see, a weak reference if that's more appropriate). Una referencia fuerte incrementa el recuento de referencias y mantiene activo el objeto actual.A strong reference does increment the reference count, and it does keep the current object alive. Solo tienes que declarar una variable de captura (llamada strong_this en este ejemplo) e inicializarla con una llamada a implements::get_strong, que recupera una referencia fuerte a nuestro puntero this.You just declare a capture variable (called strong_this in this example), and initialize it with a call to implements::get_strong, which retrieves a strong reference to our this pointer.

Importante

Dado que get_trong es una función miembro de la plantilla de estructura winrt::implements, puedes llamarla solo desde una clase que derive directa o indirectamente de winrt::implements, como por ejemplo una clase C++/WinRT.Because get_strong is a member function of the winrt::implements struct template, you can call it only from a class that directly or indirectly derives from winrt::implements, such as a C++/WinRT class. Para más información acerca de cómo derivar desde winrt::implements y ver ejemplos, consulta Crear API con C++/WinRT.For more info about deriving from winrt::implements, and examples, see Author APIs with C++/WinRT.

event_source.Event([this, strong_this { get_strong()}](auto&& ...)
{
    std::wcout << m_value.c_str() << std::endl;
});

Incluso puedes omitir la captura automática del objeto actual y acceder al miembro de datos mediante la variable de captura en lugar de mediante la variable this implícita.You can even omit the automatic capture of the current object, and access the data member through the capture variable instead of via the implicit this.

event_source.Event([strong_this { get_strong()}](auto&& ...)
{
    std::wcout << strong_this->m_value.c_str() << std::endl;
});

Si una referencia fuerte no es apropiada, entonces puedes llamar a implements::get_weak para recuperar una referencia débil al puntero this.If a strong reference isn't appropriate, then you can instead call implements::get_weak to retrieve a weak reference to this. Una referencia débil no mantiene activo el objeto actual.A weak reference does not keep the current object alive. Por tanto, solo tienes que confirmar que todavía puedes recuperar una referencia fuerte desde la referencia débil antes de acceder a los miembros.So, just confirm that you can still retrieve a strong reference from the weak reference before accessing members.

event_source.Event([weak_this{ get_weak() }](auto&& ...)
{
    if (auto strong_this{ weak_this.get() })
    {
        std::wcout << strong_this->m_value.c_str() << std::endl;
    }
});

Si capturas un puntero sin formato, tienes que asegurarte de mantener activo el objeto al que apuntas.If you capture a raw pointer, then you'll need to make sure you keep the pointed-to object alive.

Si usas una función miembro como delegadoIf you use a member function as a delegate

Además de las funciones lambda, estos principios también se aplican al uso de una función miembro como tu delegado.As well as lambda functions, these principles also apply to using a member function as your delegate. La sintaxis es diferente, así que veamos algún código.The syntax is different, so let's look at some code. En primer lugar, este es el controlador de eventos de la función miembro potencialmente inseguro, que utiliza un puntero this básico.First, here's the potentially unsafe member function event handler, using a raw this pointer.

struct EventRecipient : winrt::implements<EventRecipient, IInspectable>
{
    winrt::hstring m_value{ L"Hello, World!" };

    void Register(EventSource& event_source)
    {
        event_source.Event({ this, &EventRecipient::OnEvent });
    }

    void OnEvent(IInspectable const& /* sender */, int /* args */)
    {
        std::wcout << m_value.c_str() << std::endl;
    }
};

Se trata de la manera estándar y convencional de hacer referencia a un objeto y a su función miembro.This is the standard, conventional way to refer to an object and its member function. Para que sea seguro, puedes (a partir de la versión 10.0.17763.0 [Windows 10, versión 1809] de Windows SDK) establecer una referencia fuerte o débil en el punto en el que está registrado el controlador.To make this safe, you can—as of version 10.0.17763.0 (Windows 10, version 1809) of the Windows SDK—establish a strong or a weak reference at the point where the handler is registered. En ese momento, se sabe que el objeto de destinatario del evento sigue activo.At that point, the event recipient object is known to be still alive.

Para obtener una referencia fuerte, solo tienes que llamar a get_strong en lugar al puntero this básico.For a strong reference, just call get_strong in place of the raw this pointer. C++/WinRT garantiza que el delegado resultante tenga contiene una referencia fuerte al objeto actual.C++/WinRT ensures that the resulting delegate holds a strong reference to the current object.

event_source.Event({ get_strong(), &EventRecipient::OnEvent });

La captura de una referencia fuerte significa que el objeto será pasible de ser destruido solo después de que se haya anulado el registro del controlador y se hayan devuelto todas las devoluciones de llamada pendientes.Capturing a strong reference means that your object will become eligible for destruction only after the handler has been unregistered and all outstanding callbacks have returned. Sin embargo, esa garantía solo es válida en el momento en que se genera el evento.However, that guarantee is valid only at the time the event is raised. Si el controlador de eventos es asincrónico, tendrás que dar a la corrutina una referencia fuerte a la instancia de clase antes del primer punto de suspensión (para más detalles y el código, consulta la sección Acceso de forma segura al puntero this en una corrutina de miembro de clase anteriormente en este tema).If your event handler is asynchronous, then you'll have to give your coroutine a strong reference to the class instance before the first suspension point (for details, and code, see the Safely accessing the this pointer in a class-member coroutine section earlier in this topic). Pero esto crea una referencia circular entre el origen del evento y tu objeto, por lo que debes interrumpirlo explícitamente revocando el evento.But that creates a circular reference between the event source and your object, so you need to explicitly break that by revoking your event.

Para obtener una referencia débil, llama a get_weak.For a weak reference, call get_weak. C++/ WinRT garantiza que el delegado resultante contiene una referencia débil.C++/WinRT ensures that the resulting delegate holds a weak reference. En el último momento, y en segundo plano, el delegado intenta resolver la referencia débil a una fuerte y solo llama a la función miembro si es correcto.At the last minute, and behind the scenes, the delegate attempts to resolve the weak reference to a strong one, and only calls the member function if it's successful.

event_source.Event({ get_weak(), &EventRecipient::OnEvent });

Si el delegado llama a la función miembro, C++/WinRT mantendrá el objeto activo hasta que vuelva el controlador.If the delegate does call your member function, then C++/WinRT will keep your object alive until your handler returns. Sin embargo, si el controlador es asincrónico, se devuelve en los puntos de suspensión, por lo que tendrás que dar a la corrutina una referencia fuerte a la instancia de clase antes del primer punto de suspensión.However, if your handler is asynchronous, then it returns at suspension points, and so you'll have to give your coroutine a strong reference to the class instance before the first suspension point. De nuevo, para más información, consulta la sección Acceso de forma segura al puntero this en una corrutina de miembro de clase anteriormente en este tema.Again, for more info, see Safely accessing the this pointer in a class-member coroutine section earlier in this topic.

Ejemplo de referencia débil con SwapChainPanel::CompositionScaleChangedA weak reference example using SwapChainPanel::CompositionScaleChanged

En este ejemplo de código, usamos el evento SwapChainPanel::CompositionScaleChanged a modo de otra ilustración de referencias débiles.In this code example, we use the SwapChainPanel::CompositionScaleChanged event by way of another illustration of weak references. El código registra un controlador de eventos mediante un lambda que captura una referencia débil al destinatario.The code registers an event handler using a lambda that captures a weak reference to the recipient.

winrt::Windows::UI::Xaml::Controls::SwapChainPanel m_swapChainPanel;
winrt::event_token m_compositionScaleChangedEventToken;

void RegisterEventHandler()
{
    m_compositionScaleChangedEventToken = m_swapChainPanel.CompositionScaleChanged([weak_this{ get_weak() }]
        (Windows::UI::Xaml::Controls::SwapChainPanel const& sender,
        Windows::Foundation::IInspectable const& object)
    {
        if (auto strong_this{ weak_this.get() })
        {
            strong_this->OnCompositionScaleChanged(sender, object);
        }
    });
}

void OnCompositionScaleChanged(Windows::UI::Xaml::Controls::SwapChainPanel const& sender,
    Windows::Foundation::IInspectable const& object)
{
    // Here, we know that the "this" object is valid.
}

En la cláusula de captura lamba, se crea una variable temporal que representa una referencia débil a this.In the lamba capture clause, a temporary variable is created, representing a weak reference to this. En el cuerpo del lambda, si puede obtenerse una referencia fuerte a this, se llama a la función OnCompositionScaleChanged.In the body of the lambda, if a strong reference to this can be obtained, then the OnCompositionScaleChanged function is called. De esta forma, this puede usarse con seguridad dentro de ** OnCompositionScaleChanged**.That way, inside OnCompositionScaleChanged, this can safely be used.

Referencias débiles de C++/WinRTWeak references in C++/WinRT

Anteriormente, hemos visto que se utilizaban referencias débiles.Above, we saw weak references being used. En general, son buenas para interrumpir las referencias cíclicas.In general, they're good for breaking cyclic references. Por ejemplo, para la implementación nativa del marco de la interfaz de usuario basada en XAML (debido al diseño histórico del marco), el mecanismo de referencia débil en C++/WinRT es necesario para controlar referencias cíclicas.For example, for the native implementation of the XAML-based UI framework—because of the historical design of the framework—the weak reference mechanism in C++/WinRT is necessary to handle cyclic references. Sin embargo, fuera de XAML es probable que no necesites usar referencias débiles (no es que haya algo inherentemente específico de XAML en ellas).Outside of XAML, though, you likely won't need to use weak references (not that there's anything inherently XAML-specific about them). Más bien, la mayoría de las veces deberías poder diseñar tu propia API de C++/WinRT de modo que se evite la necesidad de referencias cíclicas y referencias débiles.Rather you should, more often than not, be able to design your own C++/WinRT APIs in such a way as to avoid the need for cyclic references and weak references.

Para cualquier tipo en particular que declares, a C++/WinRT no le resulta inmediatamente evidente saber si se necesitan referencias débiles o cuándo se necesitan.For any given type that you declare, it's not immediately obvious to C++/WinRT whether or when weak references are needed. De este modo, C++/WinRT proporciona soporte de referencia débil automáticamente en la plantilla de estructura winrt::implements, desde la que tus propios tipos C++/WinRT derivan directa o indirectamente.So, C++/WinRT provides weak reference support automatically on the struct template winrt::implements, from which your own C++/WinRT types directly or indirectly derive. Es un sistema de pago pay-to-play, es decir, no te cuesta nada a menos que en realidad se consulte tu objeto para IWeakReferenceSource.It's pay-for-play, in that it doesn't cost you anything unless your object is actually queried for IWeakReferenceSource. Y puedes optar por no recibir tal soporte explícitamente.And you can choose explicitly to opt out of that support.

Ejemplos de códigoCode examples

La plantilla de estructura winrt::weak_ref es una opción para obtener una referencia débil a una instancia de clase.The winrt::weak_ref struct template is one option for getting a weak reference to a class instance.

Class c;
winrt::weak_ref<Class> weak{ c };

O bien, puedes usar la función auxiliar winrt::make_weak.Or, you can use the use the winrt::make_weak helper function.

Class c;
auto weak = winrt::make_weak(c);

Crear una referencia débil no afecta el recuento de referencias en el propio objeto; simplemente hace que se asigne un bloque de control.Creating a weak reference doesn't affect the reference count on the object itself; it just causes a control block to be allocated. Dicho bloque de control se encarga de implementar la semántica de la referencia débil.That control block takes care of implementing the weak reference semantics. Puedes intentar promover la referencia débil a una referencia fuerte y, si se realiza correctamente, utilizarla.You can then try to promote the weak reference to a strong reference and, if successful, use it.

if (Class strong = weak.get())
{
    // use strong, for example strong.DoWork();
}

Siempre que exista alguna otra referencia fuerte, la llamada weak_ref::get incrementa el recuento de referencias y devuelve la referencia fuerte al autor de la llamada.Provided that some other strong reference still exists, the weak_ref::get call increments the reference count and returns the strong reference to the caller.

Optar por no recibir el soporte de referencia débilOpting out of weak reference support

El soporte de referencia débil es automático.Weak reference support is automatic. Pero puedes optar por no recibir explícitamente tal soporte pasando la estructura del marcador winrt::no_weak_ref como un argumento de plantilla a tu clase base.But you can choose explicitly to opt out of that support by passing the winrt::no_weak_ref marker struct as a template argument to your base class.

Si derivas directamente de winrt::implements.If you derive directly from winrt::implements.

struct MyImplementation: implements<MyImplementation, IStringable, no_weak_ref>
{
    ...
}

Si vas a crear una clase en tiempo de ejecución.If you're authoring a runtime class.

struct MyRuntimeClass: MyRuntimeClassT<MyRuntimeClass, no_weak_ref>
{
    ...
}

No importa dónde aparezca la estructura del marcador dentro del paquete de parámetro variádicas.It doesn't matter where in the variadic parameter pack the marker struct appears. Si solicitas una referencia débil para un tipo que has optado por no recibir, el compilador te ayudará con "This is only for weak ref support" (Esto es solo para soporte de referencia débil).If you request a weak reference for an opted-out type, then the compiler will help you out with "This is only for weak ref support".

API importantesImportant APIs