Dalam situasi apa kita perlu menulis kualifikasi kepemilikan __autoreleasing di bawah ARC?

118

Saya mencoba menyelesaikan teka-teki.

__strongadalah default untuk semua penunjuk objek yang dapat dipertahankan Objective-C seperti NSObject, NSString, dll. Ini adalah referensi yang kuat. ARC menyeimbangkannya dengan a -releasedi akhir ruang lingkup.

__unsafe_unretainedsama dengan cara lama. Ini digunakan untuk penunjuk yang lemah tanpa mempertahankan objek yang dapat dipertahankan.

__weakseperti __unsafe_unretainedkecuali bahwa itu adalah referensi lemah auto-zeroing yang berarti bahwa penunjuk akan disetel ke nol segera setelah objek yang direferensikan dibatalkan alokasinya. Ini menghilangkan bahaya petunjuk yang menggantung dan kesalahan EXC_BAD_ACCESS.

Tapi sebenarnya __autoreleasingbagus untuk apa? Saya kesulitan menemukan contoh praktis tentang kapan saya perlu menggunakan kualifikasi ini. Saya percaya itu hanya untuk fungsi dan metode yang mengharapkan pointer-pointer seperti:

- (BOOL)save:(NSError**);

atau

NSError *error = nil;
[database save:&error];

yang di bawah ARC harus dinyatakan seperti ini:

- (BOOL)save:(NSError* __autoreleasing *);

Tapi ini terlalu kabur dan saya ingin memahami alasannya . Potongan kode yang saya temukan menempatkan __autoreleasing di antara dua bintang, yang terlihat aneh bagi saya. NSError**Jenisnya adalah (pointer-pointer ke NSError), jadi mengapa menempatkan di __autoreleasingantara bintang-bintang dan tidak hanya di depan NSError**?

Juga, mungkin ada situasi lain yang harus saya andalkan __autoreleasing.

Anggota Bangga
sumber
1
Saya memiliki pertanyaan yang sama dan jawaban di bawah ini tidak sepenuhnya meyakinkan ... misalnya, mengapa sistem tidak menyediakan antarmuka yang menerima argumen NSError ** yang dideklarasikan dengan dekorator __autoreleasing seperti Anda dan Catatan Rilis Transisi ke Arc mengatakan mereka seharusnya? misalnya, Salah satu dari banyak rutinitas ini di NSFileManager.h ??
Ayah

Jawaban:

67

Kamu benar. Seperti yang dijelaskan dalam dokumentasi resmi:

__autoreleasing untuk menunjukkan argumen yang diteruskan oleh referensi (id *) dan dirilis secara otomatis saat kembali.

Semua ini dijelaskan dengan sangat baik dalam panduan transisi ARC .

Dalam contoh NSError Anda, deklarasi tersebut berarti __strong, secara implisit:

NSError * e = nil;

Akan diubah menjadi:

NSError * __strong error = nil;

Saat Anda memanggil savemetode Anda :

- ( BOOL )save: ( NSError * __autoreleasing * );

Kompilator kemudian harus membuat variabel sementara, disetel ke __autoreleasing. Begitu:

NSError * error = nil;
[ database save: &error ];

Akan diubah menjadi:

NSError * __strong error = nil;
NSError * __autoreleasing tmpError = error;
[ database save: &tmpError ];
error = tmpError;

Anda dapat menghindari ini dengan mendeklarasikan objek kesalahan sebagai __autoreleasing, secara langsung.

Macmade
sumber
3
Tidak, __autoreleasinghanya digunakan untuk argumen yang diteruskan oleh referensi. Ini adalah kasus khusus, karena Anda memiliki penunjuk ke penunjuk objek. Itu tidak terjadi dengan hal-hal seperti konstruktor praktis, karena mereka hanya mengembalikan pointer ke objek, dan karena ARC menanganinya secara otomatis.
Macmade
7
Mengapa kualifikasi __autoreleasing ditempatkan di antara bintang-bintang, dan tidak hanya di depan NSError **? Ini terlihat aneh bagi saya karena tipenya adalah NSError **. Atau karena ini mencoba untuk menunjukkan bahwa pointer NSError * yang diarahkan ke harus memenuhi syarat sebagai menunjuk ke objek autoreleased?
Anggota Bangga
1
@Bangga Anggota sehubungan dengan komentar pertama Anda - itu salah (jika saya mengerti Anda dengan benar) - lihat jawaban Glen Low di bawah. Objek kesalahan dibuat dan ditetapkan ke variabel pelepasan otomatis (yang Anda berikan) di dalam fungsi simpan. Penugasan ini menyebabkan objek dipertahankan dan dirilis otomatis pada saat itu. Deklarasi fungsi save mencegah kita mengirimkan apapun selain variabel autoreleasing karena itulah yang dibutuhkannya - itulah mengapa compiler membuat variabel sementara jika kita mencobanya.
Colin
2
Jadi mengapa tidak ada antarmuka Apple yang memiliki ini? misalnya, semua yang ada di NSFileManager.h?
Ayah
1
@Macmade: Secara kebetulan saya melihat bahwa jawaban Anda telah diedit (oleh stackoverflow.com/users/12652/comptrol ) dan saya mendapat kesan bahwa setidaknya perubahan pada contoh pertama Anda ("secara implisit ... akan diubah menjadi ...) salah, karena kualifikasi __strong telah dipindahkan dari baris kedua ke baris pertama. Mungkin Anda bisa memeriksanya.
Martin R
34

Menindaklanjuti jawaban Macmade dan pertanyaan lanjutan Anggota Bangga di komentar, (juga akan memposting ini sebagai komentar tetapi melebihi jumlah karakter maksimal):

Inilah alasan mengapa variabel qualifier __autoreleasing ditempatkan di antara dua bintang.

Sebagai pengantar, sintaks yang benar untuk mendeklarasikan pointer objek dengan qualifier adalah:

NSError * __qualifier someError;

Kompiler akan memaafkan ini:

__qualifier NSError *someError;

tapi itu tidak benar. Lihat panduan transisi ARC Apple (baca bagian yang memulai "Anda harus menghias variabel dengan benar ...").

Untuk menjawab pertanyaan yang ada: Penunjuk ganda tidak dapat memiliki kualifikasi manajemen memori ARC karena penunjuk yang menunjuk ke alamat memori adalah penunjuk ke tipe primitif, bukan penunjuk ke objek. Namun, saat Anda mendeklarasikan penunjuk ganda, ARC ingin mengetahui apa aturan manajemen memori untuk penunjuk kedua. Itulah mengapa variabel penunjuk ganda ditentukan sebagai:

SomeClass * __qualifier *someVariable;

Jadi dalam kasus argumen metode yang merupakan penunjuk NSError ganda, tipe datanya dideklarasikan sebagai:

- (BOOL)save:(NSError* __autoreleasing *)errorPointer;

yang dalam bahasa Inggris mengatakan "penunjuk ke __autoreleasing penunjuk objek NSError".

Binyamin Bauman
sumber
15

The definitif ARC spesifikasi mengatakan bahwa

Untuk objek __autoreleasing, penunjuk baru dipertahankan, dilepas otomatis, dan disimpan ke lvalue menggunakan semantik primitif.

Jadi misalnya kodenya

NSError* __autoreleasing error = someError;

sebenarnya akan diubah menjadi

NSError* error = [[someError retain] autorelease];

... itulah sebabnya ini berfungsi ketika Anda memiliki parameter NSError* __autoreleasing * errorPointer, metode yang dipanggil kemudian akan menetapkan kesalahan *errorPointerdan semantik di atas akan dimulai.

Anda dapat menggunakan __autoreleasingdalam konteks yang berbeda untuk memaksa objek ARC ke dalam kumpulan autorelease, tetapi itu tidak terlalu berguna karena ARC sepertinya hanya menggunakan kumpulan autorelease pada metode return dan sudah menanganinya secara otomatis.

Glen Low
sumber