首页 iOS.& Swift Books 教程设计模式

16
组播委托模式 由约书亚格林写

组播委托模式是一种行为模式,它是委托模式的变化。它允许您创建一对多委托关系,而不是简单委托中的一对一关系。它涉及四种类型:

  1. 一个 需要委托的对象,也称为 委托对象,是对象 一个或多个代表。

  2. 委托协议 定义委托可能或应该实施的方法。

  3. 委托(S) 是实现委派协议的对象。

  4. 组播代表 是一个持有ONO代表的类课程,并允许您通知每个委托的代表值得反应。

组播委托模式和委托模式之间的主要区别是存在组播委托辅助类。 Swift默认情况下不向您提供此类。但是,您可以轻松创建自己的,您可以在本章中完成。

笔记: Apple introduced a new Multicast type in the Combine framework in Swift 5.1. This is different than the 多播 introduced in this chapter. It allows you to handle multiple Publisher events. In such, this could be used as an alternative to the multicast delegate pattern as part of a reactive achitecture.

Multicast is an advanced topic in the Combine framework, and it’s beyond the scope of this chapter. If you’d like to learn more about Combine, check out our book about it, 结合:Swift的异步编程 (http://bit.ly/swift-combine)。

你什么时候应该用它?

使用此模式创建一对多委托关系。

例如,只要发生更改发生在另一个对象时,您可以使用此模式通知多个对象。然后,每个委托都可以更新其自己的状态或以响应执行相关操作。

操场例子

打开 中级agepattern.xcWorkspace. 在里面 起动机 目录,或从上一章中从您自己的游乐场工作区继续,然后打开 多播 从这一页的页面 文件层次结构.

// 1
public class MulticastDelegate<ProtocolType> {

  // MARK: - DelegateWrapper
  // 2
  private class DelegateWrapper {

    weak var delegate: AnyObject?

    init(_ delegate: AnyObject) {
      self.delegate = delegate
    }
  }
}
// MARK: - Instance Properties
// 1
private var delegateWrappers: [DelegateWrapper]

// 2
public var delegates: [ProtocolType] {
  delegateWrappers = delegateWrappers
    .filter { $0.delegate != nil }
  return delegateWrappers.map
    { $0.delegate! } as! [ProtocolType]
}

// MARK: - Object Lifecycle
// 3
public init(delegates: [ProtocolType] = []) {
  delegateWrappers = delegates.map {
    DelegateWrapper($0 as AnyObject)
  }
}
// MARK: - Delegate Management
// 1
public func addDelegate(_ delegate: ProtocolType) {
  let wrapper = DelegateWrapper(delegate as AnyObject)
  delegateWrappers.append(wrapper)
}

// 2
public func removeDelegate(_ delegate: ProtocolType) {
  guard let index = delegateWrappers.firstIndex(where: {
    $0.delegate === (delegate as AnyObject)
  }) else {
    return
  }
  delegateWrappers.remove(at: index)
}
public func invokeDelegates(_ closure: (ProtocolType) -> ()) {
  delegates.forEach { closure($0) }
}
// MARK: - Delegate Protocol
public protocol EmergencyResponding {
  func notifyFire(at location: String)
  func notifyCarCrash(at location: String)
}
// MARK: - Delegates
public class FireStation: EmergencyResponding {

  public func notifyFire(at location: String) {
    print("Firefighters were notified about a fire at "
      + location)
  }

  public func notifyCarCrash(at location: String) {
    print("Firefighters were notified about a car crash at "
      + location)
  }
}

public class PoliceStation: EmergencyResponding {

  public func notifyFire(at location: String) {
    print("Police were notified about a fire at "
      + location)
  }

  public func notifyCarCrash(at location: String) {
    print("Police were notified about a car crash at "
      + location)
  }
}
// MARK: - Delegating Object
public class DispatchSystem {
  let multicastDelegate =
    MulticastDelegate<EmergencyResponding>()
}
// MARK: - Example
let dispatch = DispatchSystem()
var policeStation: PoliceStation! = PoliceStation()
var fireStation: FireStation! = FireStation()

dispatch.multicastDelegate.addDelegate(policeStation)
dispatch.multicastDelegate.addDelegate(fireStation)
dispatch.multicastDelegate.invokeDelegates {
  $0.notifyFire(at: "Ray’s house!")
}
Police were notified about a fire at Ray's house!
Firefighters were notified about a fire at Ray's house!
print("")
fireStation = nil

dispatch.multicastDelegate.invokeDelegates {
  $0.notifyCarCrash(at: "Ray's garage!")
}
Police were notified about a car crash at Ray's garage!

你应该小心吗?

此模式最适用于“仅限信息”委派调用。

教程项目

您将继续从上一章继续镜像垫应用程序。

@objc public protocol DrawViewDelegate: class {
  func drawView(_ source: DrawView, didAddLine line: LineShape)
  func drawView(_ source: DrawView, didAddPoint point: CGPoint)
}
// MARK: - Delegate Management
public let multicastDelegate =
  MulticastDelegate<DrawViewDelegate>()

public func addDelegate(_ delegate: DrawViewDelegate) {
  multicastDelegate.addDelegate(delegate)
}

public func removeDelegate(_ delegate: DrawViewDelegate) {
  multicastDelegate.removeDelegate(delegate)
}
public override func touchesBegan(_ touches: Set<UITouch>,
                                  with event: UIEvent?) {
  guard let point = touches.first?.location(in: drawView)
    else { return }
  let line = LineShape(color: drawView.lineColor,
                       width: drawView.lineWidth,
                       startPoint: point)
  // 1
  addLine(line)

  // 2
  drawView.multicastDelegate.invokeDelegates {
    $0.drawView(drawView, didAddLine: line)
  }
}

private func addLine(_ line: LineShape) {
  drawView.lines.append(line)
  drawView.layer.addSublayer(line)
}
public override func touchesMoved(_ touches: Set<UITouch>,
                                  with event: UIEvent?) {
  guard let point = touches.first?.location(in: drawView)
    else { return }

  // 1
  addPoint(point)

  // 2
  drawView.multicastDelegate.invokeDelegates {
    $0.drawView(drawView, didAddPoint: point)
  }
}

private func addPoint(_ point: CGPoint) {
  drawView.lines.last?.addPoint(point)
}
// MARK: - DrawViewDelegate
extension DrawViewState: DrawViewDelegate {
  public func drawView(_ source: DrawView,
                       didAddLine line: LineShape) { }

  public func drawView(_ source: DrawView,
                       didAddPoint point: CGPoint) { }
}
// MARK: - DrawViewDelegate
extension AcceptInputState {

  public override func drawView(_ source: DrawView,
                                didAddLine line: LineShape) {
    let newLine = line.copy() as LineShape
    addLine(newLine)
  }

  public override func drawView(_ source: DrawView,
                                didAddPoint point: CGPoint) {
    addPoint(point)
  }
}
// MARK: - DrawViewDelegate
extension DrawView: DrawViewDelegate {

  public func drawView(_ source: DrawView,
                       didAddLine line: LineShape) {
    currentState.drawView(source, didAddLine: line)
  }

  public func drawView(_ source: DrawView,
                       didAddPoint point: CGPoint) {
    currentState.drawView(source, didAddPoint: point)
  }
}
// MARK: - View Lifecycle
public override func viewDidLoad() {
  super.viewDidLoad()
  mirrorDrawViews.forEach {
    inputDrawView.addDelegate($0)
  }
}

关键点

您了解了本章中的组播委派模式。以下是其关键点:

有一个技术问题?想报告一个错误吗? 您可以向官方书籍论坛中的书籍作者提出问题和报告错误 这里.

有反馈分享在线阅读体验吗? 如果您有关于UI,UX的反馈,请高在线到设计团队:

© 2021 Razeware LLC

您可以免费读取,本章的部分显示为 混淆了 文本。解锁这本书,以及我们整个书籍和视频目录,带有Raywenderlich.com的专业订阅。

现在解锁

要突出或记笔记,您需要在订阅中拥有这本书或自行购买。