2016-03-28 34 views
2

我有一個UIViewController,它在其某個屬性上調用一個異步函數。對於回調,而不是閉包,我想提供一個具有正確參數類型的函數。如何在curried函數中指定`weak`自我

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 
    } 
} 

每一件事工作正常,但有將已經固定,如果我做了兩個對象之間的保留週期:

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

有沒有辦法取消在該保留週期初始設置?


編輯

一些像我錯過:Fetcher.fetch沒有規定如何在保留週期會發生(對於例如,它可以極力挽留關閉),但是這是我的本意,通過沒有它標記爲@noescape。道歉!

+1

也許你能刪除你的標題中提到的「令行禁止功能」 S,因爲在沒有人提這個問題? – milos

回答

3

你爲什麼認爲有一個保留週期? Fetcher不存儲傳入的completion參考。如果您運行在操場下面:

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 

...你會看到所有的打印,因爲它應該:

fetching 
did fetch 
deinit ViewController 
deinit Fetcher 

不過,是你存放出於某種原因參考:

class Fetcher { 

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

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

然後你就確實有保留週期和兩個對象永遠不會deinit ...

編輯

這個問題越來越多了。特別是,由於@noescape在我們進入異步執行過程中目前無法執行,因此我們可以接近這樣一個理想嗎?有兩種方法可以讓人想起,但這兩種方法都不幸地爲調用者提供了編譯器支持的保證,儘管它們仍然遵循原始問題的精神,因爲它們不涉及傳遞閉包:

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)([]) 
      } 
     } 
    } 
} 

...能夠像這樣被使用(其中fetch2假定符合CompletionHandler):

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

...與效果:

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

...印刷:

deinit ViewController 
deinit Fetcher 
fetching2 
fetching3 

(與上面的第一個解決方案的控制檯輸出,看看在下面的意見與@Sulthan討論)

+0

您的示例無效,因爲您實際上沒有使用任何異步調用。 – Sulthan

+0

「無效」也許有點過於強大,但即使不存在於@ raheel的代碼示例中,異步仍是問題的核心,這是正確的。我已經相應地編輯了我的操場示例。謝謝! – milos

+0

你說得對,保留週期只是暫時的,然而問題是'fetch'是異步運行的,'ViewController'沒有被釋放。 – Sulthan

1

如果你想傳遞給方法封閉參數,那麼self將不得不被捕獲,這是沒有辦法的。

但是,你可以更明確,敷方法調用一個新的閉包:

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

雖然這是在許多常見情況下的正確方法,但這是一般性建議,而不是對所提問題的回答:「在初始設置中是否有取消保留週期的方法?」 – milos

相關問題