Swift中的委托模式

刚进入软件开发领域,我首先开始学习用Python编写代码,然后再学习Java。 两种语言都是进入面向对象编程世界的绝佳机会。 但是,直到我开始学习Swift时,我才遇到了委托模式。 许多人警告我,由于这是一个新主意,因此可能需要一些时间来学习和习惯。 我准备好自己,看了一些视频,并完成了一些教程,但是第一次遇到一个复杂的话题时,有时会遇到挫折和困惑。 我继续前进,并自信地将其从我要学习的新主题列表中删除。 我以为,如果我在项目中需要它,我已经获得了足够的知识,至少可以查找它并将其轻松集成到我的代码中。 当机会出现在实际项目中使用它时,我很快意识到我对自己的理解并没有我想象的那么好。 此外,我很难找到一种资源来列出所需的代码行以及它们如何在涉及的两个类之间组合在一起。 我再次“打书”(隐喻地,我完全使用了互联网。)并开始了一个过于简单的实践项目,该项目隔离了授权模式,以便在它起作用之前我可以一直使用它,并且我完全理解了概念和用法。 因此,我对所学内容进行了反思。

代表团

简单来说,委托允许两个类进行通信。 从非技术意义上讲,委派(动词)是“将任务或责任委托给另一个人”(《牛津词典》)。 在技​​术世界中,除了对象之外,它是完全相同的。

该项目

我做了一个练习项目,该项目跟踪按下按钮时增加的计数。 我在这篇文章的底部包括了完整的代码,以及指向项目所在的GitHub的链接。 在我解释每个步骤时,请随时跳过或继续进行。 在第一个视图控制器中,我有一个显示计数的标签和一个通过segue移至下一个视图控制器的按钮。

在第二个视图控制器中,我只有一个按钮来更新计数。

在第二个视图控制器中,我可以多次点击“更新计数”按钮。 它将当前计数打印到控制台。 完成后,我可以在顶部的导航控制器中进行回溯,并看到第一个视图控制器中的计数标签已更新。 以下是我在第二个视图控制器上单击“更新计数”五次并返回到第一个视图后的示例。

如果我在不关闭应用程序的情况下再次重复该过程,它将继续从我上次中断的地方开始更新计数,在这种情况下为5。

代码

设置委派模式需要使用几行代码。 这里将扮演三个更大的对象:定义需求的协议,符合协议的类(即委托)和访问属性或调用协议中概述的方法的委托类。

  1. 定义协议

对于此示例,需要在两个类之间传递的唯一操作是增加计数器。 协议本身就是对象。 关键字协议是必填项,后跟用户提供的名称以及方括号。 该名称的处理方式与类名或枚举相同,其中首字母大写。 通常,在委托模式中使用时,协议名称将包含单词“ Delegate”以阐明其用途。

在方括号内,我定义了所需的方法。 我使用关键字func来指定我需要一个方法,该方法之后是我为其分配的名称,并带有开括号和闭括号。

 协议IncrementCountDelegate { 
  func gainCount() 
  } 

如果我的函数要返回某些东西或有参数,我会这样表示:

  func myFunction(参数:ParameterType)-> ReturnType 

在协议中,我可能还需要一个属性,但对于此示例,则不需要。 虽然我知道我的方法gainCount()最终可能会需要一个称为count的属性,但委托类不一定需要了解它,因此我没有在协议中包括它。 以后再说。 但是,如果确实需要包含一个属性,则该属性应如下所示:

  var myVariable:VariableType {获取设置} 

设置关键字 是可选的,表示委托类将修改此属性。 如果完全在协议中,则使用get关键字 始终是必需的,因为您是说委派类至少需要知道这一点。

2.具有符合协议的类

我的第一个视图控制器由符合我刚刚定义的协议的类管理。 它的工作是跟踪计数并提供我方法的一些实现。 此类是委托,因为当调用所需方法时,它是负责所有工作的类。 要使类符合协议,请在类声明中添加一个冒号和协议名称,如下所示:

类MyClass:MyProtocol { 
  } 

这也是实现继承的方式。 有时,冒号后面可能有多个对象名称。 在这种情况下,对象名称仅用逗号分隔。 在我的项目中,类ViewController继承自UIViewController并符合IncrementCountDelegate 。 看起来像这样:

 类ViewController:UIViewController,IncrementCountDelegate { 
  } 

如果括号内未添加任何其他内容,则Xcode将引发错误。 这是因为您没有按照IncrementCountDelegate协议中的承诺定义我称为creaseCount()的必需方法。 这是我的实现:

  func gainCount(){ 
 计数+ = 1 
打印(数)
countLabel.text =“ \(count)”
  } 

ViewController现在符合该协议,但是还没有count或countLabel。 我实例化count并将其设置为零,并连接要在视图上更新的标签。 我现在有这个:

 类ViewController:UIViewController,IncrementCountDelegate { 
  var count = 0 
@IBOutlet弱var countLabel:UILabel!
  func gainCount(){ 
计数+ = 1
打印(数)
countLabel.text =“ \(计数)”
}
  } 

现在应该没有错误了。 代表已建立。

3.创建一个告诉委托人做某事的类

委托类是需要访问在委托中分别实例化或实现的属性或方法的类。 在第二个视图控制器中,我具有“更新计数”按钮。 我将此类命名为UpdateCountViewController ,该类继承自UIViewController ,并将按钮挂钩为@IBAction 。 通过查看它,我已经知道我需要一个名为增量计数()的方法,该方法在任何符合增量计数Delegate的类中都知道 我将继续设置一个我称为“ 委托”的变量,该变量的类型为“ 增加计数”。 我将其设置为“可选”,在类型声明后用问号表示,因为此时我没有值。 我只想让我的班级知道我将在某个时候进行设置。 在Swift中,如果未分配其他任何值,则会将可选变量或常量自动设置为关键字nil 。 只有设置了变量后,我才能访问该类的方法。 现在,我可以继续告诉我的班级,“如果有一个设置为此变量的班级,当我按下’Update Count’按钮时,我将要使用称为creaseCount()的方法。“现在,我的代码看起来像这样:

 类UpdateCountViewController:UIViewController { 
  var委托:IncrementCountDelegate? 
  @IBAction func updateCountButtonPressed(sender:AnyObject){ 
委托?.increaseCount()
}
  } 

这样就结束了所有委派类的责任。 它说:“给我一个代表来执行此任务。 当一个人分配给我时,让他做他的工作,如果曾经按下我的按钮,他就称其为“增加计数”。这时,单击“更新计数”时Xcode不会抛出错误。 它不会崩溃。 实际上,它什么也不会做! UpdateCountViewController仍然不知道委托人是谁。 由于未分配任何委托,因此它根本没有调用任何方法!

注意:对于许多刚刚学习Swift的人来说,可选是一个新概念。 它们经常使用,有助于使代码更安全,更可靠。 在这篇文章中,我没有详细介绍它们,但是如果它们使您感到困惑,我强烈建议您进行进一步的研究。

4.告诉委托类使用哪个委托实例

最后一步是告诉委托类,该委托的哪个实例包含我要使用的增量计数()的实现。 在此示例中,我可以使用segues完成此操作。 正如我在上面标题为“项目”的部分中所述,按钮“ Next View Controller”执行到下一个View Controller的segue。 震撼吧? 我可以在情节提要中单击此Segue,打开属性检查器,并为Segue赋予标识符“ toUpdateCountViewController”。 现在,在委托中,我可以重写UIViewControllerprepareForSegue方法,并说:“嘿,我知道这种选择导致的视图控制器将需要委托。 因此,当执行segue时,我将成为该委托人!”下面的代码就是这样的:

 覆盖func prepareForSegue(segue:UIStoryboardSegue,发送方:AnyObject?){ 
 如果segue.identifier ==“ toUpdateCountViewController” { 
让destinationVC = segue.destinationViewController为? UpdateCountViewController
  destinationVC?.delegate =自我 
}
  } 

因此,委托ViewController负责监视要执行的segue。 一旦收到即将发生segue的信号,委托就将自己分配给委托类的委托属性。 现在一切都应该正常工作。 完成的程序如下所示:

单击此处转到GitHub中的项目。

结论

尽管需要进行一些设置,但委派模式可能是一个非常有用的工具,尤其是当您有多个需要在两个或多个类之间进行通信的方法或属性时。 只记得:

  1. 定义协议
  2. 具有符合协议的类
  3. 创建一个告诉委托人做某事的类
  4. 告诉委托类使用哪个委托实例

这是一个非常简单的示例,例如,可以通过使用闭包和依赖项注入而无需进行太多设置即可完成。 但是,作为一个初学者,我需要以最基本的方式来了解这种模式。 我仍在学习,因此,如果有任何读者发现任何错误或可以以任何方式添加到讨论中,请随时发表评论。