2016-03-28 19 views
2

UIViewController, özelliklerinden birinde bir uyumsuzluk işlevini çağıran bir UIViewController var. Geri arama için, bir kapatma yerine doğru parametre türlerine sahip bir işlev sunmak istiyorum.Curried işlevinde `zayıf 'kendini nasıl belirleyebilirim

class Fetcher { 
    func fetch(completion: ([String] ->())) { 
     // ... do stuff 
     completion([ ... ]) 
    } 
} 

class ViewController: UIViewController { 
    let fetcher = Fetcher() 

    func fetch() { 
     fetcher.fetch(didFetch) 
    } 

    func didFetch(result: [String]) { 
     // handle result 
    } 
} 

Ben yapsaydı sabit olurdu iki nesne arasında bir muhafaza döngüsü vardır hariç her şey gayet iyi çalışıyor:

fetcher.fetch() { [weak self] in 
    // handle result 
} 

içinde muhafaza döngüsünü iptal etmek için bir yol var mı ilk kurulum? Fetcher.fetch döngüsü ne olacak muhafaza (örn için, kuvvetle kapatma muhafaza edebilir) nasıl belirtmez, ama o kadar kalmadan, amacım buydu:


DÜZENLEME

şey kaçırdım @noescape olarak işaretlenmelidir. Özür!

+1

Belki size başlığında referans için "curried işlev" ler kaldırabilirsiniz soru? – milos

cevap

3

Neden bir tutma döngüsü olduğunu düşünüyorsunuz? Fetcher, completion başvurusunda geçirilen iletiyi saklamıyor. Eğer çalıştırırsanız bir oyun alanında aşağıdaki:

fetching 
did fetch 
deinit ViewController 
deinit Fetcher 

Ancak nedense referans olarak saklamak için senin yerinde: olması gerektiği gibi

import UIKit 
import XCPlayground 
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true 

class Fetcher { 

    func fetch(completion: [String] ->()) { 
     dispatch_async(dispatch_get_main_queue()) { 
      print("fetching") 
      completion([]) 
     } 
    } 

    deinit { print("deinit Fetcher") } 
} 

class ViewController: UIViewController { 

    let fetcher = Fetcher() 

    func fetch() { 
     fetcher.fetch(didFetch) 
    } 

    func didFetch(result: [String]) { 
     print("did fetch") 
    } 

    deinit { print("deinit ViewController") } 
} 

var vc: ViewController? = ViewController() 
vc?.fetch() 
vc = nil 

... her şeyi baskılar göreceksiniz

class Fetcher { 

    var completion: ([String] ->())? 

    func fetch(completion: [String] ->()) { 
     self.completion = completion // causing retain cycle 
     completion([]) 
    } 
} 

Sonra gerçekten bir döngü tutmak zorunda kalacak ve suçlu iki nesnenin would never deinit ...

Edit

Bu soru benden büyüyor. Özellikle, @noescape'un zaman uyumsuz yürütmeye geçtiğimizden bu yana uygulamak imkansız olduğu için, böyle bir ideale ne kadar yaklaşabiliriz? Aklıma gelen iki yaklaşım vardır; ne yazık ki, ne yazık ki, derhal, asıl sorunun ruhu içinde bir kapamada geçmeyi gerektirmedikleri halde, derhal, derleyiciye destekli garantiler sunmaktadır:

protocol CompletionHandler : AnyObject { // only needed for `fetch2` 
    associatedtype Result 
    func onCompletion(_: Result) 
} 

extension Fetcher { 

    func fetch2 <H: CompletionHandler where H.Result == [String]> (handler: H) { 
     dispatch_async(dispatch_get_main_queue()) { [weak handler] in 
      print("fetching2") 
      handler?.onCompletion([]) 
     } 
    } 

    func fetch3 <T: AnyObject> (handler: T, curry: T -> [String] ->()) { 
     dispatch_async(dispatch_get_main_queue()) { [weak handler] in 
      print("fetching3") 
      if let handler = handler { 
       curry(handler)([]) 
      } 
     } 
    } 
} 
böylece gibi kullanılabilir

... (fetch2CompletionHandler uygunluğunu kabul olduğu):

fetcher.fetch2(self) 
fetcher.fetch3(self, curry: ViewController.onCompletion) 

... etkisiyle:

var vc: ViewController? = ViewController() 
vc?.fetch() 
vc = nil 

... yazdırma:

deinit ViewController 
deinit Fetcher 
fetching2 
fetching3 

(yukarıdaki birinci çözümün konsol çıkışına karşılaştırarak aşağıdaki yorum @Sulthan ile tartışmaya bakınız) Eğer bir için bir yöntem geçmek istiyorsanız

+0

Örneğiniz geçersizdir, çünkü aslında herhangi bir zaman uyumsuz çağrı kullanmıyorsunuz. – Sulthan

+0

"Geçersiz", belki biraz fazla güçlü, ancak asimetrinin, @ rajcanın kod örneğinde bulunmasa bile sorunun özünde olduğu doğru. Oyunumun örneğini buna göre düzenledim. Teşekkürler! – milos

+0

Koruma döngüsünün yalnızca geçicidir, ancak sorun, 'getirme 'eşzamansız olarak çalışırken' ViewController' bırakılmamıştır. – Sulthan

1

kapanma parametresi, daha sonra self yakalanmalıdır, bunun etrafında bir yolu yoktur.

Ancak, daha açık ve net olması ve yeni bir kapatma yöntemi çağrısında sarabilirsiniz: Onlara hiçbir söz de olmadığı için

func fetch() { 
    fetcher.fetch { [weak self] in 
     self?.didFetch($0) 
    }   
} 
+0

Birçok ortak senaryoya girmenin en doğru yolu budur, ancak sorulan soruya bir cevap yerine genel bir tavsiye noktasıdır: "İlk kurulumda tutma döngüsünü iptal etmenin bir yolu var mı?" – milos