2016-03-28 2 views
2

У меня есть UIViewController, который вызывает функцию async по одному из своих свойств. Для обратного вызова вместо закрытия я хотел бы предоставить функцию, которая имеет подходящие типы параметров.Как указать `слабый` self в curried-функции

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 
} 

Есть ли способ, чтобы отменить цикл сохранения в Начальная настройка?


EDIT

Что-то я пропустил: Fetcher.fetch не определяет, каким образом сохранить цикл будет (для например, он может сохранить закрытие сильно), но это было мое намерение, не имея его следует отметить @noescape. Извиняюсь!

+1

Возможно, вы могли бы удалить ссылку на «кэрри функции» ы в вас названии, поскольку нет никакого упоминания о них в вопрос? – 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

Смежные вопросы