Udostępnij za pośrednictwem


Reguły zarządzania liczbami odwołań

Użycie liczby odwołań do zarządzania okresem istnienia obiektu umożliwia wielu klientom uzyskiwanie i wydawanie dostępu do pojedynczego obiektu bez konieczności koordynowania się ze sobą w zarządzaniu okresem istnienia obiektu. O ile obiekt klienta jest zgodny z pewnymi regułami użycia, obiekt, w efekcie, zapewnia to zarządzanie. Te reguły określają sposób zarządzania odwołaniami między obiektami. (COM nie określa wewnętrznych implementacji obiektów, chociaż te reguły są rozsądnym punktem wyjścia dla zasad w obiekcie).

Koncepcyjnie wskaźniki interfejsu można traktować jako znajdujące się w zmiennych wskaźnika, które obejmują cały stan obliczeń wewnętrznych, który zawiera wskaźnik interfejsu. Dotyczyłoby to zmiennych w lokalizacjach pamięci, w rejestrach procesora wewnętrznego oraz zmiennych generowanych przez programistów i generowanych przez kompilator. Przypisanie do zmiennej wskaźnika lub inicjowanie jej obejmuje utworzenie nowej kopii już istniejącego wskaźnika. W przypadku jednej kopii wskaźnika w jakiejś zmiennej (wartości użytej w przypisania/inicjowaniu) istnieją teraz dwie. Przypisanie do zmiennej wskaźnika niszczy kopię wskaźnika obecnie w zmiennej, podobnie jak zniszczenie samej zmiennej. (Oznacza to, że zakres, w którym znaleziono zmienną, taką jak ramka stosu, jest niszczony).

Z punktu widzenia klienta COM zliczanie odwołań jest zawsze wykonywane dla każdego interfejsu. Klienci nigdy nie powinni zakładać, że obiekt używa tego samego licznika dla wszystkich interfejsów.

Domyślnym przypadkiem jest to, że AddRef musi być wywoływana dla każdej nowej kopii wskaźnika interfejsu i release musi być wywoływana w celu każdego zniszczenia wskaźnika interfejsu, z wyjątkiem sytuacji, gdy następujące reguły zezwalają inaczej:

  • Parametry wyjmujące do funkcji. Obiekt wywołujący musi wywołać AddRef parametru, ponieważ zostanie on wydany (z wywołaniem Release) w kodzie implementowania, gdy wartość out jest przechowywana na nim.
  • Pobieranie zmiennej globalnej. Podczas tworzenia lokalnej kopii wskaźnika interfejsu z istniejącej kopii wskaźnika w zmiennej globalnej należy wywołać AddRef w kopii lokalnej, ponieważ inna funkcja może zniszczyć kopię w zmiennej globalnej, podczas gdy kopia lokalna jest nadal prawidłowa.
  • Nowe wskaźniki syntetyzowane z "powietrza cienkiego". Funkcja, która syntetyzuje wskaźnik interfejsu przy użyciu specjalnej wiedzy wewnętrznej, a nie uzyskiwania go z innego źródła, musi wywołać AddRef początkowo na nowo syntetyzowanym wskaźniku. Ważne przykłady takich procedur obejmują procedury tworzenia wystąpienia, implementacje QueryInterfaceitd.
  • Pobieranie kopii wewnętrznie przechowywanego wskaźnika. Gdy funkcja pobiera kopię wskaźnika przechowywanego wewnętrznie przez obiekt o nazwie, kod tego obiektu musi wywołać AddRef wskaźnika przed zwróceniem funkcji. Po pobraniu wskaźnika obiekt źródłowy nie ma innego sposobu określania, w jaki sposób jego okres istnienia odnosi się do kopii wewnętrznej przechowywanej wskaźnika.

Jedyne wyjątki od przypadku domyślnego wymagają, aby kod zarządzający znał relacje okresów istnienia co najmniej dwóch kopii wskaźnika do tego samego interfejsu na obiekcie i po prostu upewnij się, że obiekt nie zostanie zniszczony, zezwalając na przejście liczby odwołań do zera. Zazwyczaj istnieją dwa przypadki:

  • Gdy jedna kopia wskaźnika już istnieje, a następnie zostanie utworzona druga, a następnie zostanie zniszczona, gdy pierwsza kopia nadal istnieje, wywołania AddRef i release dla drugiej kopii można pominąć.
  • Gdy istnieje jedna kopia wskaźnika, a druga zostanie utworzona, a następnie pierwsza zostanie zniszczona przed drugim, wywołania AddRef dla drugiej kopii i release dla pierwszej kopii można pominąć.

Poniżej przedstawiono konkretne przykłady tych sytuacji, a pierwsze dwa są szczególnie powszechne:

  • W parametrach funkcji. Okres istnienia kopii wskaźnika interfejsu przekazanego jako parametr do funkcji jest zagnieżdżony w wskaźniku używanym do inicjowania wartości, więc nie ma potrzeby oddzielnej liczby odwołań dla parametru.
  • Parametry wychodzące z funkcji, w tym wartości zwracane. Aby ustawić parametr out, funkcja musi mieć stabilną kopię wskaźnika interfejsu. W zamian obiekt wywołujący jest odpowiedzialny za zwolnienie wskaźnika. W związku z tym parametr out nie wymaga oddzielnej liczby odwołań.
  • Zmienne lokalne. Implementacja metody ma kontrolę nad okresami istnienia każdej zmiennej wskaźnika przydzielonej w ramce stosu i może użyć tej metody, aby określić, jak pominąć nadmiarowe AddRef/release par.
  • Backpointers. Niektóre struktury danych zawierają dwa obiekty, z których każdy ma wskaźnik do drugiego. Jeśli okres istnienia pierwszego obiektu jest znany jako zawierający okres istnienia drugiego obiektu, nie trzeba mieć liczby odwołań względem wskaźnika drugiego obiektu do pierwszego obiektu. Często unikanie tego cyklu jest ważne w utrzymaniu odpowiedniego zachowania cofania transakcji. Jednak nieliczane wskaźniki powinny być używane z ekstremalną ostrożnością, ponieważ część systemu operacyjnego, która obsługuje przetwarzanie zdalne, nie ma możliwości poznania tej relacji. W związku z tym, w prawie wszystkich przypadkach, gdy backpointer widzi drugi, "przyjaciel" obiekt pierwszego wskaźnika (w ten sposób unikanie cykliczności) jest preferowanym rozwiązaniem. Na przykład architektura obiektów połączonych modelu COM korzysta z tego podejścia.

Podczas implementowania lub używania obiektów zliczanych do odwołań może być przydatne zastosowanie sztucznych liczników odwołań, co gwarantuje stabilność obiektu podczas przetwarzania funkcji. Implementując metodę interfejsu, można wywołać funkcje, które mogą zmniejszyć liczbę odwołań do obiektu, powodując przedwczesne zwolnienie obiektu i niepowodzenie implementacji. Niezawodnym sposobem uniknięcia tego jest wstawienie wywołania AddRef na początku implementacji metody i sparowanie go z wywołaniem Release tuż przed zwróceniem metody.

W niektórych sytuacjach zwracane wartości AddRef i Release mogą być niestabilne i nie powinny być oparte; powinny być używane tylko do celów debugowania lub diagnostyki.

zarządzanie okresami istnienia obiektów za pomocą zliczania odwołań