Swift'te her zaman [sahipsiz benliği] kullanalım mı


467

WWDC 2014 oturum 403 Orta Swift ve konuşma metninde aşağıdaki slayt vardı

resim açıklamasını buraya girin

Konuşmacı bu durumda, eğer [unowned self]orada kullanmazsak , bir bellek sızıntısı olacağını söyledi. Bu her zaman [unowned self]iç kapakta kullanmamız gerektiği anlamına mı geliyor ?

On Swift Hava app ViewController.swift hattı 64 , ben kullanmıyorum [unowned self]. Ama ve @IBOutletgibi bazılarını kullanarak kullanıcı arayüzünü güncelliyoruz . TAMAM olabilir çünkü tanımladığım tüm s . Ancak güvenlik için daima kullanmalı mıyız ?self.temperatureself.loadingIndicator@IBOutletweak[unowned self]

class TempNotifier {
  var onChange: (Int) -> Void = {_ in }
  var currentTemp = 72
  init() {
    onChange = { [unowned self] temp in
      self.currentTemp = temp
    }
  }
}

resim bağlantısı kopuk
Daniel Gomez Rico

@ DanielG.R. Teşekkürler, görebiliyorum. i.stack.imgur.com/Jd9Co.png
Jake Lin

2
Yanılmıyorsam, slaytta verilen örnek yanlıştır - bir ortak (dahili olarak, ancak yine de) bir özellik onChangeolduğundan bir [weak self]kapatma olmalıdır , bu nedenle başka bir nesne, TempNotifier nesnesini etrafında tutarak (süresiz olarak kullanan nesne , kendi zayıf referansı ile gittiğini onChangegörene kadar kapatmayı bırakmadı )TempNotifierTempNotifier . Eğer var onChange …vardı private var onChange …o zaman [unowned self]doğru olacaktır. Buna rağmen% 100 emin değilim; biri beni düzeltirse lütfen yanılıyorsam.
Slipp D. Thompson

@Jake Lin `var onChange: (Int) -> Void = {}` kıvırcık ayraçlar boş bir kapanış mı gösteriyor? ile boş bir dizi tanımlarken olduğu gibi []? Açıklamayı Apple belgelerinde bulamıyorum.
bibscy

@bibscy evet, {}boş kapatma (kapatma örneği) varsayılan olarak (hiçbir şey yapmaz), (Int) -> Voidkapatma tanımıdır.
Jake Lin

Yanıtlar:


871

Hayır, kesinlikle kullanmak istemeyeceğiniz zamanlar vardır [unowned self]. Bazen, kapağın çağrıldığı zaman hala etrafta olduğundan emin olmak için kapağın kendini yakalamasını istersiniz.

Örnek: Zaman uyumsuz bir ağ isteği yapma

Eğer yapıyorsanız bir zaman uyumsuz ağ isteği yapmak kapatma korumak istiyorsanız selfeğer istek bittikten için. Bu nesne başka şekilde yeniden yerleştirilmiş olabilir, ancak yine de isteğin sonlandırılmasıyla ilgilenebilirsiniz.

Ne zaman kullanılır unowned selfveyaweak self

Gerçekten kullanmak istediğiniz tek zaman [unowned self] veya güçlü bir referans döngüsü[weak self] oluşturacağınız . Güçlü bir referans döngüsü, nesnelerin birbirine sahip oldukları (belki de üçüncü bir taraf aracılığıyla) bir sahiplik döngüsü olduğunda ve bu nedenle her ikisi de birbirlerinin birbirine yapışmasını sağladığından asla yer değiştirmeyeceklerdir.

Belirli bir kapatma durumunda, bunun içinde atıfta bulunulan herhangi bir değişkenin kapanmaya "sahip" olduğunu fark etmeniz yeterlidir. Kapatma etrafta olduğu sürece, bu nesnelerin olduğu garanti edilir. Bu sahipliği durdurmanın tek yolu, [unowned self]ya da yapmaktır [weak self]. Eğer bir sınıfın bir kapanışı varsa ve bu kapanış o sınıfa güçlü bir referans yakalarsa, kapanış ve sınıf arasında güçlü bir referans döngüsü vardır. Bu, sınıfın kapanışa sahip olan bir şeye sahip olup olmadığını da içerir.

Özellikle videodaki örnekte

Slayttaki örnekte TempNotifier, onChangeüye değişkeni aracılığıyla kapatmaya sahiptir . Onlar beyan olmadıysa selfolarak unowned, kapatma kendi de olurself kuvvetli referans döngüsü yaratır.

Arasındaki fark unowned veweak

Arasındaki fark unownedve weakolmasıdır weakİsteğe Bağlı iken olarak ilan edilir unowneddeğil. Bunu ilan ederek weak, bir noktada kapağın içinde sıfır olabileceği davasını ele alabilirsiniz. Sıfır olan bir unowneddeğişkene erişmeye çalışırsanız , tüm program çökecektir. Bu nedenle, yalnızca unowned, kapatma kapalıyken değişkenin her zaman etrafta olacağından eminseniz kullanın


1
Selam. Mükemmel cevap. Sahipsiz benliği anlamak için uğraşıyorum. Zayıf kullanmanın bir nedeni, sadece 'benlik isteğe bağlı' olmak benim için yeterli değil. Neden özellikle 'sahipsiz benliği' kullanmak istiyorum stackoverflow.com/questions/32936264/…

19
@robdashnash, Sahipsiz benliği kullanmanın avantajı, tasarımdan emin olduğunuzda gereksiz kod olabilecek bir isteğe bağlı kodu açmanız gerekmemesidir, asla sıfır olmayacaktır. Nihayetinde, sahipsiz benlik kısalık için ve belki de gelecekteki geliştiricilere asla sıfır bir değer beklemediğinizin bir ipucu olarak kullanılır.
drewag

77
[weak self]Eşzamansız bir ağ isteğinde kullanmak için bir durum , bu isteğin görünümü doldurmak için kullanıldığı bir görünüm denetleyicisidir . Kullanıcı geri çekilirse, artık görünümü doldurmamıza veya görünüm denetleyicisine başvurmamıza gerek yoktur.
David James

1
weakreferanslar nil, nesne yeniden yerleştirildiğinde de ayarlanır . unownedreferanslar değildir.
BergQuester

1
Biraz kafam karıştı. unownediçin kullanılır non-Optionaliken weakkullanılır Optionalbizim selfiçin Optionalveya non-optional?
Muhammed Nayab

193

Güncelleme 11/2016

Bu cevabı uzatan bir makale yazdım (ARC'nin ne yaptığını anlamak için SIL'e bakıyorum), buradan kontrol edin .

Orijinal cevap

Önceki cevaplar, bir diğerinin ne zaman ve neden kullanılacağına dair basit kurallar vermiyor, bu yüzden birkaç şey eklememe izin verin.

Sahipsiz veya zayıf tartışma , değişkenin yaşam süresi ve ona referans veren kapanış sorunuyla ilgilidir .

hızlı zayıf vs sahipsiz

Senaryolar

İki olası senaryo olabilir:

  1. Kapak değişkeni ile aynı kullanım ömrüne sahiptir, bu nedenle kapak yalnızca değişken erişilinceye kadar erişilebilir olacaktır . Değişken ve kapanış aynı kullanım ömrüne sahiptir. Bu durumda referansı sahipsiz olarak bildirmeniz gerekir . Yaygın bir örnek, [unowned self]ebeveynleri bağlamında bir şeyler yapan ve başka bir yere referans verilmeyen ebeveynlerin çoğunu geçmeyen küçük kapaklar örneğinde kullanılır.

  2. Kapanma ömrü, değişkenin birinden bağımsızdır, değişkene artık erişilemediğinde, kapatmaya yine de başvurulabilir. Bu durumda referansı zayıf olarak ilan etmeli ve kullanmadan önce nil olmadığını doğrulamalısınız (çözmeyi zorlamayın). Bunun genel bir örneği, [weak delegate]tamamen ilişkisiz (ömür boyu) bir temsilci nesnesine gönderme yapan bazı kapatma örneklerinde görebilirsiniz.

Gerçek Kullanım

Peki, çoğu zaman hangisini kullanmalısınız / kullanmalısınız?

Joe Groff'dan Twitter'dan Alıntı :

Sahipsiz daha hızlıdır ve değişmezliğe ve isteğe bağlı olmaya izin verir.

Zayıfa ihtiyacınız yoksa kullanmayın.

Burada bilinmeyen *iç işler hakkında daha fazla bilgi bulacaksınız .

* Genellikle, bilinmeyen referansa erişmeden önce çalışma zamanı denetimlerinin (geçersiz başvurular için kilitlenmeye neden olan) yapıldığını belirtmek için sahipsiz (güvenli) olarak da adlandırılır.


26
Papağan açıklama duymaktan bıktım "kendini sıfır olabilir hafta kullanın, hiç sıfır olamaz zaman sahipsiz kullanın". Tamam, anladık - milyon kez duydum! Bu cevap, aslında OP'nin sorusuna doğrudan cevap veren düz ingilizce'de kendiliğinden ne zaman boş olabileceğine dair daha derinlere iner. Bu harika açıklama için teşekkürler !!
TruMan1

Teşekkürler @ TruMan1, aslında yakında blogumda sona erecek bu konuda bir yazı yazıyorum, cevap bir bağlantı ile güncelleyecektir.
Umberto Raimondi

1
Güzel cevap, çok pratik. Performansa duyarlı zayıf varyasyonlarımdan bazılarını bilinmeyen hale getirmekten ilham aldım.
original_username

"Kapanma ömrü değişkeninkinden bağımsız" Burada bir yazım hatası var mı?
Bal

1
Bir kapatma her zaman üst nesneyle aynı ömre sahipse, nesne yok edildiğinde referans sayısı yine de dikkate alınmaz mı? Neden bu durumda sahipsiz veya zayıfla uğraşmak yerine 'kendini' kullanmıyorsunuz?
LegendLength

105

Bir görünüm denetleyicisi için bazı somut örnekler ekleyeceğimi düşündüm. Sadece Stack Overflow ile ilgili değil, açıklamaların çoğu gerçekten iyi, ama gerçek dünya örnekleriyle daha iyi çalışıyorum (@drewag bunun üzerinde iyi bir başlangıç ​​yaptı):

  • Bir ağdan yanıt işlemek için bir kapatma varsa weak, uzun ömürlü oldukları için kullanın . Görünüm denetleyicisi istek tamamlanmadan selfkapanabilir, böylece kapatma çağrıldığında artık geçerli bir nesneyi göstermez.
  • Düğmedeki bir olayı işleyen kapanışınız varsa. Bunun unownednedeni, görünüm denetleyicisi yok olur olmaz, düğme ve başvurduğu diğer tüm öğeler selfaynı anda kaybolur. Kapatma bloğu da aynı anda kaybolacaktır.

    class MyViewController: UIViewController {
          @IBOutlet weak var myButton: UIButton!
          let networkManager = NetworkManager()
          let buttonPressClosure: () -> Void // closure must be held in this class. 
    
          override func viewDidLoad() {
              // use unowned here
              buttonPressClosure = { [unowned self] in
                  self.changeDisplayViewMode() // won't happen after vc closes. 
              }
              // use weak here
              networkManager.fetch(query: query) { [weak self] (results, error) in
                  self?.updateUI() // could be called any time after vc closes
              }
          }
          @IBAction func buttonPress(self: Any) {
             buttonPressClosure()
          }
    
          // rest of class below.
     }

17
Bunun daha fazla oy alması gerekiyor. Bir düğmeye basmanın kapanmasının görünüm denetleyicisinin ömrü dışında nasıl var olmayacağını gösteren iki katı örnek vardır ve bu nedenle sahipsiz kullanılabilir, ancak UI'yi güncelleyen çoğu ağ çağrısının zayıf olması gerekir.
Tim Fuqua

2
Sadece açıklığa kavuşturmak için, kendini bir kapatma bloğunda çağırırken daima sahipsiz veya zayıf mı kullanıyoruz? Yoksa zayıf / sahipsiz olarak adlandırmayacağımız bir zaman var mı? Eğer öyleyse, buna da bir örnek verebilir misiniz?
luke

Çok teşekkür ederim.
Shawn Baek

1
Bu bana [zayıf benlik] ve [sahipsiz benlik] hakkında daha derin bir anlayış kazandırdı. Çok teşekkürler @possen!
Tommy

Bu harika. kullanıcı etkileşimini temel alan, ancak tamamlanması biraz zaman alan bir animasyonum varsa . Ve sonra kullanıcı başka bir viewController taşır. Sanırım bu durumda hala weakdeğil de kullanmalıyım unowned?
Tatlım


50

İşte Apple Geliştirici Forumlarından parlak alıntılar lezzetli ayrıntıları açıkladı:

unownedvs unowned(safe)vsunowned(unsafe)

unowned(safe), erişimde nesnenin hala canlı olduğunu iddia eden, sahip olmayan bir referanstır. Bu, x!her erişildiğinde örtük olarak açılmamış zayıf bir isteğe bağlı referans gibi . unowned(unsafe)__unsafe_unretainedARC'deki gibidir — bu sahip olmayan bir referanstır, ancak nesnenin erişimde hala hayatta olup olmadığını çalışma zamanı kontrolü yoktur, bu nedenle sarkan referanslar çöp belleğine ulaşır. unownedeşanlamlı her zaman unowned(safe)şu anda, ama niyet o şekilde optimize edilecek olmasıdır unowned(unsafe)içinde -Ofast çalışma zamanı denetler devre dışı bırakıldığında oluşturur.

unowned vs weak

unownedaslında çok daha basit bir uygulama kullanır weak. Yerel Swift nesneleri iki referans sayısı taşır ve unowned referanslar , güçlü referans sayısı yerine sahipsiz referans sayısını çarpar . Nesne, güçlü referans sayısı sıfıra ulaştığında başlatılır, ancak gerçekte sahipsiz referans sayısı da sıfıra . Bu, bilinmeyen referanslar olduğunda hafızanın biraz daha uzun süre tutulmasına neden olur, ancak bu genellikleunowned kullanılanakadar yeniden konumlandırılmaz , çünkü ilgili nesneler zaten neredeyse eşit ömürlere sahip olmalı ve yandan çok daha basit ve daha düşük zayıf referansları sıfırlamak için kullanılan tablo tabanlı uygulama.

Güncelleme: Modern Swift yılında weakolduğu gibi dahili olarak aynı mekanizmayı kullanır unownedyapar . Bu nedenle, Objective-C'yi weakSwift ile karşılaştırdığından bu karşılaştırma yanlıştır unonwed.

Nedenleri

Referanslara sahip olduktan sonra hafızayı canlı tutmanın amacı nedir? Kod, başlatıldıktan sonra nesneye sahip olmayan bir başvuru kullanarak nesne ile bir şeyler yapmaya çalışırsa ne olur?

Hafıza canlı tutulur, böylece tutma sayıları hala kullanılabilir. Bu şekilde, bir kişi sahipsiz nesneye güçlü bir referans tutmaya çalıştığında, çalışma zamanı nesneyi korumanın güvenli olduğundan emin olmak için güçlü referans sayısının sıfırdan büyük olduğunu kontrol edebilir.

Nesne tarafından tutulan veya sahip olunmayan referanslara ne olur? Başlatıldığında ömürleri nesneden ayrılıyor mu yoksa son bilinmeyen referans serbest bırakıldıktan sonra nesne yeniden yerleştirilene kadar bellekleri de korunuyor mu?

Nesneye ait tüm kaynaklar, nesnenin son güçlü başvurusu serbest bırakılır ve onun deinit'i çalıştırılır çalıştırılmaz serbest bırakılır. Sahipsiz referanslar sadece hafızayı canlı tutar - referans sayıları olan başlıktan başka, içeriği önemsizdir.

Heyecanlı, ha?


38

Burada harika cevaplar var. Ancak Swift'in zayıf referansları uygulama şeklindeki son değişiklikler, herkesin zayıf benliğe karşı sahipsiz kişisel kullanım kararlarını değiştirmelidir. Daha önce, sahipsiz benliği kullanarak en iyi performansa ihtiyacınız varsa, benliğin asla sıfır olmayacağından emin olabildiğiniz sürece, zayıf benliğe göre daha üstündü, çünkü sahipsiz benliğe erişmek zayıf benliğe erişmekten çok daha hızlıdır.

Ancak Mike Ash, Swift'in yan tabloları kullanmak için zayıf değişkenlerin uygulamasını nasıl güncellediğini ve bunun zayıf kişisel performansı nasıl önemli ölçüde iyileştirdiğini belgeledi.

https://mikeash.com/pyblog/friday-qa-2017-09-22-swift-4-weak-references.html

Zayıf benlik için önemli bir performans cezası olmadığına göre, ileriye doğru kullanmayı varsayılan olarak kullanmamız gerektiğine inanıyorum. Zayıf benliğin yararı, isteğe bağlı olmasıdır, bu da daha doğru kod yazmayı çok daha kolay hale getirir, temelde Swift'in bu kadar büyük bir dil olmasının nedeni budur. Sahipsiz benliğin kullanımı için hangi durumların güvenli olduğunu bildiğinizi düşünebilirsiniz, ancak diğer birçok geliştirici kodunu gözden geçirme deneyimim çoğu değil. Ben genellikle bir denetleyici dağıtıldıktan sonra bir arka plan iş parçacığının tamamlandığı durumlarda, sahipsiz kendiliğinden ayrılmış olduğu çökmeler çok sabit.

Hatalar ve çökmeler, programlamanın en zaman alıcı, ağrılı ve pahalı parçalarıdır. Doğru kodu yazmak ve bunlardan kaçınmak için elinizden geleni yapın. Ben asla bir seçenek açmaya zorlamak ve zayıf benlik yerine asla sahipsiz benlik kullanmak için bir kural yapmak öneririz. Kaydırmaya zorlanma ve sahipsiz benlik aslında güvenlidir. Ancak çökmeleri ve hataları bulmak ve hata ayıklamak zor ortadan kaldırmaktan çok kazanacaksınız.


Son paragraftaki güncelleme ve Amen için teşekkür ederiz.
sloganı

1
Peki yeni değişikliklerden sonra Bir yerde weakkullanılamayacak bir zaman var mı unowned?
Tatlım

4

Göre Apple-doc

  • Zayıf başvurular her zaman isteğe bağlı türdedir ve başvurdukları örnek dağıtıldığında otomatik olarak sıfır olur.

  • Yakalanan referans asla sıfır olmazsa, zayıf bir referanstan ziyade her zaman bilinmeyen bir referans olarak yakalanmalıdır.

Misal -

    // if my response can nil use  [weak self]
      resource.request().onComplete { [weak self] response in
      guard let strongSelf = self else {
        return
      }
      let model = strongSelf.updateModel(response)
      strongSelf.updateUI(model)
     }

    // Only use [unowned self] unowned if guarantees that response never nil  
      resource.request().onComplete { [unowned self] response in
      let model = self.updateModel(response)
      self.updateUI(model)
     }

0

Yukarıdakilerin hiçbiri mantıklı değilse:

tl; Dr.

Tıpkı bir gibi , referansın kullanım noktasında sıfır olmayacağını garantiimplicitly unwrapped optional ediyorsanız , sahipsiz kullanın. Değilse, zayıf kullanmalısınız.

Açıklama:

Aşağıdakileri aldım: zayıf sahipsiz bağlantı . Topladığımdan, sahipsiz ben sıfır olamaz ama zayıf ben olabilir ve sahiplenmemiş ben, Objective-C'de rezil bir şey olan sarkan işaretlere yol açabilir. Umarım yardımcı olur

"UNOWNED Zayıf ve bilinmeyen referanslar benzer şekilde davranır, ancak aynı DEĞİLDİR."

Zayıf başvurular gibi bilinmeyen başvurular, atıfta bulunulan nesnenin alıkonma sayısını artırmaz . Bununla birlikte, Swift'te, sahipsiz bir referansın İsteğe Bağlı olmama avantajı vardır . Bu, isteğe bağlı ciltleme kullanmak yerine onları yönetmeyi kolaylaştırır . Bu, Örtülü Olarak Açılmamış Seçeneklerden farklı değildir. Ayrıca, bilinmeyen başvurular sıfırlanmaz . Bu, nesne yeniden yerleştirildiğinde işaretçiyi sıfırlamadığı anlamına gelir. Bu, bilinmeyen referansların kullanımının bazı durumlarda sarkan işaretlere yol açabileceği anlamına gelir.. Benim gibi Objective-C günlerini hatırlayan nerds sizin için, sahipsiz referanslar güvensiz_güvenilmez referanslar eşler.

Burası biraz kafa karıştırıcı oluyor.

Zayıf ve bilinmeyen referansların her ikisi de alıkonma sayısını artırmaz.

Her ikisi de tutma döngülerini kırmak için kullanılabilir. Peki onları ne zaman kullanıyoruz ?!

Göre Apple'ın docs :

“ Bu başvurunun ömrü boyunca bir noktada sıfır olması geçerli olduğunda zayıf bir referans kullanın . Bunun tersine, başlatma sırasında bir kez ayarlandığında referansın hiçbir zaman sıfır olmayacağını bildiğinizde bilinmeyen bir referans kullanın. ”


0
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let controller = storyboard.instantiateViewController(withIdentifier: "AnotherViewController")
        self.navigationController?.pushViewController(controller, animated: true)

    }

}



import UIKit
class AnotherViewController: UIViewController {

    var name : String!

    deinit {
        print("Deint AnotherViewController")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        print(CFGetRetainCount(self))

        /*
            When you test please comment out or vice versa

         */

//        // Should not use unowned here. Because unowned is used where not deallocated. or gurranted object alive. If you immediate click back button app will crash here. Though there will no retain cycles
//        clouser(string: "") { [unowned self] (boolValue)  in
//            self.name = "some"
//        }
//


//
//        // There will be a retain cycle. because viewcontroller has a strong refference to this clouser and as well as clouser (self.name) has a strong refferennce to the viewcontroller. Deint AnotherViewController will not print
//        clouser(string: "") { (boolValue)  in
//            self.name = "some"
//        }
//
//


//        // no retain cycle here. because viewcontroller has a strong refference to this clouser. But clouser (self.name) has a weak refferennce to the viewcontroller. Deint AnotherViewController will  print. As we forcefully made viewcontroller weak so its now optional type. migh be nil. and we added a ? (self?)
//
//        clouser(string: "") { [weak self] (boolValue)  in
//            self?.name = "some"
//        }


        // no retain cycle here. because viewcontroller has a strong refference to this clouser. But clouser nos refference to the viewcontroller. Deint AnotherViewController will  print. As we forcefully made viewcontroller weak so its now optional type. migh be nil. and we added a ? (self?)

        clouser(string: "") {  (boolValue)  in
            print("some")
            print(CFGetRetainCount(self))

        }

    }


    func clouser(string: String, completion: @escaping (Bool) -> ()) {
        // some heavy task
        DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
            completion(true)
        }

    }

}

Eğer emin değilseniz [unowned self] o zaman kullanın [weak self]

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.