Objective-C'de self = [super init] nil olup olmadığını neden kontrol etmeliyim?


165

Objective-C yazım yöntemleri yazma hakkında genel bir sorum var.

Her yerde (Apple'ın kodu, kitapları, açık kaynak kodu vb.) Başlatma yönteminin başlatma işlemine devam etmeden önce self = [super init] 'in nil olmadığını kontrol etmesi gerektiğini görüyorum.

Bir init yöntemi için varsayılan Apple şablonu:

- (id) init
{
    self = [super init];

    if (self != nil)
    {
        // your code here
    }

    return self;
}

Neden?

Yani init ne zaman sıfır olacak? NSObject üzerinde init çağırdı ve geri döndüm, bir şey gerçekten berbat olmalı, değil mi? Ve bu durumda, bir program bile yazamazsınız ...

Bir sınıfın init yönteminin sıfır döndürmesi gerçekten yaygın mıdır? Öyleyse, hangi durumda ve neden?


1
Wil Shipley bir süre önce bununla ilgili bir makale yayınladı. [self = [aptal init];] ( wilshipley.com/blog/2005/07/self-stupid-init.html ) Yorumları da okuyun, bazı iyi şeyler.
Ryan Townshend

6
Sen sorabilirsiniz Wil Shipley veya Mike Ash veya Matt Gallagher . Her iki durumda da, bu tartışmalı bir konudur. Ama genellikle Apple'ın deyimlerine sadık kalmak iyidir ... sonuçta bu onların Çerçeveleri.
jbrennan

1
Wil, [super init] 'in alıcıyı iade edemeyeceğini bilerek, init sırasında kendini körü körüne yeniden atamadığı için daha fazla dava açıyor gibi görünüyor.
Jasarien

3
Wil, bu yazı ilk olarak yapıldığından beri düşüncelerini değiştirdi.
bbum

5
Bu soruyu bir süre önce görmüştüm ve tekrar buldum. Mükemmel. +1
Dan Rosenstark

Yanıtlar:


53

Örneğin:

[[NSData alloc] initWithContentsOfFile:@"this/path/doesn't/exist/"];
[[NSImage alloc] initWithContentsOfFile:@"unsupportedFormat.sjt"];
[NSImage imageNamed:@"AnImageThatIsntInTheImageCache"];

... ve bunun gibi. (Not: Dosya yoksa NSData bir istisna atabilir). Nil'in geri dönmesinin bir sorun ortaya çıktığında beklenen davranış olduğu birkaç alan vardır ve bu nedenle tutarlılık uğruna nil'i her zaman kontrol etmek standart bir uygulamadır.


10
Evet, ancak bu ilgili sınıfın init yönteminin İÇİNDE DEĞİL. NSData, NSObject'ten miras alır. NSData [super init] 'in sıfır döndürüp döndürmediğini kontrol ediyor mu? Burada sorduğum şey bu. Anlaşılmadıysa özür dilerim ...
Jasarien

9
Eğer bu sınıfları alt sınıflara ayıracak olsaydınız, [super init] 'in sıfır döndürmesi oldukça gerçek bir şans olurdu. Tüm sınıflar NSObject'in doğrudan alt sınıfları değildir. Sıfır döndürmeyeceğine dair hiçbir garanti yoktur. Bu, genellikle teşvik edilen savunma amaçlı bir kodlama uygulamasıdır.
Chuck

7
NSObject init her durumda nil dönebilir şüpheliyim. Bellek yetersizse, ayırma başarısız olur, ancak başarılı olursa, init'in başarısız olabileceğinden şüpheliyim - NSObject'in Sınıf dışında herhangi bir örnek değişkeni bile yoktur. GNUStep'te, sadece "kendini geri getir" olarak uygulanmakta ve Mac'te sökme aynı görünmektedir. Bütün bunlar, elbette, ilgisiz - sadece standart deyimi takip edin ve yapabilecek olup olmayacağı konusunda endişelenmenize gerek kalmayacak.
Peter N Lewis

9
En iyi uygulamaları takip etmemeye cehennem gelmedim. Ancak, neden ilk etapta en iyi uygulama olduklarını bilmek isterim. Bu bir kuleden atlamak gibi bir şey. Nedenini bilmiyorsanız sadece devam edip yapmazsınız. Büyük bir para ödülü ile alta inecek büyük, yumuşak bir yastık var mı? Eğer atlayacağımı bilseydim. Değilse, yapmazdım. Neden takip ettiğimi bilmeden bir uygulamayı körü körüne takip etmek istemiyorum ...
Jasarien

4
Tahsisat sıfır döndürürse, inil her zaman sıfır ile sonuçlanacak olan nil'e gönderilir, bu da kendiliğinden sıfır ile sonuçlanır.
T.

50

Bu özel deyim standarttır çünkü her durumda çalışır.

Nadiren de olsa, ...

[super init];

... farklı bir örnek döndürür, bu yüzden kendine atamayı gerektirir.

Ve nil döndüreceği durumlar olacak, böylece kodunuzun artık var olmayan bir örnek değişken yuvasını başlatmaya çalışmaması için nil kontrolü gerekir.

Sonuç olarak, kullanılacak belgelenmiş doğru kalıp olmasıdır ve eğer kullanmıyorsanız, yanlış yapıyorsunuzdur.


3
Nullabilite belirteçleri ışığında bu hala geçerli mi? Süper sınıfımın başlatıcısı null değilse, yine de kontrol etmek için ekstra dağınıklığa gerçekten değer mi? (Her ne kadar NSObject'in kendisi için bir fikri yok gibi görünüyor -init...)
natevw

[super init]Doğrudan üst sınıf olduğunda nil döndüren bir durum var mı NSObject? Bu "her şeyin bozulduğu" bir durum değil mi?
Dan Rosenstark

1
@DanRosenstark NSObjectDoğrudan üst sınıf değilse . Ancak ... NSObjectdoğrudan üst sınıf olarak ilan etseniz bile , çalışma zamanında bir şey değiştirilmiş olabilir, böylece NSObjectuygulanması initgerçekte adlandırılan şey değildir.
bbum

1
Çok teşekkürler @bbum, bu gerçekten bazı hata düzeltme yönlendirmek yardımcı oldu. Bazı şeyleri dışlamak iyi olur!
Dan Rosenstark

26

Bence, çoğu sınıfta, [super init] 'ten gelen dönüş değeri nil ise ve bunu standart uygulamalar tarafından önerildiği gibi kontrol ederseniz ve nil ise erken dönerseniz, temel olarak uygulamanız hala düzgün çalışmaz. Bunu bile bile, bunu düşünmek durumunda ise (kendini! = Nil) onay sınıfınızın düzgün çalışması için, orada, aslında zamanın% 99.99 do olmayan sıfır olarak gerek kendini. Şimdi, sebebi ne olursa olsun, herhalde [süper init] yaptığı dönüş nil, doğal çağrısı olduğunu varsayar çünkü büyük olasılıkla, zaten başarısız nerede temelde nil karşı çek temelde sınıfın arayana kova yukarı geçiyor başarılı.

Temelde, elde ettiğim şey, zamanın% 99,99'udur, if (self! = Nil) size daha fazla sağlamlık açısından bir şey satın almaz, çünkü parayı davetlinize kadar geçirirsiniz. Bunu gerçekten güçlü bir şekilde ele alabilmek için aslında tüm arama hiyerarşinizde kontroller yapmanız gerekir. Ve o zaman bile, size satın alacağı tek şey, uygulamanızın biraz daha temiz / sağlam bir şekilde başarısız olacağı. Ama yine de başarısız olur.

Eğer bir kütüphane sınıfı keyfi olarak [super init] sonucu nil döndürmeye karar verdiyse, hemen hemen her şeye uyuyorsunuz demektir ve bu daha çok kütüphane sınıfının yazarının uygulama hatası yaptığını gösterir.

Uygulamalar çok daha sınırlı bir bellekte çalıştığında bunun daha eski bir kodlama önerisi olduğunu düşünüyorum.

Ama C seviyesi kodu için, hala bir NULL işaretçisi karşı malloc () dönüş değerini kontrol ediyorum. Oysa, Objective-C için, aksine bir kanıt bulana kadar, genellikle if (self! = Nil) kontrollerini atlayacağım. Neden tutarsızlık?

Çünkü, C ve malloc seviyelerinde, bazı durumlarda aslında kısmen iyileşebilirsiniz. Oysa Objective-C'de, vakaların% 99,99'unda, [super init] sıfır döndürürse, başa çıkmaya çalışsanız bile temelde lanet olasısınız demektir. Sen de sadece uygulamanın çökmesine ve sonrasında ile başa çıkmasına izin verebilir.


6
İyi konuşma. İkinci olarak.
Roger CS Wernersson

3
+1 Tamamen katılıyorum. Sadece küçük bir not: Desenin, tahsinin daha sık başarısız olduğu zamanlardan kaynaklandığına inanmıyorum. Tahsis genellikle init çağrıldığında yapılır. Ayırma başarısız olursa, init çağrılmaz.
Nikolai Ruhe

8

Bu, yukarıdaki yorumların bir özetidir.

Üst sınıfın geri döndüğünü varsayalım nil. Ne olacak?

Sözleşmeleri takip etmezseniz

Kodunuz inityöntemin ortasında çökecek . ( initönemli bir şey olmadıkça )

Sözleşmeleri takip ederseniz, üst sınıfın sıfır dönebileceğini bilmemek (çoğu insan buraya gelir)

Kodunuz muhtemelen bir noktada çökecek, çünkü örneğiniz nilfarklı bir şey beklediğiniz yerde. Ya da programınız çökmeden beklenmedik şekilde davranacaktır. Ah hayatım! Bunu istiyor musun? Bilmiyorum...

Sözleşmeleri izlerseniz, alt sınıfınızın sıfırdan dönmesine izin vererek

Kod belgelerinizde (!) Açıkça belirtilmelidir: "return ... or nil" ve kodunuzun geri kalanının bununla başa çıkması için hazırlanması gerekir. Şimdi bir anlam kazandı.


5
Buradaki ilginç nokta, bence, # 1 seçeneğinin # 2 seçeneğine açıkça tercih edilmesidir. Alt sınıfınızın init'in sıfır döndürmesini isteyebileceğiniz gerçekten durumlar varsa, # 3 tercih edilmelidir. Bunun tek nedeni kodunuzdaki bir hatadan kaynaklanıyorsa, # 1 kullanın. # 2 seçeneğini kullanmak, uygulamanızın daha sonraki bir zamana kadar patlamasını geciktirir ve böylece hatayı çok daha zor bir şekilde ayıklamak için geldiğinizde işinizi yapar. Sessizce istisnaları yakalamak ve onları ele almadan devam etmek gibi.
Mark Amery

Veya
hızlıca

7

Tipik olarak, sınıfınız doğrudan kaynağından NSObjectgeliyorsa, buna gerek yoktur. Bununla birlikte, sınıfınız diğer sınıflardan geliyormuş gibi, başlatıcıları geri dönebilir nilve eğer öyleyse, başlatıcısı bunu yakalayabilir ve doğru davranabilir.

Ve evet, kayıt için, en iyi uygulamayı takip ediyorum ve tüm sınıflarıma, doğrudan gelenlerden bile yazıyorum NSObject.


1
Bunu göz önünde bulundurarak, bir değişkeni başlattıktan sonra ve fonksiyonları çağırmadan önce nil'i kontrol etmek iyi bir uygulama olur mu? ör.Foo *bar = [[Foo alloc] init]; if (bar) {[bar doStuff];}
dev_does_software

Devralma , GNUstep'in eski sürümleri gibi egzotik çalışma zamanlarını sayıyorsa (bu da geri döner ), bunun ne olursa olsun, bir kontrol ve bir atama da size vereceğini NSObjectgaranti etmez . -initNSObjectGSObject
Maxthon Chan

3

Haklısın, sık sık yazabilirsin [super init], ama bu sadece hiçbir şeyin alt sınıfı için işe yaramazdı. İnsanlar sadece bazen gerekli olduğunda bile sadece zaman bir kod standart çizgisini ezberlemek ve hepsini kullanmayı tercih ve böylece biz standart olsun if (self = [super init])iade edilen nil olasılığını ve dışındaki bir nesnenin olasılığını hem alır, selfiade edilen hesaba katın.


3

Yaygın bir hata yazmaktır

self = [[super alloc] init];

bu, bir üst sınıf yapıcısında / initinde olmasını istediğiniz DEĞİL üst sınıfın bir örneğini döndürür. Alt sınıf yöntemlerine yanıt vermeyen, kafa karıştırıcı olabilecek bir nesne geri alırsınız ve bulunmayan yöntemlere veya tanımlayıcılara yanıt vermemeyle ilgili kafa karıştırıcı hatalar vb.

self = [super init]; 

süper sınıfın alt sınıfların üyelerini ayarlamadan önce ilk olarak başlatılacak üyeleri (değişkenler veya diğer nesneler) varsa gereklidir . Aksi takdirde, objc çalışma zamanı hepsini 0 veya sıfır olarak başlatır . ( bellek parçalarını genellikle temizlemeden ayıran ANSI C'nin aksine )

Ve evet, temel sınıf başlatma, bellek yetersiz hataları, eksik bileşenler, kaynak edinme hataları vb. Nedeniyle başarısız olabilir, bu nedenle nil kontrolü akıllıca olur ve birkaç milisaniyeden daha az sürer.


2

Bu, intialazation çalıştığını kontrol etmek için, init yöntemi nil döndürmediyse if ifadesi true değerini döndürür, bu nedenle nesnenin oluşturulmasını kontrol etmenin bir yolu doğru çalıştı. Bu init başarısız olabilir düşünmek için birkaç neden belki onun süper sınıf bilmiyor ya da bir tür bir şey geçersiz kılınmış bir init yöntemi, olsa da bu yaygın olduğunu düşünmek olmaz. Ama eğer olursa, her zaman kontrol edilen bir çarpışma olduğunu tahmin etmek daha iyi bir şey değildir ...


ama htey birlikte çağrılırsa, tahsis edilen dosyalar ne olur?
Daniel

Tahsis başarısız olursa, init init olarak adlandırdığınız sınıfın bir örneği yerine nil'e gönderilecektir. Bu durumda, hiçbir şey olmaz ve [super init] 'in sıfır döndürüp döndürmediğini test etmek için hiçbir kod yürütülmez.
Jasarien

2
Bellek ayırma her zaman + ayırmada yapılmaz. Sınıf kümelerini ele alalım; bir NSString, başlatıcı çağrılıncaya kadar hangi alt sınıfın kullanılacağını bilmez.
bbum

1

OS X'te, -[NSObject init]bellek nedenlerinden dolayı başarısız olması muhtemel değildir . Aynı şey iOS için söylenemez.

Ayrıca, nilherhangi bir nedenle geri dönebilecek bir sınıfı alt sınıfa yazarken yazmak iyi bir uygulamadır .


2
İOS ve Mac OS üzerinde -[NSObject init]olduğunu çok herhangi bellek değil olarak hafıza nedenlerden dolayı arıza yapması muhtemel.
Nikolai Ruhe

Sanırım
tahsis
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.