2016-11-16 1 views
3

Мне нужны простые способы сделать класс, который я могу передать, и вызвать функцию обратного вызова с помощью Swift 3.0Как сделать быстрый класс 3.0 с функцией func, которая вызывает обратный вызов?

Я не люблю использовать что-либо вроде Notification Centers или Selectors with # перед ними. Я придирчив, и мне тоже не нравится печатать так много.

Пример:

func setCallback(function:Callback){ 
    self.callback = function 
} 

func callCallback(){ 
    self.callback() 
} 
+1

Попробуйте [это] (https://www.weheartswift.com/closures/) учебник – Honey

+0

@Honey отличный учебник! – JThora

ответ

3

Давайте представим, у вас есть какая-то сложная задача, и вы хотите поставить обратный вызов вызываются, когда задача выполнена:

class ComplexTaskManager { 
    var completionHandler: (() -> Void)? 

    func performSomeComplexTask() { 
     ... 

     // when done, call the completion handler 

     completionHandler?() 

     // if this was a one-time completion handler, you'd `nil` it; it helps protect against strong reference cycles; otherwise comment the next line 

     completionHandler = nil 
    } 
} 

Таким образом, вы будете использовать это нравится:

let manager = ComplexTaskManager() 

manager.completionHandler = { 
    // this is what I want to do when the task is done 
} 

manager.performSomeComplexTask() 

Теперь, я воображал, что мы имеем дело с каким-то трудоемкое асинхронной задачей, для которой вы хотите вызвать функцию обратного вызова, когда это сделано. Но есть и другие аналогичные шаблоны (например, возможно, вы выполняете итерацию через некоторый объект модели, и вы хотите вызвать ваш обратный вызов для каждого экземпляра внутри этого объекта), но идея такая же, как и выше.


Если у вас есть сильный опорный цикл, вы можете исправить это с помощью слабых ссылок (например, поставки [weak self] или [unowned self] к закрытию). Например, представьте себе:

class ViewController { 
    let manager = ComplexTaskManager() 

    @IBOutlet weak var statusLabel: UILabel! 

    override viewDidLoad() { 
     super.viewDidLoad() 

     statusLabel.text = "Starting complex task" 

     manager.completionHandler = { [weak self] in 
      self?.statusLabel.text = "Done" 
     } 

     manager.performSomeComplexTask() 
    } 
} 

Но вам нужно только сделать это, если есть сильный опорный цикл (например, контроллер представления держит сильную ссылку менеджеру и из-за присутствия self внутри закрытия, то менеджер сохраняет обратную связь с контроллером представления). Если какой-либо из них являются локальными ссылками, а не свойство, хотя, нет никакого сильного опорного цикла ломаться.


Откровенно, тем более общий шаблон для закрытия, которая будет вызываться один раз за задачей является предоставить его в качестве параметра метода. Например:

class ComplexTaskManager { 
    func performSomeComplexTask(completionHandler:() -> Void) { 
     ... 

     // when done, call the completion handler 

     completionHandler() 
    } 
} 

Таким образом, вы бы использовать его как:

let manager = ComplexTaskManager() 

manager.performSomeComplexTask() { 
    // this is what I want to do when the task is done 
} 

Красота этого подхода заключается в том, что это не только проще, но и позволяет избежать риска сильных опорных циклов.

+0

для первого подхода, решаете ли вы сильную проблему с опорным циклом? Я имею в виду, не будет ли компилятор давать ошибку для ссылки на 'completHandler()' using 'self'? – Honey

+0

Используйте ссылку 'weak' или' unowned'. См. Пересмотренный ответ. – Rob

+0

Мой вопрос был что-то * else *, которое было вызвано недоразумением. Извините ... Я думаю, что если вы откажетесь от вопроса, он будет более чистым: /, а затем удалите все комментарии ниже. – Honey

1

Один из способов:

типа, который представляет собой необязательный обратный вызов без входных значений или возвращающие значения могут быть записаны следующим образом:
(() ->())?


Swift 3,0 Примера:

class SimpleState:NSObject{ 

    var key:String? = nil 
    var callback:(() ->())? = nil 

    init(key:String){ 
     self.key = key 
    } 

    func setCallback(function:(() ->())?){ 
     self.callback = function 
    } 

    func runCallback(){ 
     if(self.callback != nil){ 
      self.callback() 
     } 
    } 
} 
+3

Вместо 'if callback! = Nil {callback()}', вы можете просто выполнить 'callback?() '. – Rob

+2

И вам действительно не нужен метод setCallback, поскольку вы обычно просто делаете 'simpleState.callback = ...'. Я знаю, что вы просто выразили это, потому что это то, о чем попросил ОП, но просто установка свойства закрытия напрямую, вероятно, более чистая. – Rob

+0

@Rob, если вы ответите с более ясным и чистым примером, включая упомянутые вами изменения кода, я буду зелёным проверить ya! – JThora

0

Он также помогает при обмене сообщениями при их создании.Что-то вроде этого:

class OutsideClass { 
    public var completionHandler: ((_ success:Bool) -> Void)? 
    public var errorHandler: ((_ err:String) -> Void)? 

    func doSomething() { 
     errorHandler?("An error occured") 
    } 
} 

, то вы можете просто назначить функцию

class SomeClass { 
    func useOutsideClass() { 
     var outsideClassInstance = OutsideClass() 
     outsideClassInstance.errorHandler = self.errorHander 
     outsideClassInstance.doSomething() 
    } 

    func errorHandler(err:String) -> Void { 
     print(err) 
    } 
} 
Смежные вопросы