Haruskah IBOutlet menjadi kuat atau lemah di bawah ARC?

551

Saya mengembangkan secara eksklusif untuk iOS 5 menggunakan ARC. Haruskah IBOutlets ke UIViews (dan subclass) menjadi strongatau weak?

Pengikut:

@property (nonatomic, weak) IBOutlet UIButton *button;

Akan menyingkirkan semua ini:

- (void)viewDidUnload
{
    // ...
    self.button = nil;
    // ...
}

Apakah ada masalah dalam melakukan ini? Templat menggunakan strongseperti halnya properti yang dibuat secara otomatis dibuat ketika menghubungkan langsung ke header dari editor 'Pembuat Antarmuka', tetapi mengapa? Yang UIViewControllersudah memiliki strongreferensi untuk viewyang mempertahankan subview.

hypercrypt
sumber
11
Sebagai catatan, IBOutletCollection()tidak boleh weak, jika tidak kembali sebagai nil.
ohho
Xcode 8.2.1 menggunakan lemah saat membuat IBOutlets melalui pembangun antarmuka. Namun banyak jawaban di sini di SO menyarankan untuk menggunakan yang kuat.
neoneye
1
@neoneye Saya baru saja mencoba dengan menyeret xcode 8.3.2 dari storyboard ke file cepat dan defaultnya adalahstrong
CupawnTae

Jawaban:

252

Praktik terbaik yang direkomendasikan saat ini dari Apple adalah agar IBOutlets menjadi kuat kecuali jika lemah diperlukan secara khusus untuk menghindari siklus penyimpanan. Seperti yang disebutkan oleh Johannes di atas, ini dikomentari dalam sesi "Menerapkan Desain UI dalam Interface Builder" dari WWDC 2015 di mana seorang Insinyur Apple berkata:

Dan opsi terakhir yang ingin saya tunjukkan adalah tipe penyimpanan, yang bisa kuat atau lemah. Secara umum Anda harus membuat outlet Anda kuat, terutama jika Anda menghubungkan outlet ke subview atau kendala yang tidak selalu akan dipertahankan oleh hierarki tampilan. Satu-satunya saat Anda benar-benar perlu membuat outlet menjadi lemah adalah jika Anda memiliki tampilan khusus yang mereferensikan sesuatu yang mendukung hierarki tampilan dan secara umum itu tidak dianjurkan.

Saya bertanya tentang ini di Twitter kepada seorang insinyur di tim IB dan dia mengkonfirmasi bahwa kuat harus menjadi default dan bahwa dokumen pengembang sedang diperbarui.

https://twitter.com/_danielhall/status/620716996326350848 https://twitter.com/_danielhall/status/620717252216623104

Daniel Hall
sumber
33
Apakah ini benar atau apakah jawabannya dengan 300+ upvotes yang benar? Saya perhatikan bahwa InterfaceBuilder secara default menggunakan lemah ketika Anda Ctrl-drag dari storyboard ke .h
Arunabh Das
4
Yang dengan 400+ suara sudah benar, tetapi ketinggalan jaman. Karena iOS 6 viewDidUnload tidak dipanggil, jadi tidak ada manfaat untuk memiliki outlet yang lemah.
kjam
7
@kjam ada manfaatnya. Pertama dan terpenting Anda tidak harus memiliki referensi kuat untuk sesuatu yang tidak Anda buat. Kedua, perolehan kinerja dapat diabaikan. Jangan melanggar praktik terbaik dalam pemrograman hanya karena beberapa pria, bahkan pria yang ditempatkan dengan baik, mengatakan ini 10 mikrodetik lebih cepat. Maksud kode jelas, jangan coba mainkan kompilator yang optimal. Hanya kode untuk kinerja ketika telah diukur dalam kasus tertentu yang menjadi masalah.
Cameron Lowell Palmer
5
Biarkan saya tidak setuju dengan Anda. 'Memegang referensi kuat untuk sesuatu yang tidak Anda buat' terjadi sepanjang waktu di Objective-C. Itu sebabnya ada penghitungan referensi , bukan satu pemilik. Apakah Anda memiliki referensi untuk mencadangkan rekomendasi ini? Bisakah Anda mendaftarkan keuntungan lain dari gerai yang lemah?
kjam
4
Berikut adalah video WWDC yang disebutkan dalam answer developer.apple.com/videos/play/wwdc2015/407/?time=1946
petrsyn
450

PERINGATAN, JAWABAN KEDUA : jawaban ini tidak mutakhir sesuai WWDC 2015, untuk jawaban yang benar mengacu pada jawaban yang diterima (Daniel Hall) di atas. Jawaban ini akan tetap dicatat.


Dirangkum dari perpustakaan pengembang :

Dari perspektif praktis, di iOS dan OS X outlet harus didefinisikan sebagai properti yang dideklarasikan. Outlet umumnya lemah, kecuali yang dari File's Owner ke objek tingkat atas dalam file nib (atau, di iOS, adegan storyboard) yang harus kuat. Outlet yang Anda buat karena itu biasanya lemah karena:

  • Outlet yang Anda buat, misalnya, subview dari tampilan pengontrol tampilan atau jendela pengontrol jendela, adalah referensi sewenang-wenang antara objek yang tidak menyiratkan kepemilikan.

  • Outlet yang kuat sering ditentukan oleh kelas kerangka kerja (misalnya, outlet tampilan UIViewController's, atau outlet jendela NSWindowController).

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;
Alexsander Akers
sumber
10
Bagaimana Anda mendapatkan tautan "perpustakaan pengembang" untuk melompat ke bagian tertentu dari halaman dokumen Apple? Setiap kali saya menautkan ke dokumen Apple selalu menautkan ke bagian atas halaman (bahkan jika konten yang menarik setengah jalan ke halaman). Terima kasih.
bearMountain
68
Saya menyalin tautan dari panel navigasi di sebelah kiri. : D
Alexsander Akers
27
Apa artinya "kecuali untuk yang dari Pemilik File ke objek tingkat atas dalam file nib (atau, di iOS, adegan storyboard)" "?
Van Du Tran
16
@ VanDuTran - itu berarti objek di NIB yang berada di tingkat root, yaitu mengatakan Anda instantiated tampilan lain di sana yang tidak secara langsung subview dari tampilan utama, maka perlu memiliki referensi yang kuat.
mattjgalloway
6
Tingkat atas berarti bahwa ketika Anda melihat pena, objek muncul di daftar di sebelah kiri. Hampir semua biji memiliki UIView di dalamnya - ini mungkin satu-satunya objek tingkat atas. Jika Anda menambahkan item lain, dan mereka muncul dalam daftar, mereka adalah "objek tingkat atas"
David H
50

Meskipun dokumentasi merekomendasikan penggunaan weakpada properti untuk subview, karena iOS 6 tampaknya baik-baik saja untuk digunakan strong(kualifikasi kepemilikan default) sebagai gantinya. Itu disebabkan oleh perubahan dalam UIViewControllerpandangan yang tidak diturunkan lagi.

  • Sebelum iOS 6, jika Anda menyimpan tautan yang kuat ke subview tampilan pengontrol, jika tampilan utama pengontrol view diturunkan, itu akan menahan subview selama pengontrol tampilan ada.
  • Sejak iOS 6, tampilan tidak diturunkan lagi, tetapi dimuat sekali dan bertahan selama pengontrolnya ada. Jadi sifat yang kuat tidak masalah. Mereka juga tidak akan membuat siklus referensi yang kuat, karena mereka menunjukkan grafik referensi yang kuat.

Yang mengatakan, saya terpecah antara menggunakan

@property (nonatomic, weak) IBOutlet UIButton *button;

dan

@property (nonatomic) IBOutlet UIButton *button;

di iOS 6 dan setelah:

  • Dengan weakjelas menyatakan bahwa pengontrol tidak menginginkan kepemilikan tombol.

  • Tetapi menghilangkan weaktidak ada salahnya di iOS 6 tanpa tampilan bongkar, dan lebih pendek. Beberapa mungkin menunjukkan itu juga lebih cepat, tetapi saya belum menemukan aplikasi yang terlalu lambat karena weak IBOutlets.

  • Tidak menggunakan weakdapat dianggap sebagai kesalahan.

Intinya: Sejak iOS 6 kita tidak bisa mendapatkan kesalahan ini lagi selama kita tidak menggunakan view unloading. Waktunya berpesta. ;)

Tammo Freese
sumber
Itu benar, tetapi Anda mungkin masih ingin membongkar tampilan sendiri. Dalam hal ini Anda harus mengatur semua outlet Anda nilsecara manual.
hypercrypt
PS: weaksedikit lebih murah di ARM64: D
hypercrypt
Itu benar, jika Anda menerapkan tampilan bongkar, weakproperti atau __weakvariabel instan adalah cara untuk pergi. Saya hanya ingin menunjukkan bahwa ada sedikit potensi kesalahan di sini. Adapun weakmenjadi lebih murah di arm64, saya bahkan belum melihat masalah kinerja kehidupan nyata dengan weak IBOutlets pada armv7. :)
Tammo Freese
Dalam hal ini, strongmasuk akal juga. stronghanya berbahaya jika Anda menggunakan view unloading — tetapi siapa yang melakukannya hari ini? :)
Tammo Freese
2
@Rocotilos iPhone pertama memiliki RAM yang sangat terbatas. Jika saya ingat dengan benar, 128 MB, menyisakan sekitar 10 MB untuk aplikasi yang aktif. Memiliki jejak memori yang kecil sangat penting, maka ada pandangan yang membongkar. Itu berubah karena kita sekarang memiliki lebih banyak RAM, dan Apple mengoptimalkan UIViews di iOS 6, sehingga pada peringatan memori, banyak memori dapat dibebaskan tanpa membongkar tampilan.
Tammo Freese
34

Saya tidak melihat masalah dengan itu. Pra-ARC, saya selalu membuat IBOutlet saya assign, karena mereka sudah dipertahankan oleh superview mereka. Jika Anda membuatnya weak, Anda tidak harus menghilangkannya di viewDidUnload, seperti yang Anda tunjukkan.

Satu peringatan: Anda dapat mendukung iOS 4.x dalam proyek ARC, tetapi jika Anda melakukannya, Anda tidak dapat menggunakan weak, jadi Anda harus membuatnya assign, dalam hal ini Anda masih ingin memasukkan referensi viewDidUnloaduntuk menghindari pointer menggantung. Berikut adalah contoh bug penunjuk menggantung yang saya alami:

UIViewController memiliki UITextField untuk kode pos. Ini menggunakan CLLocationManager untuk membalikkan geocode lokasi pengguna dan mengatur kode pos. Inilah panggilan balik delegasi:

-(void)locationManager:(CLLocationManager *)manager
   didUpdateToLocation:(CLLocation *)newLocation
          fromLocation:(CLLocation *)oldLocation {
    Class geocoderClass = NSClassFromString(@"CLGeocoder");
    if (geocoderClass && IsEmpty(self.zip.text)) {
        id geocoder = [[geocoderClass alloc] init];
        [geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) {
            if (self.zip && IsEmpty(self.zip.text)) {
                self.zip.text = [[placemarks objectAtIndex:0] postalCode];
            }
        }];    
    }
    [self.locationManager stopUpdatingLocation];
}

Saya menemukan bahwa jika saya menolak pandangan ini pada waktu yang tepat dan tidak nihil. Zip masuk viewDidUnload, panggilan balik delegasi dapat membuang pengecualian akses buruk pada self.zip.text.

Christopher Pickslay
sumber
4
Saya juga memahami bahwa weakproperti tidak perlu diisi viewDidUnload. Tetapi mengapa template Apple untuk membuat outlet mencakup [self setMySubview:nil]?
Yang Meyer
3
Apakah ada kasus dunia nyata di mana penggunaan kuat / dipertahankan untuk IBOutlet Anda dapat menyebabkan masalah? Atau itu hanya mempertahankan yang berlebihan, yang berarti gaya pengkodean yang buruk tetapi tidak akan mempengaruhi kode Anda?
Enzo Tran
1
Apakah ada yang namanya penahan yang berlebihan? Jika ada retain tambahan, itu akan menyebabkannya tidak dihitung dengan benar, dan karenanya tidak akan dibebaskan secepat mungkin karena ada retain tambahan pada retain countnya.
karlbecker_com
25

IBOutletharus kuat, karena alasan kinerja. Lihat Referensi Storyboard, IBOutlet Kuat, Adegan Dok di iOS 9

Seperti dijelaskan dalam paragraf ini, outlet ke subview tampilan view controller bisa menjadi lemah, karena subview ini sudah dimiliki oleh objek tingkat atas dari file nib. Namun, ketika Outlet didefinisikan sebagai pointer lemah dan pointer diatur, ARC memanggil fungsi runtime:

id objc_storeWeak(id *object, id value);

Ini menambahkan pointer (objek) ke tabel menggunakan nilai objek sebagai kunci. Tabel ini disebut sebagai tabel lemah. ARC menggunakan tabel ini untuk menyimpan semua petunjuk lemah dari aplikasi Anda. Sekarang, ketika nilai objek deallocated, ARC akan beralih di atas tabel lemah dan mengatur referensi lemah ke nil. Atau, ARC dapat menghubungi:

void objc_destroyWeak(id * object)

Kemudian, objek tidak terdaftar dan objc_destroyWeak memanggil lagi:

objc_storeWeak(id *object, nil)

Pembukuan ini terkait dengan referensi yang lemah bisa memakan waktu 2-3 kali lebih lama dari rilis referensi yang kuat. Jadi, referensi yang lemah memperkenalkan overhead untuk runtime yang dapat Anda hindari dengan hanya mendefinisikan outlet sebagai kuat.

Pada Xcode 7, disarankan strong

Jika Anda menonton WWDC 2015 sesi 407 Menerapkan Desain UI di Interface Builder , itu menyarankan (transkrip dari http://asciiwwdc.com/2015/sessions/407 )

Dan opsi terakhir yang ingin saya tunjukkan adalah tipe penyimpanan, yang bisa kuat atau lemah.

Secara umum Anda harus membuat outlet Anda kuat, terutama jika Anda menghubungkan outlet ke tampilan sub atau ke kendala yang tidak selalu akan dipertahankan oleh hierarki tampilan.

Satu-satunya saat Anda benar-benar perlu membuat outlet menjadi lemah adalah jika Anda memiliki tampilan khusus yang mereferensikan sesuatu yang mendukung hierarki tampilan dan secara umum itu tidak dianjurkan.

Jadi saya akan memilih yang kuat dan saya akan mengklik terhubung yang akan menghasilkan outlet saya.

onmyway133
sumber
1
Jawaban bagus yang menjelaskan alasan sebenarnya
mengapa-
Itu bagus dan semua tetapi saya telah melihat kebocoran yang berasal dari gesture recognition diimplementasikan di storyboard.
thibaut noah
1
Saya tidak bisa mengerti kalimat ini. "Satu-satunya saat Anda benar-benar perlu membuat outlet menjadi lemah adalah jika Anda memiliki tampilan khusus yang merujuk sesuatu yang mendukung hierarki tampilan dan secara umum itu tidak dianjurkan." Ada contoh?
user1872384
Saya menghitung waktu deinit yang dibutuhkan oleh yang lemah dan kuat, dan itu persis sama.
touti
Tetapi dengan cepat ini lebih banyak terjadi. Referensi yang lemah lebih cepat.
musim panas
20

Dalam pengembangan iOS, pemuatan NIB sedikit berbeda dari pengembangan Mac.

Dalam pengembangan Mac, IBOutlet biasanya merupakan referensi yang lemah: jika Anda memiliki subkelas NSViewController, hanya tampilan tingkat atas yang akan dipertahankan dan ketika Anda membatalkan pengontrol semua subview dan outletnya dibebaskan secara otomatis.

UiViewController menggunakan Key Value Coding untuk mengatur outlet menggunakan referensi yang kuat. Jadi ketika Anda membatalkan alokasi UIViewController Anda, tampilan teratas akan secara otomatis dialokasikan, tetapi Anda juga harus membatalkan alokasi semua outletnya dalam metode dealloc.

Dalam posting ini dari Peternakan Big Nerd , mereka membahas topik ini dan juga menjelaskan mengapa menggunakan referensi yang kuat di IBOutlet bukanlah pilihan yang baik (bahkan jika direkomendasikan oleh Apple dalam kasus ini).

Giuseppe
sumber
16
Ini menjelaskannya pada 2009. Dengan ARC, ini telah berubah secara signifikan.
Dafydd Williams
1
:( tautan Big Nerd Ranch sudah mati ... namun saya benar-benar harus membacanya. Adakah yang tahu lebih detail tentang pos itu, sehingga saya dapat menemukannya?
Motti Shneor
@MottiShneor jangan khawatir, ini bukan masalah besar karena tautannya sudah sekitar sebelum ARC dan tidak relevan lagi.
Sergey Grischyov
18

Satu hal yang ingin saya tunjukkan di sini, dan itu adalah, terlepas dari apa yang para insinyur Apple nyatakan dalam video WWDC 2015 mereka di sini:

https://developer.apple.com/videos/play/wwdc2015/407/

Apple terus mengubah pikiran mereka tentang masalah ini, yang memberi tahu kita bahwa tidak ada jawaban yang benar untuk pertanyaan ini. Untuk menunjukkan bahwa bahkan insinyur Apple terpecah mengenai hal ini, lihat kode sampel terbaru Apple, dan Anda akan melihat beberapa orang menggunakan yang lemah, dan beberapa tidak.

Contoh Apple Pay ini menggunakan lemah: https://developer.apple.com/library/ios/samplecode/Emporium/Listings/Emporium_ProductTableViewController_swift.html#//apple_ref/doc/uid/TP40016175-Emporium_ProductTableViewController_Wift

Seperti halnya contoh gambar-dalam-gambar ini: https://developer.apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_swift.html#//apple_ref/doc/uid/TP40016166-AVFoundationPlayer_Pelanggan_Pelatihan_PemainKebijakan_Pelajaran_Pemain

Seperti halnya contoh Lister: https://developer.apple.com/library/ios/samplecode/Lister/Listings/Lister_ListCell_swift.html#//apple_ref/doc/uid/TP40014701-Lister_ListCell_swift-DontLinkElementID_57

Seperti halnya contoh Lokasi Inti: https://developer.apple.com/library/ios/samplecode/PotLoc/Listings/Potloc_PotlocViewController_swift.html#//apple_ref/doc/uid/TP40016176-Potloc_Potloc_Potloc_Potloc_Delokkan_ID

Seperti halnya contoh pratinjau pengontrol tampilan: https://developer.apple.com/library/ios/samplecode/viewControllerPelajar/daftar/Tumbuh/Tumbi

Seperti halnya contoh HomeKit: https://developer.apple.com/library/ios/samplecode/HomeKitCatalog/Listings/HMCatalog_Homes_Action_Sets_ActionSetViewController_swift.html#//apple_ref/doc/uid/TP4camp_cetak_dorongAnda_Karena_MemilikiKelasKaca_Memiliki

Semua itu sepenuhnya diperbarui untuk iOS 9, dan semua menggunakan outlet yang lemah. Dari sini kita belajar bahwa A. Masalahnya tidak sesederhana beberapa orang mengetahuinya. B. Apple telah berubah pikiran berulang kali, dan C. Anda dapat menggunakan apa pun yang membuat Anda bahagia :)

Terima kasih khusus kepada Paul Hudson (penulis www.hackingwithsift.com) yang memberi saya klarifikasi, dan referensi untuk jawaban ini.

Saya harap ini memperjelas masalah ini sedikit lebih baik!

Hati hati.

syedfa
sumber
Saya telah memeriksa masalah ini selama beberapa waktu dan belum menemukan jawaban konkret. Karena tautan di atas menunjukkan bahwa keduanya baik-baik saja dan secara umum sesuai dengan saran Xcode.
subin272
9

Dari WWDC 2015 ada sesi tentang Menerapkan Desain UI di Interface Builder . Sekitar tanda 32 menit ia mengatakan bahwa Anda selalu ingin membuat Anda @IBOutlet kuat .

Johannes
sumber
Menarik. Saya kira ini berubah ketika tampilan bongkar dihapus?
hypercrypt
6

Waspada, IBOutletCollectionseharusnya @property (strong, nonatomic).

Landonandrey
sumber
3
Mengapa tidak copyseperti itu NSArray?
hypercrypt
5

Sepertinya ada sesuatu yang berubah selama bertahun-tahun dan sekarang Apple merekomendasikan untuk menggunakan yang kuat secara umum. Bukti pada sesi WWDC mereka ada di sesi 407 - Menerapkan Desain UI di Interface Builder dan mulai pukul 32:30. Catatan saya dari apa yang dia katakan adalah (hampir, jika tidak tepat, mengutipnya):

  • koneksi outlet secara umum harus kuat terutama jika kita menghubungkan subview atau kendala yang tidak selalu dipertahankan oleh hierarki tampilan

  • koneksi outlet yang lemah mungkin diperlukan saat membuat tampilan khusus yang memiliki referensi untuk sesuatu yang dicadangkan dalam hierarki tampilan dan secara umum tidak disarankan

Di bangsal lain itu harus selalu kuat sekarang selama beberapa tampilan kustom kami tidak membuat siklus mempertahankan dengan beberapa tampilan di hierarki tampilan

EDIT:

Beberapa mungkin mengajukan pertanyaan. Apakah menjaganya dengan referensi yang kuat tidak membuat siklus penahan karena pengontrol tampilan root dan tampilan memiliki menyimpan referensi untuk itu? Atau mengapa perubahan itu terjadi? Saya pikir jawabannya di awal pembicaraan ini ketika mereka menggambarkan bagaimana biji dibuat dari xib. Ada pena terpisah yang dibuat untuk VC dan untuk tampilan. Saya pikir ini mungkin alasan mengapa mereka mengubah rekomendasi. Tetap menyenangkan untuk mendapatkan penjelasan yang lebih dalam dari Apple.

Julian Król
sumber
4

Saya pikir informasi yang paling penting adalah: Elemen-elemen di xib secara otomatis dalam tampilan subview. Subview adalah NSArray. NSArray memiliki elemen-elemennya. dll punya petunjuk kuat pada mereka. Jadi dalam kebanyakan kasus Anda tidak ingin membuat pointer kuat lain (IBOutlet)

Dan dengan ARC Anda tidak perlu melakukan apa-apa viewDidUnload

kraag22
sumber