Bagaimana cara kerja kumpulan autoreleasePool NSAutoreleasePool?

95

Seperti yang saya pahami, apa pun yang dibuat dengan alokasi , baru , atau salinan harus dirilis secara manual. Sebagai contoh:

int main(void) {
    NSString *string;
    string = [[NSString alloc] init];
    /* use the string */
    [string release];
}

Pertanyaan saya, bukankah ini sama validnya ?:

int main(void) {
    NSAutoreleasePool *pool;
    pool = [[NSAutoreleasePool alloc] init];
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
    [pool drain];
}
James Sumners
sumber

Jawaban:

68

Ya, potongan kode kedua Anda benar-benar valid.

Setiap kali -autorelease dikirim ke sebuah objek, itu ditambahkan ke kumpulan autorelease paling dalam. Saat pool dikosongkan, itu hanya mengirim -release ke semua objek di pool.

Kumpulan Autorelease hanyalah kemudahan yang memungkinkan Anda untuk menunda pengiriman -release sampai "nanti". Itu "nanti" bisa terjadi di beberapa tempat, tapi yang paling umum di aplikasi Cocoa GUI adalah di akhir siklus run loop saat ini.

kperryua
sumber
5
di mana akhir dari siklus pengulangan saat ini, jika saya tidak memiliki pengulangan?
Terima kasih
24
Bukankah seharusnya "paling luar" menjadi "paling dalam"?
Mike Weller
an objectseharusnya an object that is a subclass of NSObject or NSProxy and doesn't override -autorelease.
1
EDIT: Mengubah paling luar menjadi paling dalam.
chakrit
1
Penting: Jika Anda menggunakan Penghitungan Referensi Otomatis (ARC), Anda tidak dapat menggunakan kumpulan rilis otomatis secara langsung. Sebagai gantinya, Anda menggunakan blok @autoreleasepool. Dari developer.apple.com/library/mac/#documentation/Cocoa/Reference/…
Md Mahbubur Rahman
37

NSAutoreleasePool: tiriskan vs. lepaskan

Karena fungsi dari draindan releasetampaknya menyebabkan kebingungan, mungkin perlu dijelaskan di sini (meskipun hal ini tercakup dalam dokumentasi ...).

Sebenarnya, dari perspektif gambaran besar drainadalah tidak setara dengan release:

Dalam lingkungan yang dihitung referensi, drainmelakukan operasi yang sama seperti release, sehingga keduanya dalam pengertian itu setara. Untuk menekankan, ini berarti Anda tidak membocorkan kolam jika Anda menggunakan draindaripada release.

Dalam lingkungan yang mengumpulkan sampah, releasetidak ada operasi. Jadi itu tidak berpengaruh. drain, di sisi lain, berisi petunjuk kepada kolektor bahwa ia harus "mengumpulkan jika diperlukan". Jadi dalam lingkungan pengumpulan sampah, penggunaan drainmembantu menyapu pengumpulan keseimbangan sistem.

mmalc
sumber
4
Pada dasarnya tidak mungkin untuk 'membocorkan' a NSAutoreleasePool. Ini karena kumpulan beroperasi seperti tumpukan. Membuat instance kumpulan akan mendorong kumpulan itu ke bagian atas tumpukan kumpulan rilis otomatis thread tersebut. -releasemenyebabkan kumpulan tersebut keluar dari tumpukan DAN kumpulan apa pun yang didorong ke atasnya, tetapi untuk alasan apa pun tidak muncul.
johne
7
Dalam hal apa ini relevan dengan apa yang saya tulis?
mmalc
2
Saya suka bagaimana dia meluangkan waktu untuk berani DAN. JEPRET!
Billy Grey
17

Seperti yang telah ditunjukkan, potongan kode kedua Anda sudah benar.

Saya ingin menyarankan cara yang lebih ringkas untuk menggunakan kumpulan autorelease yang berfungsi di semua lingkungan (penghitungan ref, GC, ARC) dan juga menghindari kebingungan pembuangan / rilis:

int main(void) {
  @autoreleasepool {
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
  }
}

Dalam contoh di atas, harap perhatikan blok @autoreleasepool . Ini didokumentasikan di sini .

Neovibrant
sumber
2
Harap dicatat, bahwa autorelease tidak diizinkan dengan ARC.
dmirkitanov
1
Untuk memperjelas, seseorang harus menggunakan @autoreleasepoolblok dengan ARC.
Simon
7

Tidak, kamu salah. Dokumentasi menyatakan dengan jelas bahwa di bawah non-GC, -drain setara dengan -release, yang berarti NSAutoreleasePool tidak akan bocor.

kperryua
sumber
Saya bertanya-tanya mengapa Xcode akan menghasilkan kode dengan -drain jika itu masalahnya. Saya menggunakan -drain karena saya pikir itu setara dengan -release berdasarkan kode yang dihasilkan oleh Xcode.
James Sumners
1
Pada dasarnya tidak mungkin untuk 'membocorkan' a NSAutoreleasePool: developer.apple.com/mac/library/documentation/Cocoa/Conceptual/…
johne
0

mengirim autorelease dan bukan rilis ke suatu objek akan memperpanjang masa pakai objek tersebut setidaknya hingga kumpulan itu sendiri dikosongkan (mungkin lebih lama jika objek tersebut dipertahankan kemudian). Sebuah objek dapat dimasukkan ke dalam pool yang sama beberapa kali, dalam hal ini objek menerima pesan rilis untuk setiap kali dimasukkan ke dalam pool.

Hardik Mamtora
sumber
-2

Iya dan tidak. Anda akhirnya akan melepaskan memori string tetapi "membocorkan" objek NSAutoreleasePool ke dalam memori dengan menggunakan drain, bukan rilis jika Anda menjalankan ini di lingkungan sampah yang dikumpulkan (bukan yang dikelola memori). "Kebocoran" ini hanya membuat instance NSAutoreleasePool "tidak dapat dijangkau" seperti objek lain tanpa petunjuk kuat di bawah GC, dan objek tersebut akan dibersihkan saat GC berjalan, yang bisa jadi langsung setelah panggilan ke -drain:

menguras

Dalam lingkungan pengumpulan sampah, memicu pengumpulan sampah jika memori yang dialokasikan sejak pengumpulan terakhir lebih besar dari ambang batas saat ini; jika tidak berperilaku sebagai rilis. ... Dalam lingkungan pengumpulan sampah, metode ini akhirnya memanggil objc_collect_if_needed.

Jika tidak, ini mirip dengan -releaseberperilaku di bawah non-GC, ya. Seperti yang dinyatakan orang lain, -releaseadalah tidak ada operasi di bawah GC, jadi satu-satunya cara untuk memastikan fungsi kumpulan dengan baik di bawah GC adalah melalui -drain, dan di -drainbawah non-GC bekerja persis seperti di -releasebawah non-GC, dan bisa dibilang mengkomunikasikan fungsinya dengan lebih jelas sebagai baik.

Saya harus menunjukkan bahwa pernyataan Anda "apa pun yang dipanggil dengan new, alokasi, atau init" tidak boleh menyertakan "init" (tetapi harus menyertakan "copy"), karena "init" tidak mengalokasikan memori, ia hanya menyiapkan objek (konstruktor mode). Jika Anda menerima objek yang dialokasikan dan fungsi Anda hanya memanggil init seperti itu, Anda tidak akan melepaskannya:

- (void)func:(NSObject*)allocd_but_not_init
{
    [allocd_but_not_init init];
}

Itu tidak menghabiskan lebih banyak memori daripada yang sudah Anda mulai (dengan asumsi init tidak membuat instance objek, tetapi Anda tidak bertanggung jawab untuk itu).

Loren Segal
sumber
Saya tidak merasa nyaman membiarkan jawaban ini diterima ketika informasi Anda tentang drain kurang tepat. Lihat developer.apple.com/documentation/Cocoa/Reference/Foundation/… Perbarui dan saya akan menerimanya kembali.
James Sumners
Apa yang tidak akurat tentang balasannya? Dalam lingkungan pengumpulan sampah (seperti yang dinyatakan), drain tidak menghapus AutoReleasePool, jadi Anda akan membocorkan memori kecuali Anda menggunakan rilis. Kutipan yang saya cantumkan langsung dari mulut kuda, dokumen-dokumennya habis.
Loren Segal
1
Loren: Di bawah GC, - [NSAutoreleasePool drain] akan memicu pengumpulan. -pertahankan, -release, dan -autorelease semuanya diabaikan oleh kolektor; itulah mengapa -drain digunakan pada kumpulan rilis otomatis di bawah GC.
Chris Hanson
Dalam dokumentasi untuk 'drain': Dalam lingkungan memori terkelola, ini berperilaku sama seperti memanggil rilis. Dengan demikian Anda akan melakukannya tidak membocorkan memori jika Anda menggunakan 'drain' sebagai pengganti rilis.
mmalc
-[NSAutoreleasePool release]di lingkungan yang dikumpulkan sampah adalah no-op. -[NSAutoreleasePool drain]bekerja di lingkungan yang dihitung referensi dan dikumpulkan sampahnya.
Jonathan Sterling