Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
winrt::implements yapı şablonu kendi C++/WinRT uygulamalarınızın (çalışma zamanı sınıflarının ve etkinleştirme fabrikalarının) doğrudan veya dolaylı olarak türetildiği temeldir.
Bu konu, winrt::implements C++/WinRT 2.0'daki uzantı noktalarını açıklar. Denetlenebilir nesnelerin varsayılan davranışını özelleştirmek için uygulama türlerinizde bu uzantı noktalarını uygulamayı seçebilirsiniz (IInspectable arabirimi açısından denetlenebilir).
Bu uzantı noktaları, uygulama türlerinizin yok edilmesini ertelemenize, yok etme sırasında güvenle sorgulama yapmanıza ve projeksiyon edilen yöntemlerinize giriş ve çıkışları bağlamanıza olanak tanır. Bu konu başlığı altında bu özellikler açıklanır ve bunları ne zaman ve nasıl kullanacağınız hakkında daha fazla bilgi ve bulabilirsiniz.
Ertelenen yıkım
"Doğrudan Ayırımların Teşhisi" konusuna, uygulama türünüzün özel bir yıkıcıya sahip olamayacağını belirtmiştik.
Genel bir destructor'a sahip olmanın avantajı, ertelenmiş imhaya olanak tanımasıdır; bu, nesnenizdeki son IUnknown::Release çağrısını algılayarak, bu nesne üzerindeki kontrolü alıp imhasını süresiz olarak erteleyebilme yeteneğidir.
Klasik COM nesnelerinin içsel olarak başvuru sayıldığını hatırlayın; başvuru sayısı IUnknown::AddRef ve IUnknown::Release işlevleri aracılığıyla yönetilir. Yayıngeleneksel bir uygulamasında, referans sayısı 0'a ulaştığında bir COM nesnesinin C++ destrüktörü çağrılır.
uint32_t WINRT_CALL Release() noexcept
{
uint32_t const remaining{ subtract_reference() };
if (remaining == 0)
{
delete this;
}
return remaining;
}
delete this;
, nesnenin kapladığı belleği boşaltmadan önce nesnenin yıkıcısını çağırır. Yıkıcınızda ilginç bir şey yapmanıza gerek olmadığı şartıyla bu işlem yeterince iyi çalışır.
using namespace winrt::Windows::Foundation;
...
struct Sample : implements<Sample, IStringable>
{
winrt::hstring ToString() const;
~Sample() noexcept
{
// Too late to do anything interesting.
}
};
ilginç derken neyi kastediyoruz? Bir destrüktör doğal olarak eşzamanlıdır. İş parçacıkları arasında geçiş yapamazsınız; belki de iş parçacığına özgü bazı kaynakları farklı bir bağlamda yok edebilirsiniz. Belirli kaynakları boşaltmak için ihtiyacınız olabilecek başka bir arabirim için nesneyi güvenilir bir şekilde sorgulayamazsınız. Liste devam ediyor. Yıkımınızın önemsiz olmadığı durumlarda daha esnek bir çözüme ihtiyacınız vardır. Burada C++/WinRT'nin final_release işlevi devreye girer.
struct Sample : implements<Sample, IStringable>
{
winrt::hstring ToString() const;
static void final_release(std::unique_ptr<Sample> ptr) noexcept
{
// This is the first stop...
}
~Sample() noexcept
{
// ...And this happens only when *unique_ptr* finally deletes the object.
}
};
nesnenizin başvuru sayısı 0'a geçtiğinde final_release çağırmak için Yayın C++/WinRT uygulamasını güncelleştirdik. Bu durumda, nesne başka bekleyen başvuru olmadığından emin olabilir ve artık kendi özel sahipliğine sahiptir. Bu nedenle, sahipliğini statik final_release işlevine aktarabilir.
Başka bir deyişle, nesne kendisini paylaşılan sahipliği destekleyen bir nesneden özel mülkiyete dönüştürmüştür. std::unique_ptr, nesnenin özel mülkiyetine sahiptir ve bu nedenle semantiğinin bir parçası olarak nesneyi doğal olarak yok eder—bu nedenle, std::unique_ptr, başka bir yere taşınmadığı sürece kapsam dışına çıktığında bir public destructor’a ihtiyaç vardır. Anahtar da bu. std::unique_ptr nesneyi canlı tutması koşuluyla, nesnesini süresiz olarak kullanabilirsiniz. Aşağıda nesneyi başka bir yere nasıl taşıyabileceğinizi gösteren bir çizim gösterilmiştir.
struct Sample : implements<Sample, IStringable>
{
winrt::hstring ToString() const;
static void final_release(std::unique_ptr<Sample> ptr) noexcept
{
batch_cleanup.push_back(std::move(ptr));
}
};
Bu kod, nesnesini batch_cleanup adlı bir koleksiyona kaydeder ve bu koleksiyonda işleri, uygulamanın çalışma zamanının gelecekteki bir noktasındaki tüm nesneleri temizlemek olacaktır.
Normalde, std::unique_ptr yok edildiğinde nesne yok edilir, ancak std::unique_ptr::resetçağırarak yok edilmesini sağlayabilirsiniz; veya std::unique_ptr bir yere kaydederek erteleyebilirsiniz.
Belki de daha pratik ve daha güçlü bir şekilde, final_release işlevini bir eş yordama dönüştürebilir ve gerektiğinde iş parçacıklarını askıya alıp değiştirebilirken nihai yıkımını tek bir yerde işleyebilirsiniz.
struct Sample : implements<Sample, IStringable>
{
winrt::hstring ToString() const;
static winrt::fire_and_forget final_release(std::unique_ptr<Sample> ptr) noexcept
{
co_await winrt::resume_background(); // Unwind the calling thread.
// Safely perform complex teardown here.
}
};
Askıya alma noktası, ilk olarak IUnknown::Release işlevine yapılan çağrıyı başlatan iş parçacığının geri dönmesine neden olur ve böylece çağırana, bir zamanlar sahip olduğu nesnenin artık bu arabirim işaretçisi üzerinden erişilemeyeceğini bildirir. UI çerçevelerinin genellikle nesneleri başlangıçta oluşturan belirli ui iş parçacığında yok edilmesini sağlaması gerekir. Bu özellik, imha nesnenin serbest bırakılmasından ayrıldığından bu tür bir gereksinimin yerine getirilmesini önemsiz hale getirir.
final_release geçirilen nesnenin yalnızca bir C++ nesnesi olduğunu unutmayın; artık bir COM nesnesi değil. Örneğin, nesneye yapılan mevcut COM zayıf referansları artık çözülmez.
Yok etme sırasında güvenli sorgular
Ertelenmiş yok etme kavramını temel alarak, yıkım sırasında arabirimleri güvenli bir şekilde sorgulama yeteneğidir.
Klasik COM, iki merkezi kavramı temel alır. Birincisi başvuru sayma, ikincisi ise arabirimleri sorgulamaktır. AddRef ve Release'e ek olarak, IUnknown arabirimi QueryInterfacesağlar. Bu yöntem, XAML gibi belirli UI çerçeveleri tarafından, XAML hiyerarşisinin birleştirilebilir tür sisteminin benzetimini yaptığı şekilde çapraz geçiş yapmak için yoğun olarak kullanılır. Basit bir örnek düşünün.
struct MainPage : PageT<MainPage>
{
~MainPage()
{
DataContext(nullptr);
}
};
Bu zararsız görünebilir. Bu XAML sayfası, kendi yıkıcısında veri bağlamını temizlemek istiyor. Ancak DataContext, FrameworkElement temel sınıfının bir özelliğidir ve ayrı IFrameworkElement arabiriminde bulunur. Sonuç olarak C++/WinRT, DataContext özelliğini çağırabilmek için doğru vtable'ı aramak amacıyla QueryInterface çağrısını eklemelidir. Ancak yok edicinin içinde olma nedenimiz, başvuru sayısının 0'a geçmesidir. Burada QueryInterface çağrılması, bu başvuru sayısını geçici olarak artırır; ve tekrar 0'a döndüğünde nesne yeniden yok edilir.
C++/WinRT 2.0 bunu desteklemek için sağlamlaştırılmıştır. İşte basitleştirilmiş bir biçimde C++/WinRT 2.0'da Release uygulaması.
uint32_t Release() noexcept
{
uint32_t const remaining{ subtract_reference() };
if (remaining == 0)
{
m_references = 1; // Debouncing!
T::final_release(...);
}
return remaining;
}
Tahmin etmiş olabileceğiniz gibi, önce başvuru sayısını azaltır ve ardından yalnızca başvuru olmadığında hareket eder. Ancak, bu konuda daha önce açıkladığımız statik final_release işlevini çağırmadan önce, başvuru sayısını 1 olarak ayarlayarak kararlı hale getirmektedir. Bunu (elektrik mühendisliğinden bir terim ödünç alma) olarak adlandırıyoruz. Bu, son başvurunun yayınlanmasını önlemek için kritik öneme sahiptir. Bu durumda başvuru sayısı istikrarsız hale gelir ve
Son başvuru yayımlandıktan sonra queryinterface
Bunu, referans sayısını dengeleyerek yapar. Son referans serbest bırakıldığında, referans sayısı ya 0’dır ya da tamamen beklenmedik bir değerde olabilir. İkinci durum, zayıf başvurular söz konusu olduğunda ortaya çıkabilir. Her iki durumda da, QueryInterface için sonraki bir çağrı gerçekleşirse bu durum sürdürülemez; çünkü bu, başvuru sayısının geçici olarak artmasına neden olur ve bu da debouncing kavramına gönderme yapar. 1'e ayarlamak, bu nesne üzerinde Serbest Bırakma için bir daha asla son bir çağrı yapılmamasını sağlar. std::unique_ptr artık nesnenin sahibi olduğundan tam olarak istediğimiz budur, ancak QueryInterface/Release çiftlerine yönelik sınırlanmış çağrılar güvenli olacaktır.
Daha ilginç bir örnek düşünün.
struct MainPage : PageT<MainPage>
{
~MainPage()
{
DataContext(nullptr);
}
static winrt::fire_and_forget final_release(std::unique_ptr<MainPage> ptr)
{
co_await 5s;
co_await winrt::resume_foreground(ptr->Dispatcher());
ptr = nullptr;
}
};
İlk olarak, final_release işlevi çağrılır ve uygulamaya temizleme zamanının geldiğini bildirir. Burada, final_release bir coroutine olduğu ortaya çıkar. İş parçacığı havuzunda birkaç saniye bekleyerek ilk askıya alma noktasının simülasyonunu başlatır. Ardından sayfanın dağıtıcı iş parçacığında devam eder.
DispatcherDependencyObject temel sınıfının bir özelliği olduğundan bu son adım bir sorgu içerir. Son olarak, sayfa aslında nullptr
'nın std::unique_ptr'ye atanması aracılığıyla silinir. Bu da sayfanın yok edicisini çağırır.
Yıkıcıda veri bağlamını temizleriz, çünkü bildiğimiz gibi, FrameworkElement temel sınıfı için bir sorgu gerektirir.
C++/WinRT 2.0 tarafından sağlanan başvuru sayısı kaldırma (veya başvuru sayısı sabitleme) nedeniyle bunların tümü mümkündür.
Yöntem girişi ve çıkış kancaları
Daha az yaygın kullanılan uzantı noktası, abi_guard yapısı ve abi_enter ve abi_exit işlevleridir.
Uygulama türünüz bir abi_enterişlevi tanımlıyorsa, bu işlev, IInspectableyöntemleri hariç, yansıtılan arabirim yöntemlerinizin her birine girişte çağrılır.
Benzer şekilde, abi_exittanımlarsanız, bu tür her yöntemden çıkışta çağrılır; ancak abi_enter özel durum oluşturursa çağrılmaz. Yansıtılan arabirim yönteminizin kendisi bir istisna oluşturduğunda bile, yine de çağrılacaktır.
Örneğin, bir istemci, bir nesneyi kullanılamaz duruma getiren (mesela, ShutDown veya Disconnect yöntemi çağrısından sonra) bir eylemden sonra yine de kullanmaya çalışırsa, varsayımsal invalid_state_error istisnasını fırlatmak için abi_enter kullanabilirsiniz. C++/WinRT yineleyici sınıfları, temel alınan koleksiyon değiştiyse abi_enter işlevinde geçersiz durum özel durumu atmak için bu özelliği kullanır.
Basit abi_enter ve abi_exitişlevlerinin ötesinde, abi_guardadlı iç içe geçmiş bir tür tanımlayabilirsiniz. Bu durumda, projeksiyon arabirim yöntemlerinizin her birine (IInspectable olmayan) girişinde, bir abi_guard örneği oluşturulur ve bu örneğin oluşturucu parametresi olarak nesneye bir referans verilir. Yöntemden çıkışta abi_guard ardından yok edilecektir. İstediğiniz ek durumu abi_guard türüne yerleştirebilirsiniz.
kendi abi_guardtanımlamazsanız, oluşturulurken abi_enter ve yıkılırken abi_exit çağıran varsayılan bir tane vardır.
Bu korumalar yalnızcayansıtılan arabirim aracılığıyla
İşte bir kod örneği.
struct Sample : SampleT<Sample, IClosable>
{
void abi_enter();
void abi_exit();
void Close();
};
void example1()
{
auto sampleObj1{ winrt::make<Sample>() };
sampleObj1.Close(); // Calls abi_enter and abi_exit.
}
void example2()
{
auto sampleObj2{ winrt::make_self<Sample>() };
sampleObj2->Close(); // Doesn't call abi_enter nor abi_exit.
}
// A guard is used only for the duration of the method call.
// If the method is a coroutine, then the guard applies only until
// the IAsyncXxx is returned; not until the coroutine completes.
IAsyncAction CloseAsync()
{
// Guard is active here.
DoWork();
// Guard becomes inactive once DoOtherWorkAsync
// returns an IAsyncAction.
co_await DoOtherWorkAsync();
// Guard is not active here.
}