2016-09-11 34 views
14

Swift 2.2'de, @noescape ile çıkmayan olarak işaretlenen kapakların açık bir self gerektirmediğini fark ettim. Swift 3'te, tüm kapanışlar varsayılan olarak kaçmıyor ve şimdi bunların çıkmasını istiyorsanız @escaping ile işaretlenmelerini gerektiriyor.Neden Swift 3'te varsayılan olarak kaçmıyorsa kapatmalar açık bir `self` gerektirir?

Varsayılan olarak Swift 3'teki tüm kapakların kaçmadığı göz önüne alındığında, neden açık bir self gerektiriyor?

final class SomeViewController: NSViewController { 

    var someClosure:() ->() = { _ in } 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     someClosure = { 
      view.layer = CALayer() // ERROR: Implicit use of `self` in closure; use `self.` to make capture semantics explicit 
     } 
    } 
} 

cevap

9

Swift 3, tüm kapanışları

Hayır, Swift 3'te, yalnızca kapatma işlev argümanları (kendileri fonksiyonlardır yani fonksiyon girişler) tarafından sigara kaçan olan varsayılan olarak sigara kaçan vardır varsayılan (SE-0103 uyarınca). Örneğin:

class A { 

    let n = 5 
    var bar :() -> Void = {} 

    func foo(_ closure:() -> Void) { 
     bar = closure // As closure is non-escaping, it is illegal to store it. 
    } 

    func baz() { 
     foo { 
      // no explict 'self.' required in order to capture n, 
      // as foo's closure argument is non-escaping, 
      // therefore n is guaranteed to only be captured for the lifetime of foo(_:) 
      print(n) 
     } 
    } 
} 

yukarıdaki örnekte closure olmayan çıkış, hafızaya alınabilir veya bu şekilde fonksiyon foo(_:) ömrü ile ömrünü sınırlayan, yakalanan yasaklanmış edilir. Bu nedenle, yakaladığı herhangi bir değerin, işlev çıktıktan sonra yakalanmayacağı garanti edilir - bu, döngü tutma gibi yakalama ile oluşabilecek sorunlardan endişelenmeniz gerekmediği anlamına gelir. Ancak

, bir kapak depolanmış özelliği (yukarıdaki örnekte olduğu gibi bar) tanımı ömrü gibi (o @noescape ile işaretlemek için anlamsız olacaktır) kaçan tarafından olmayan belirli bir işleve sınırlı - o (ve dolayısıyla yakalanan tüm değişkenler), verilen örnek bellekte kaldığı sürece bellekte kalır. Bu nedenle döngüleri tutma gibi problemlere kolayca yol açabilir, bu nedenle, yakalama semantiklerini açık hale getirmek için açık bir self. kullanmanız gerekir. someClosure kuvvetle self yakalar olarak

noktada Aslında, durumda, örnek kod, çağrılan viewDidLoad() üzerine bir muhafaza döngüsü yaratacak ve bir depolanmış özellik olduğu gibi self şiddetle someClosure başvurur. Tabii


, sen @noescape özellik kullanmak edebilmek için beklenebilir bir yerde bir işlevde yerel değişken olan bir kapatma üzerindedir. Böyle bir kapanış, işlevin dışında saklanmadığı veya yakalanmadığı sürece öngörülebilir bir ömre sahip olacaktır. Örneğin: @noescape Swift 3'te kaldırılıyor olarak

class A { 

    let n = 5 

    func foo() { 

     let f : @noescape() -> Void = { 
      print(n) 
     } 

     f() 
    } 
} 

yazık ki, bu (İlginç olan Xcode 8 GM, bu mümkün, ama bir kullanımdan kaldırılması uyarı vermesidir) mümkün olmayacaktır. Jon Shier says olarak, dilin yeniden eklenmesini beklememiz gerekecek.

+0

Açıklama için teşekkür ederiz! Sadece, kontrolörün kendisinin tahsis edildiği zaman, kapatmanın ve görünümün de tahsis edileceği veya bir bellek sızıntısına neden olabileceği açıklığa kavuşturmak için mi? – beingadrian

+0

@beingadrian 'SomeClosure 'özelliğinize' self '(örneğin viewDidLoad'daki örneğinizde olduğu gibi) yakalayan bir kapak atandığında, gerçekten bir kurtarma döngüsü oluşturacaktır ve bu nedenle başka bir güçlü olmadığında sızacaktır görünüm denetleyicinize yapılan başvurular (bu görünümü, görüntü denetleyicinizin sınıfında bir 'print' ifadesiyle 'deinit' uygulayarak kendiniz doğrulayabilirsiniz). En basit çözüm, “ben” i “zayıf” olarak ele almaktır (kapanmayı “{[zayıf benlik” in ...}) olarak tanımlar. Daha sonra kapağın içindeki isteğe bağlı zincirlemeyi, görünümün katmanını atayabilmek için kullanabilirsiniz, 'self? .view.layer = CALayer()' – Hamish

+0

@beingadrian Başka bir potansiyel çözüm, 'kendini' kapamada 'unowned 'olarak yakalamaktır. Görünüm denetleyici örneği ayrıldıktan sonra kapatma işlemi başlatılırsa çökme olasılığı yüksek olabilir. Bu nedenle, sadece 'someClosure' bir 'private' özelliği ise bunu yapmamızı tavsiye ederim (o zaman başka bir sınıfın referans almasına imkan yok). – Hamish

4

Depolanmış kapanışlar, gerçekten olmadıkları zaman bile, varsayılan olarak kaçıyor olarak kabul edilir. Kaçma yapmadıklarını işaretlemenin bir yolu yok, bu yüzden @noescape'u ekleyebilecekleri veya yapamayacakları dile geri dönene kadar bu şekilde sıkıştık. Hızlı-evrim posta listesinde this discussion'a bakın.

+0

Bu ilgi çekici. Umarım en azından bir güncellemede daha net hale getirirler.Cevap için teşekkürler! – beingadrian