闭包与委托模式

Chr*_*sco 12 delegates closures ios swift

我正在使用网络请求类,我担心崩溃.例如,在将回调方法传递给函数时,使用闭包非常简单:

// some network client
func executeHttpRequest(#callback: (success: Bool) -> Void) {
    // http request

    callback(true)
}

// View Controller
func reload() {
    networkClient.executeHttpRequest() { (success) -> Void in
        self.myLabel.text = "it succeeded" // NOTE THIS CALL
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,由于应该执行回调的进程是异步的,当回调与容器类元素(在本例中是一个UIKit类)交互时,它可能容易在类似的情况下崩溃

  1. 当异步任务仍在执行时,用户导航到另一个View Controller
  2. 用户在异步任务仍在执行时按下主页按钮
  3. 等等...

因此,当回调最终被触发时,self.myLabel.text可能会导致崩溃,因为self引用的View Controller 可能已经被释放.

到这一点.我是对的还是在内部快速实施某些内容以便永远不会发生这种情况?

如果我是正确的,那么当代理模式派上用场时,就像代理变量一样weak references,这意味着,如果取消分配,它们不会保留在内存中.

// some network client

// NOTE this variable is an OPTIONAL and it's also a WEAK REFERENCE
weak var delegate: NetworkClientDelegate?

func executeHttpRequest() {
    // http request

    if let delegate = self.delegate {
        delegate.callback(success: true)
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意self.delegate,由于它是a weak reference,它将指向nilView Controller(实现NetworkClientDelegate协议的人)是否被释放,并且在这种情况下不调用回调.

我的问题是:闭包有什么特别的东西,使它们成为类似于这个场景的好选择,而不是回到委托模式?如果提供闭包的例子(由于nil指针而不会导致崩溃)将会很好.谢谢.

Cou*_*per 14

因此,当回调最终被触发时,self.myLabel.text可能会导致崩溃,因为自己所引用的View Controller可能已经被释放.

如果self已导入的盖,很强的借鉴意义,可以保证self不会直到闭合已经执行完毕被释放了.也就是说,当调用闭包时,视图控制器仍处于活动状态 - 即使此时视图不可见.语句self.myLabel.text = "it succeeded"将被执行,但即使标签不可见,也不会崩溃.

但是,有一个微妙的问题可能导致在某些情况下崩溃:

假设,闭包具有对视图控制器的最后且唯一的强引用.闭包完成,随后被取消分配,这也释放了对视图控制器的最后一个强引用.这不可避免地会调用dealloc视图控制器的方法.该dealloc方法将在将执行闭包的同一线程上执行.现在,视图控制器是一个UIKit对象,必须保证发送到该对象的所有方法都将在主线程上执行.所以,IFF dealloc实际上会在其他一些线程上执行,你的代码可能会崩溃.

一种合适的方法需要"取消"一个异步任务,当它被"关闭"时,视图控制器不再需要该结果.当然,这需要取消您的"任务".

为了减轻前一种方法的一些问题,在定义闭包时,可能会捕获视图控制器的引用而不是引用.这不会阻止异步任务运行完成,但在完成处理程序中,您可以检查视图控制器是否仍然存活,并且如果它不再存在则只是挽救.

并且,如果你需要在一些可能在某个任意线程上执行的闭包中"保留"UIKit对象,请注意这可能是最后一个强引用,并确保在线程上释放最后一个强引用.

另请参阅:在dispatch_async函数中使用弱自我

编辑:

我的问题是:闭包有什么特别的东西,使它们成为类似于这个场景的好选择,而不是回到委托模式?

我想说,在许多用例中,闭包是"更好"的方法:

代理更容易出现循环引用而不是闭包之类的问题(因为它们由对象"拥有",并且此对象可能被捕获为委托中的变量).

闭包作为完成处理程序的经典用例还改进了代码的"局部性",使其更易于理解:您说明调用任务的语句之后任务完成时会发生什么 - 无论可能需要多长时间.

闭包与常规"函数"的巨大优势在于闭包在定义时捕获整个"上下文".也就是说,它可以引用变量并在定义时将它们"导入"闭包 - 并在执行时使用它,无论何时发生,以及在定义时间的原始"堆栈"消失时已经.