我不清楚这两个,当今世界正在转向封闭类型。但是我不清楚这一点。有人可以用实时示例向我解释吗?
答案 0 :(得分:1)
在Swift / obj-c中,术语delegate
用于表示对特定选择器做出响应的协议。
关于它的事情就像在对象上调用方法一样。
例如
protocol CalculatorDelegate : class { // : class so it can be made 'weak'
func onCalculation(result: Int) -> Void
}
现在,如果我们有一个Calculator
类,要使用委托,我们会做类似的事情
class Calculator() {
weak var delegate: CalculatorDelegate?
func calculate(_ a: Int, _ b: Int) -> Int {
let result = a + b
self.delegate?.onCalculation(result: result)
return result
}
}
然后在其他一些类中(例如,在iOS中-一个View Controller),我们可以这样做:
class MyClass : CalculatorDelegate {
func onCalculation(result: Int) {
print("Delegate method on calculation called with result \(result)")
}
func someButtonPress() {
let calculator = Calculator()
calculator.delegate = self
calculator.calculate(42, 66)
}
}
所以您可以看到设置非常复杂。
现在闭包只是可以在其他地方调用的代码块,因此您可以像这样更改所有代码:
class Calculator2() {
weak var delegate: CalculatorDelegate?
func calculate(_ a: Int, _ b: Int, onCalculation: (@escaping (Int) -> Void) -> Int)?) {
let result = a + b
onCalculation?(result)
return result
}
}
class MyClass {
func someButtonPress() {
let calculator = Calculator2()
calculator.calculate(42, 66, onCalculation: { (result: Int) in
print("Closure invoked with \(result)")
})
}
}
但是,在使用闭包时,您需要意识到,通过强力捕获变量(例如self)来使自己陷入困境要容易得多,即使在ARC体制下,这也会导致内存泄漏。
答案 1 :(得分:1)
所以两者的真实示例都是这样的:
protocol TestDelegateClassDelegate: class {
func iAmDone()
}
class TestDelegateClass {
weak var delegate: TestDelegateClassDelegate?
func doStuff() {
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
self.delegate?.iAmDone()
}
}
}
class TestClosureClass {
var completion: (() -> Void)?
func doStuff() {
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
self.completion?()
}
}
}
class ViewController: UIViewController, TestDelegateClassDelegate {
func iAmDone() {
print("TestDelegateClassDelegate is done")
}
override func viewDidLoad() {
super.viewDidLoad()
let testingDelegate = TestDelegateClass()
testingDelegate.delegate = self
testingDelegate.doStuff()
let testingClosure = TestClosureClass()
testingClosure.completion = {
print("TestClosureClass is done")
}
testingClosure.doStuff()
}
}
在这里,我们有2个类TestDelegateClass
和TestClosureClass
。他们每个人都有一个方法doStuff
,该方法等待3秒钟,然后向正在侦听的人报告,其中一个使用委托过程,另一个使用关闭过程。
尽管他们什么也没做,但请稍等,您可以轻松地想象它们例如将图像上传到服务器并在完成时通知。因此,例如,您可能希望在上传过程中运行活动指示器,并在完成时停止它。看起来像这样:
class ViewController: UIViewController, TestDelegateClassDelegate {
@IBOutlet private var activityIndicator: UIActivityIndicatorView?
func iAmDone() {
print("TestDelegateClassDelegate is done")
activityIndicator?.stopAnimating()
}
override func viewDidLoad() {
super.viewDidLoad()
activityIndicator?.startAnimating()
let testingDelegate = TestDelegateClass()
testingDelegate.delegate = self
testingDelegate.doStuff()
activityIndicator?.startAnimating()
let testingClosure = TestClosureClass()
testingClosure.completion = {
self.activityIndicator?.stopAnimating()
print("TestClosureClass is done")
}
testingClosure.doStuff()
}
}
自然,您将只使用两个过程之一。
您可以看到代码之间存在巨大差异。要执行委托过程,您需要创建一个协议,在这种情况下为TestDelegateClassDelegate
。协议定义了侦听器的接口。并且由于定义了iAmDone
方法,因此必须在ViewController
中定义它,只要它被定义为TestDelegateClassDelegate
。否则它将无法编译。因此,任何声明为TestDelegateClassDelegate
的东西都将具有该方法,任何类都可以调用它。在我们的情况下,我们有weak var delegate: TestDelegateClassDelegate?
。这就是为什么我们可以调用delegate?.iAmDone()
而不关心委托实际是什么的原因。例如,我们可以创建另一个类:
class SomeClass: TestDelegateClassDelegate {
func iAmDone() {
print("Something cool happened")
}
init() {
let testingDelegate = TestDelegateClass()
testingDelegate.delegate = self
testingDelegate.doStuff()
}
}
例如,一个很好的例子是使用UITableView
和delegate
的{{1}}(两者都是委托,只是属性的名称不同)。表视图将调用您设置为这些属性的任何类的方法,而无需知道该类是什么,只要它与给定的协议相对应即可。
使用闭包可以实现相同的效果。可以使用提供闭包的属性来定义表视图:
dataSource
但是这很可能导致一大堆代码。在这种情况下,由于潜在的内存泄漏,关闭也会使许多程序员头疼。并不是说闭包不是那么安全或什么,它们只是做了很多您看不到的代码,它们可能会产生保留周期。在这种情况下,最可能的泄漏是:
tableView.onNumberOfRows { section in
return 4
}
只需对其进行修复
tableView.onNumberOfRows { section in
return self.dataModel.count
}
现在看起来太复杂了。
我不会深入讨论闭包,但是最后,当您重复调用回调(如在表视图的情况下)时,在委托或闭包中都需要一个tableView.onNumberOfRows { [weak self] section in
return self?.dataModel.count ?? 0
}
链接。但是,当闭包仅被调用一次(例如上传图片)时,闭包中不需要weak
链接(在大多数情况下但并非全部)。
在回溯中,尽可能使用闭包,但在将闭包用作属性时应避免使用或谨慎使用(具有讽刺意味的是,我给出的示例)。但是您只想这样做:
weak
并将其用作
func doSomethingWithClosure(_ completion: @escaping (() -> Void)) {
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
completion()
}
}
现在,这消除了所有潜在风险。我希望这能为您解决一两个问题。
答案 2 :(得分:0)
闭包是一流的对象,因此它们可以嵌套并传递 只是, 在swift中,函数是诸如int,double或character之类的特殊数据类型,这就是为什么可以在函数参数中传递函数的原因。在swift机制中,简化了Clousers语法(如其他语言的lymbda表达式)。
例如如果要通过URSSession或Alamofire调用rest api并返回响应数据,则应使用completionHandler(它是clouser)。
无效的clouser:-{(paramter:DataType)->Void}
返回clouser:-{(paramter:DataType)->DataType}
例如(int, int) -> (int)
https://docs.swift.org/swift-book/LanguageGuide/Closures.html