Dela via


Regler för att hantera referensantal

Om du använder ett referensantal för att hantera ett objekts livslängd kan flera klienter hämta och frigöra åtkomst till ett enskilt objekt utan att behöva samordna med varandra för att hantera objektets livslängd. Så länge klientobjektet överensstämmer med vissa användningsregler tillhandahåller objektet i själva verket den här hanteringen. Dessa regler anger hur referenser mellan objekt ska hanteras. (COM anger inte interna implementeringar av objekt, även om dessa regler är en rimlig utgångspunkt för en princip inom ett objekt.)

Konceptuellt kan gränssnittspekare anses finnas i pekarvariabler som innehåller alla interna beräkningstillstånd som innehåller en gränssnittspekare. Detta skulle omfatta variabler på minnesplatser, i interna processorregister och både programmeraregenererade och kompilatorgenererade variabler. Tilldelning till eller initiering av en pekarvariabel innebär att skapa en ny kopia av en redan befintlig pekare. Där det fanns en kopia av pekaren i någon variabel (värdet som användes i tilldelningen/initieringen) finns det nu två. En tilldelning till en pekarvariabel förstör pekarkopian som för närvarande finns i variabeln, liksom förstörelsen av själva variabeln. (Det vill: omfånget där variabeln hittas, till exempel stackramen, förstörs.)

Från en COM-klients perspektiv görs alltid referensräkning för varje gränssnitt. Klienter bör aldrig anta att ett objekt använder samma räknare för alla gränssnitt.

Standardfallet är att AddRef- måste anropas för varje ny kopia av en gränssnittspekare och Release måste anropas för varje destruktion av en gränssnittspekare, förutom när följande regler tillåter något annat:

  • In-out-parametrar till funktioner. Anroparen måste anropa AddRef- på parametern eftersom den släpps (med ett anrop till Release) i implementeringskoden när out-värdet lagras ovanpå den.
  • Hämtar en global variabel. När du skapar en lokal kopia av en gränssnittspekare från en befintlig kopia av pekaren i en global variabel måste du anropa AddRef- på den lokala kopian eftersom en annan funktion kan förstöra kopian i den globala variabeln medan den lokala kopian fortfarande är giltig.
  • Nya pekare som syntetiseras ur "tunn luft". En funktion som syntetiserar en gränssnittspekare med särskild intern kunskap i stället för att hämta den från någon annan källa måste anropa AddRef initialt på den nyligen syntetiserade pekaren. Viktiga exempel på sådana rutiner är rutiner för att skapa instanser, implementeringar av QueryInterfaceoch så vidare.
  • Hämtar en kopia av en internt lagrad pekare. När en funktion hämtar en kopia av en pekare som lagras internt av objektet som anropas måste objektets kod anropa AddRef- på pekaren innan funktionen returneras. När pekaren har hämtats har det ursprungliga objektet inget annat sätt att avgöra hur dess livslängd relaterar till den internt lagrade kopian av pekaren.

De enda undantagen i standardfallet kräver att hanteringskoden känner till relationerna för livslängden för två eller flera kopior av en pekare till samma gränssnitt på ett objekt och helt enkelt ser till att objektet inte förstörs genom att låta dess referensantal gå till noll. Det finns vanligtvis två fall, enligt följande:

  • När en kopia av en pekare redan finns och en andra skapas därefter och sedan förstörs medan den första kopian fortfarande finns, kan anrop till AddRef och Release för den andra kopian utelämnas.
  • När en kopia av en pekare finns och en andra skapas och den första sedan förstörs före den andra, kan anropen till AddRef för den andra kopian och till Release för den första kopian utelämnas.

Följande är specifika exempel på dessa situationer, där de två första är särskilt vanliga:

  • I parametrar till funktioner. Livslängden för kopian av en gränssnittspekare som skickas som en parameter till en funktion är kapslad i den pekare som används för att initiera värdet, så det finns inget behov av ett separat referensantal för parametern.
  • Ut parametrar från funktioner, inklusive returvärden. Om du vill ange utparametern måste funktionen ha en stabil kopia av gränssnittspekaren. Vid retur ansvarar anroparen för att släppa pekaren. Därför behöver parametern out inte ett separat referensantal.
  • Lokala variabler. En metodimplementering har kontroll över livslängden för var och en av pekarvariablerna som allokerats på stackramen och kan använda detta för att avgöra hur du utelämnar redundanta AddRef/Release-par.
  • Backpointers. Vissa datastrukturer innehåller två objekt, var och en med en pekare mot det andra. Om livslängden för det första objektet är känd för att innehålla livslängden för det andra, är det inte nödvändigt att ha ett referensantal på det andra objektets pekare till det första objektet. Ofta är det viktigt att undvika den här cykeln för att upprätthålla rätt frigöringsbeteende. Icke-kontokopplade pekare bör dock användas med extrem försiktighet eftersom den del av operativsystemet som hanterar fjärrbearbetning inte har något sätt att känna till den här relationen. I nästan alla fall är det därför den bästa lösningen att låta backpointern se ett andra "vän"-objekt i den första pekaren (vilket undviker cirkulärheten). COM:s arkitektur för anslutningsbara objekt använder till exempel den här metoden.

När du implementerar eller använder referensberäkningsobjekt kan det vara bra att använda artificiella referensantal, som garanterar objektstabilitet under bearbetningen av en funktion. När du implementerar en -metod för ett gränssnitt kan du anropa funktioner som har en chans att minska ditt referensantal till ett objekt, vilket orsakar en för tidig version av objektet och implementeringen misslyckas. Ett robust sätt att undvika detta är att infoga ett anrop till AddRef- i början av metodimplementeringen och koppla det med ett anrop till Release precis innan metoden returneras.

I vissa situationer kan returvärdena för AddRef och Release vara instabila och bör inte användas. de ska endast användas för felsökning eller diagnostik.

hantera objektlivslängder via referensräkning