Share via


참조 개수 관리 규칙

참조 개수를 사용하여 개체 수명을 관리하면 여러 클라이언트가 개체의 수명을 관리할 때 서로 조정할 필요 없이 단일 개체에 대한 액세스를 가져오고 해제할 수 있습니다. 클라이언트 개체가 특정 사용 규칙을 준수하는 해당하는 개체는 사실상 이 관리를 제공합니다. 이러한 규칙은 개체 간의 참조를 관리하는 방법을 지정합니다. (COM은 개체의 내부 구현을 지정하지 않지만 이러한 규칙은 개체 내의 정책에 적합한 시작점이 됩니다.)

개념적으로 인터페이스 포인터는 인터페이스 포인터를 보유하는 모든 내부 계산 상태를 포함하는 포인터 변수 내에 있는 것으로 간주할 수 있습니다. 여기에는 메모리 위치 및 내부 프로세서 레지스터 내의 변수 및 프로그래머 생성 변수와 컴파일러 생성 변수가 모두 포함됩니다. 포인터 변수에 할당하거나 초기화하는 데는 기존 포인터의 새 복사본을 만드는 작업이 포함됩니다. 일부 변수(할당/초기화에 사용된 값)에는 포인터 복사본이 하나만 있었지만 이제는 두 개의 포인터가 있습니다. 포인터 변수에 대한 할당은 변수 자체의 삭제와 마찬가지로 현재 변수에 있는 포인터 복사본을 삭제합니다. (즉, 스택 프레임과 같이 변수가 발견되는 범위가 삭제됩니다.)

COM 클라이언트의 관점에서 참조 계산은 항상 각 인터페이스에 대해 수행됩니다. 클라이언트는 개체가 모든 인터페이스에 대해 동일한 카운터를 사용한다고 가정해서는 안 됩니다.

기본 사례는 인터페이스 포인터의 모든 새 복사본에 대해 AddRef를 호출해야 하며, 다음 규칙이 달리 허용하는 경우를 제외하고 인터페이스 포인터의 모든 삭제에 대해 Release를 호출해야 합니다:

  • 함수에 대한 인아웃 매개 변수. 호출자는 출력 값이 그 위에 저장될 때 구현 코드에서 릴리스(릴리스 호출 포함)되므로 매개 변수에서 AddRef를 호출해야 합니다.
  • 전역 변수 가져오기. 전역 변수에 있는 포인터의 기존 복사본에서 인터페이스 포인터의 로컬 복사본을 만들 때 로컬 복사본이 유효한 기간 동안 다른 함수가 전역 변수의 복사본을 삭제할 수 있으므로 로컬 복사본에서 AddRef를 호출해야 합니다.
  • 새로운 포인터는 갑작스럽게 합성되었습니다. 다른 소스에서 가져오는 대신 특수 내부 지식을 사용하여 인터페이스 포인터를 합성하는 함수는 새로 합성된 포인터에서 처음에 AddRef를 호출해야 합니다. 이러한 루틴의 중요한 예로는 인스턴스 만들기 루틴, QueryInterface 구현 등이 있습니다.
  • 내부적으로 저장된 포인터의 복사본 검색하기. 함수가 호출된 개체에 의해 내부적으로 저장된 포인터의 복사본을 검색하는 경우 해당 개체의 코드는 함수가 반환되기 전에 포인터에서 AddRef를 호출해야 합니다. 포인터가 검색되면 원본 개체는 수명이 포인터의 내부적으로 저장된 복사본과 어떤 관련이 있는지 결정할 수 있는 다른 방법이 없습니다.

기본 사례에 대한 유일한 예외는 관리 코드가 개체의 동일한 인터페이스에 대한 두 개 이상의 포인터 복사본의 수명 관계를 알고 있어야 하며 참조 개수가 0으로 이동하도록 허용하여 개체가 삭제되지 않도록 해야 합니다. 일반적으로 다음과 같은 두 가지 경우가 있습니다.

  • 포인터의 복사본 하나가 이미 존재하고 그 후에 두 번째 복사본이 생성된 다음 첫 번째 복사본이 여전히 존재하는 동안 제거되면 두 번째 복사본에 대한 AddRefRelease 호출을 생략할 수 있습니다.
  • 포인터의 복사본이 하나 존재하고 두 번째 복사본이 만들어지고 두 번째 복사본 전에 첫 번째 복사본이 제거되면 두 번째 복사본에 대한 AddRef 및 첫 번째 복사본에 대한 Release 호출을 생략할 수 있습니다.

다음은 이러한 상황의 구체적인 예시이며, 처음 두 가지는 특히 일반적입니다.

  • 함수에 대한 매개 변수. 함수에 매개 변수로 전달된 인터페이스 포인터의 복사본 수명은 값을 초기화하는 데 사용되는 포인터의 수명에 중첩되므로 매개 변수에 대한 별도의 참조 개수가 필요하지 않습니다.
  • 반환 값을 포함한 함수에서의 out 매개 변수. out 매개 변수를 설정하려면 함수에 인터페이스 포인터의 안정적인 복사본이 있어야 합니다. 반환 시 호출자는 반환된 포인터를 릴리스해야 합니다. 따라서 out 매개 변수에는 별도의 참조 개수가 필요하지 않습니다.
  • 지역 변수. 메서드 구현은 스택 프레임에 할당된 각 포인터 변수의 수명을 제어하며 이를 사용하여 중복 AddRef/Release 쌍을 생략하는 방법을 결정할 수 있습니다.
  • 백포인터. 일부 데이터 구조에는 각각 다른 개체에 대한 포인터가 있는 두 개의 개체가 포함됩니다. 첫 번째 개체의 수명이 두 번째 개체의 수명을 포함하는 것으로 알려진 경우 첫 번째 개체에 대한 두 번째 개체 포인터의 참조 개수를 가질 필요가 없습니다. 적절한 할당 해제 동작을 유지하는 데 있어 이 주기를 피하는 것이 중요한 경우가 있습니다. 그러나 원격 처리를 처리하는 운영 체제의 부분에는 이 관계에 대해 전혀 알지 못하기 때문에 계산되지 않은 포인터는 매우 주의해서 사용해야 합니다. 따라서 거의 모든 경우에 백포인터가 첫 번째 포인터의 두 번째 "friend" 개체를 볼 수 있게 하여 순환을 피하는 것이 바람직한 솔루션입니다. 예를 들어 COM의 연결 가능한 개체 아키텍처는 이 방법을 사용합니다.

참조 개수 개체를 구현하거나 사용하는 경우 함수를 처리하는 동안 개체 안정성을 보장하는 인공 참조 개수를 적용하는 것이 유용할 수 있습니다. 인터페이스의 메서드를 구현할 때 개체에 대한 참조 개수가 감소하여 개체가 조기에 릴리스되고 구현이 실패할 가능성이 있는 함수를 호출할 수 있습니다. 이를 방지하는 강력한 방법은 메서드 구현의 시작 부분에 AddRef 호출을 삽입하고 메서드가 반환되기 직전에 Release 호출과 페어링하는 것입니다.

경우에 따라 AddRefRelease 반환 값이 불안정할 수 있으며 여기에 의존해서는 안 됩니다. 디버깅 또는 진단 목적으로만 사용해야 합니다.

참조 계산을 통해 개체 수명 관리