使用委托模式在控制器之间进行通信。

降低应用程序复杂性的一种好方法是委托模式。 这个想法非常简单:您无需在当前上下文中包含某些行为的代码,只需向上下文提供一个委托对象,然后由该委托对象的方法执行操作。 这与继承之上的构成保持一致:您的代码避免了超类及其子类之间的紧密耦合。 通过避免继承,您的代码可以更容忍修改。 通过使代码更具模块化,您可以避免处理过多职责的大型对象,从而使维护变得更容易。

这是一个简单的例子:

 协议音箱{ 
 函数speak(); 
  } 
 猫类{ 
 共鸣箱人声和弦 
 函数meow(){ 
  vocalChords.speak() 
  } 
  } 
  KittyVocalChords类实现了Soundbox { 
 函数speak(){ 
  printToTerminal(“喵”) 
  } 
}
 猫玛丽亚= Cat.new() 
  catVocalChords = KittyVocalChords.new() 
  marya.vocalChords = catVocalChords 

猫需要知道如何喵喵叫,但是猫可能还有其他不需要注意的行为。 将它们保持为单独的对象可以使您的声乐班级完成它的声乐事务,而猫则将其完成猫的事务。 您可以将所有这些信息转储到cat类中,但这会使cat类变得更加复杂,因此更加难以阅读和推理。 另一个选择是使用所需的方法定义基类,这将降低复杂性,但是现在您已经与该基类绑定不健康。 对基类所做的任何更改都会影响继承者。 例如,如果您希望狗类也继承定义语音行为的同一基类,该怎么办? 那计算机课呢? 除此之外,更改行为就像更改委托一样简单:

 如果environment.dominate_gas ==氦{ 
  marya.vocalChords = sweakyKittyVocalChords 
  } else,如果environment.dominate_gas == NITROGEN { 
  marya.vocalChords = normalKittyVocalChords 
  } 

我们唯一需要担心的是代表。 猫类从来不需要为在不同气体中的行为而烦恼。

因此,撇开人为的例子,这在iOS开发中具有价值。 在我正在开发的健身应用中,我使用容器视图。 父控制器收到一些需要容器注意的事件。 使用委托,我们可以创建一个回调。 父控制器不需要知道容器视图在做什么,只要知道容器视图告诉它停止就可以停止并启动它。 如果容器视图发生更改,则对委托的引用也会随之更改。 一种替代方法是使用NotificationCenter,但这会给您的代码增加很多不必要的复杂性。 使用委托作为协议,您要做的就是确保新类符合协议,并且一切顺利。

这是我的项目中的一个示例:

因此,您看到容器中有三个控制器。 取决于您所使用的控制器,应该发生不同的事情。 表视图应该锁定使用输入,而带有所有信息的堆栈视图应该暂停它自己的计时器。 您当然可以简单地让父视图执行此操作。 它具有所有子视图的数组。 这将需要很多不必要的复杂代码来跟踪状态并确保您将调用正确的方法。 对您的测试写作技能而言,这是一个很好的锻炼,但是使用委托可以很容易地避免使用易碎的代码。

当我在应用程序未暂停的情况下单击表格视图单元时,您会看到此信息。

我没有问题。

现在,当我暂停应用程序然后单击表格视图时,它应将我的输入锁定在外面:

现在我不能前进了。

以下是其工作原理的一般概念:

TimerParentView.swift:

我们将协议放入此文件中,但是它可以放在任何地方。 通常将它放在与实现相同的文件中,但是在我们的示例中,我们将其放在前面和中间。 对委托的引用是可选的,因此我们不必担心是否已设置。 就像带有通知的“广播”(观察者模式)一样,我们不需要知道接收者是否存在。 我们只是开枪,但是我们还有一个额外的好处,就是只要实现的类符合我们的协议,就不需要实现任何东西,只是想要的方法。 它是多态的。

使此工作正常进行,仅需几个补充:

TimerParentView.swift

pausePlay(isPaused :)具有与lockTable类似的签名。(isLocked :)您可能建议我们对两者使用相同的函数和协议。 您可以,但是这会使您的代码更难阅读。 另外,在我的实际代码库中,代表们还有其他责任。

FirstContainerView.swift

SecondContainerView.swift和SecondTableDataSource.swift

// none of the differences are needed for this example so we'll skip 'em

ThirdContainerView.swift

再一次,您需要做的就是实现委托

OOP原则可以节省大量工作。 所有这些都是在父控制器中使用相同的方法完成的。 我通过segue将对父控制器的引用传递给容器,然后每次使用viewWillAppear(animated :)&viewWillDisappear(animated :)方法将其对引用的引用更新为相关委托。 所有容器控制器都实现必要的委托,并且当所需事件发生时,委托将触发。 就像回调一样,但不必担心细节。

PS-有人认为通知中心会是一个更好的选择。 我们中间的受虐狂可能更喜欢将所有物品都放在一间房子里,而只是使用NotificationCenter。 如果可以避免,请这样做。 将发送方发送给n个侦听器的想法似乎很简单,但是根据需要触发的事件数量,很容易就无法推断应用程序的当前状态。 代表很容易推理,并有成为方法的好处,因此可以被命名。 您的项目的新开发人员将甚至可以轻松理解最复杂的事件序列。

TimerParentView.swift

FirstContainerView.swift

FirstTableDataSource.swift

SecondContainerView.swift和SecondTableDataSource.swift

// No real changes worth noting

ThirdContainerView.swift

如您所见,使用委托更加简单。 这样,您就可以避免由于过度使用NotificationCenter而导致的许多难以跟踪的错误。 使用可选选项还可以避免牢固绑定的问题。 唯一真正的好处是不需要引用父视图,并且由于导航控制器的缘故,这实际上是必需的。 如果必须依次发生多个事件,则可能很难调试。