Mengelola Masa Pakai Objek

Ada aturan untuk antarmuka COM yang belum kami sebutkan. Setiap antarmuka COM harus mewarisi, secara langsung atau tidak langsung, dari antarmuka bernama IUnknown. Antarmuka ini menyediakan beberapa kemampuan garis besar yang harus didukung oleh semua objek COM.

Antarmuka IUnknown mendefinisikan tiga metode:

Metode QueryInterface memungkinkan program untuk mengkueri kemampuan objek pada durasi. Kita akan mengatakan lebih lanjut tentang itu di topik berikutnya, Meminta Objek untuk Antarmuka. Metode AddRef dan Release digunakan untuk mengontrol masa pakai objek. Ini adalah subjek topik ini.

Penghitungan Referensi

Apa pun yang mungkin dilakukan program, pada titik tertentu program akan mengalokasikan dan membebaskan sumber daya. Mengalokasikan sumber daya itu mudah. Mengetahui kapan harus membebaskan sumber daya sulit, terutama jika masa pakai sumber daya meluas di luar cakupan saat ini. Masalah ini tidak unik untuk COM. Setiap program yang mengalokasikan memori timbunan harus menyelesaikan masalah yang sama. Misalnya, C++ menggunakan destruktor otomatis, sementara C# dan Java menggunakan pengumpulan sampah. COM menggunakan pendekatan yang disebut penghitungan referensi.

Setiap objek COM mempertahankan jumlah internal. Ini dikenal sebagai jumlah referensi. Jumlah referensi melacak berapa banyak referensi ke objek yang saat ini aktif. Ketika jumlah referensi turun ke nol, objek akan menghapus dirinya sendiri. Bagian terakhir patut diulang: Objek menghapus dirinya sendiri. Program tidak pernah secara eksplisit menghapus objek.

Berikut adalah aturan untuk penghitungan referensi:

  • Saat objek pertama kali dibuat, jumlah referensinya adalah 1. Pada titik ini, program memiliki satu penunjuk ke objek .
  • Program dapat membuat referensi baru dengan menduplikasi (menyalin) penunjuk. Saat menyalin penunjuk, Anda harus memanggil metode AddRef objek. Metode ini menaikkan jumlah referensi satu per satu.
  • Setelah selesai menggunakan penunjuk ke objek , Anda harus memanggil Rilis. Metode Rilis mengurangi jumlah referensi satu per satu. Ini juga membatalkan penunjuk. Jangan gunakan penunjuk lagi setelah Anda memanggil Rilis. (Jika Anda memiliki penunjuk lain ke objek yang sama, Anda dapat terus menggunakan penunjuk tersebut.)
  • Ketika Anda telah memanggil Rilis dengan setiap pointer, jumlah referensi objek objek mencapai nol, dan objek menghapusnya sendiri.

Diagram berikut menunjukkan kasus sederhana tetapi khas.

Diagram yang memperlihatkan kasus sederhana penghitungan referensi.

Program membuat objek dan menyimpan penunjuk (p) ke objek. Pada titik ini, jumlah referensi adalah 1. Ketika program selesai menggunakan penunjuk, program memanggil Rilis. Jumlah referensi dikurangi menjadi nol, dan objek menghapus dirinya sendiri. Sekarang p tidak valid. Ini adalah kesalahan untuk menggunakan p untuk panggilan metode lebih lanjut.

Diagram berikutnya menunjukkan contoh yang lebih kompleks.

ilustrasi yang memperlihatkan penghitungan referensi

Di sini, program membuat objek dan menyimpan pointer p, seperti sebelumnya. Selanjutnya, program menyalin p ke variabel baru, q. Pada titik ini, program harus memanggil AddRef untuk menambah jumlah referensi. Jumlah referensi sekarang 2, dan ada dua penunjuk yang valid ke objek . Sekarang misalkan program selesai menggunakan p. Program memanggil Rilis, jumlah referensi ke 1, dan p tidak lagi valid. Namun, q masih valid. Kemudian, program selesai menggunakan q. Oleh karena itu, ia memanggil Rilis lagi. Jumlah referensi masuk ke nol, dan objek menghapus dirinya sendiri.

Anda mungkin bertanya-tanya mengapa program akan menyalin p. Ada dua alasan utama: Pertama, Anda mungkin ingin menyimpan penunjuk dalam struktur data, seperti daftar. Kedua, Anda mungkin ingin menyimpan penunjuk di luar cakupan variabel asli saat ini. Oleh karena itu, Anda akan menyalinnya ke variabel baru dengan cakupan yang lebih luas.

Salah satu keuntungan dari penghitungan referensi adalah Anda dapat berbagi penunjuk di berbagai bagian kode, tanpa berbagai jalur kode yang berkoordinasi untuk menghapus objek. Sebaliknya, setiap jalur kode hanya memanggil Rilis ketika jalur kode tersebut dilakukan menggunakan objek . Objek menangani penghapusan dirinya sendiri pada waktu yang benar.

Contoh

Berikut adalah kode dari contoh kotak dialog Buka lagi.

HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED |
    COINIT_DISABLE_OLE1DDE);

if (SUCCEEDED(hr))
{
    IFileOpenDialog *pFileOpen;

    hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
            IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));

    if (SUCCEEDED(hr))
    {
        hr = pFileOpen->Show(NULL);
        if (SUCCEEDED(hr))
        {
            IShellItem *pItem;
            hr = pFileOpen->GetResult(&pItem);
            if (SUCCEEDED(hr))
            {
                PWSTR pszFilePath;
                hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
                if (SUCCEEDED(hr))
                {
                    MessageBox(NULL, pszFilePath, L&quot;File Path&quot;, MB_OK);
                    CoTaskMemFree(pszFilePath);
                }
                pItem->Release();
            }
        }
        pFileOpen->Release();
    }
    CoUninitialize();
}

Penghitungan referensi terjadi di dua tempat dalam kode ini. Pertama, jika program berhasil membuat objek Dialog Item Umum, program harus memanggil Rilis pada penunjuk pFileOpen .

hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, 
        IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));

if (SUCCEEDED(hr))
{
    // ...
    pFileOpen->Release();
}

Kedua, ketika metode GetResult mengembalikan penunjuk ke antarmuka IShellItem , program harus memanggil Rilis pada pointer pItem .

hr = pFileOpen->GetResult(&pItem);

if (SUCCEEDED(hr))
{
    // ...
    pItem->Release();
}

Perhatikan bahwa dalam kedua kasus, panggilan Rilis adalah hal terakhir yang terjadi sebelum penunjuk keluar dari cakupan. Perhatikan juga bahwa Rilis dipanggil hanya setelah Anda menguji HRESULT agar berhasil. Misalnya, jika panggilan ke CoCreateInstance gagal, penunjuk pFileOpen tidak valid. Oleh karena itu, akan menjadi kesalahan untuk memanggil Rilis pada pointer.

Berikutnya

Meminta Objek untuk Antarmuka