【问题标题】:Is there a concept of event in Swift similar to events in C#?Swift 中是否有类似于 C# 中事件的事件概念?
【发布时间】:2017-01-03 10:56:40
【问题描述】:

我正在从 Java\C# 背景进入 Swift,但无法弄清楚一件事。 有什么方法可以在 Swift 对象中创建类似于 C# 中的事件 (Action<T>) 的通知事件?

或者我是否应该为此目的使用闭包?

【问题讨论】:

标签: swift swift3


【解决方案1】:

您可以使用委托和协议来做到这一点。这是一个简单的示例,假设您有一个带有 2 个 ViewController 的 Storyboard,分别称为 ViewController 和 SecondViewController(故事板 ID 为“Main”)。

protocol SampleProtocol: class {
    func didLoad()
    func didAppear()
}

class ViewController: UIViewController, SampleProtocol {
    override func viewDidLoad() {
        super.viewDidLoad()

        let secondViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondVC") as! SecondViewController
        secondViewController.configure(delegate: self)
        addChildViewController(secondViewController)
        view.addSubview(secondViewController.view)
    }

    func didLoad() {
        print ("didLoad")
    }

    func didAppear() {
        print ("didAppear")
    }
}

class SecondViewController: UIViewController {
    weak var delegate: SampleProtocol?

    override func viewDidLoad() {
        super.viewDidLoad()

        delegate?.didLoad()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        delegate?.didAppear()
    }

    func configure(delegate: SampleProtocol) {
        self.delegate = delegate
    }
}

【讨论】:

    【解决方案2】:

    据我了解,C# 中的 Action<T> 委托只是一个 Void-returning 函数委托,它封装了一个可以通过委托执行的方法。

    由于 Swift 支持高阶函数,您可以定义自己的 Action<T> 类型,其唯一目的是为类型保持器类型 T 的单个参数封装 Void-returning 方法。

    struct Action<T> {
        private let f: (T) -> ()
    
        init(_ f: @escaping (T) -> ()) {
            self.f = f
        }
    
        func run(_ val: T) {
            f(val)
        }
    }
    

    我们可以使用这个,例如封装一个函数:

    func addOneAndPrint(val: Int) {
        print(val+1)
    }
    
    let increaseAndPrintAction = Action(addOneAndPrint) // inferred to Action<Int>
    increaseAndPrintAction.run(1) //2
    

    或提供的闭包(类似于在 C# 中向 Action&lt;T&gt; 提供 lambda):

    let increaseAndPrintAction = Action { print($0 + 1) }
    increaseAndPrintAction.run(1) // 2
    [1, 3, 5].forEach(increaseAndPrintAction.run) // 2 4 6
    

    现在,我不知道 C# 中 Action&lt;T&gt; 的常见用例,但如果您想在完成某些任务时使用它来执行某些事件,您可能只想使用提供的完成处理程序关闭要执行的任务:

    func increaseTask(_ val: inout Int, completion: (Int) -> ()) {
        val += 1
        // ...
        completion(val)
    }
    
    var val = 1
    increaseTask(&val) { print("Successfully increased to value \($0)") }
        // Successfully increased to value 2
    print(val) // 2
    

    【讨论】:

      【解决方案3】:

      正如其他人所说,swift 中没有 event 关键字。但是很容易实现你的。

      我已经完成了满足这些要求的swift package

      source code 非常简单,不到 100 sloc。

      这是一个简短的版本:

      public class Event<T> {
          private var handlers:[EventHandler<T>] = []
      
          private init() {}
      
          private func invoke(sender:AnyObject?,value:T) -> Void {
              for handler in self.handlers {
                   handler.handle(sender,value)
              }
          }
      
          public static func += ( event: Event, handler: EventHandler<T>) -> Void {
          {
               event.handlers.append(handler)
          }
      
          public static func -= ( event: Event, handler: EventHandler<T>) -> Void {
               event.handlers.removeAll{$0 === handler}
          }
      
          public static func create() -> (invoke:Delegate<T>,event:Event<T>){
               let res = Event<T>()
               return (res.invoke,res)
          }
      }
      

      EventHandler 在哪里

      ///needed as Swift doesn't allow func ===
      public class EventHandler<T> {
           private var _handle:Delegate<T>
      
           public var handle:Delegate<T> {
                return self._handle
           }
      
           public init(handle:@escaping Delegate<T>){
                self._handle = handle
           }
      }
      

      Delegate

      public typealias Delegate<T> = (_ sender:AnyObject?,_ args:T) -> Void
      

      使用 asis :

      let tmp = Event<String>.create()
      var handler = EventHandler<String>(handle: { sender, args in
          print(args)
      })
      tmp.event += handler
      tmp.invoke(self,"Hello world !")
      //handler should print "Hello world !"
      tmp.event -= handler
      

      此解决方案提供与 C# 几乎相同的语法:+=-=Invoke,并防止在声明范围之外调用 invoke

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-07-26
        • 1970-01-01
        • 2014-06-06
        • 2017-02-16
        • 1970-01-01
        • 1970-01-01
        • 2017-12-29
        相关资源
        最近更新 更多