What is Target-Action?
整理一下目前我对 target-action 的理解:
让我们先来看落落长的定义
The term target–action design paradigm refers to a kind of software architecture, where a computer program is divided into objects which dynamically establish relationships by telling each other which object they should target and what action or message to send to that target when an event occurs. This is especially useful when implementing graphical user interfaces, which are by nature event-driven. — From Wikipedia
target-action 是一种软体架构的设计模式,将程式分成很多个 objects,这些 objects 透过告知它们应该以哪个对象为目标以及在事件发生时向该目标发送什么动作或消息来动态建立关係 . 这在实现本质上是事件驱动的图形用户界面时特别有用。
Target-action is a design pattern in which an object holds the information necessary to send a message to another object when an event occurs. The stored information consists of two items of data: an action selector, which identifies the method to be invoked, and a target, which is the object to receive the message. The message sent when the event occurs is called an action message. Although the target can be any object, even a framework object, it is typically a custom controller that handles the action message in an application-specific way. — From Apple Documentation
Target-action 是一种设计模式,其中一个物件在事件发生时,保留传送讯息给另一个物件所需的资讯。这些资讯包括两个项目:一个动作选择器 (action selector),用于识别要呼叫的方法,以及一个目标物件(target),用于接收讯息。当事件发生时传送的讯息称为动作讯息 (action message)。目标物件可以是任何物件,甚至是框架物件,但通常是处理应用程式特定方式的自订控制器。
以上资料分别截取自维基百科以及苹果官方文件,让我们不要咬文嚼字,改用以下例子来理解。
以上是一段简单的 Swift 程式码,用 class 宣告了 Door 这个物件,并且定义了一个 method open() 后,并依此建立一个 door 实例,并呼叫方法。这段程式码遵循了 OOP 的基本原则,透过将功能封装在 class 里,实现了封装性 (Encapsulation) 这个特性。
然而若将场景拉到 iOS app 上来看,例如下图:
以上是在 iOS 开发中最基本的功能之一,也就是让使用者点击某个 button 后,画面上的 label 会随之改变。
但看似简单的功能,实际上涉及了:
对于开发者而言所需着重之部分应为最后一点,即决定在发生何种事件后,要实现什么样的逻辑,来回应使用者的行为及期待。
其余部分,则由 iOS 替你处理,让开发者可以专注于处理事件处理,如以下苹果官方的示意图:
The simplest approach
在我们最一开始学习开发 iOS 时,如果你是从 UIKit 开始学习,通常都会从 Storyboard 拉元件开始。透过拉取一个 @IBAction function 到程式码中,并在 function 中定义你要实作的行为。事实上这就是一种 target-action mechanism 的展现。
但我们有另外一种方法可以达到相同的效果:
根据苹果官方定义,你可以呼叫 addTarget(_:action:for:) 此 function 来替你的元件加入特定事件。
target: 在哪做 (The object whose action method is called)action: 做什么controlEvents: 在什么情况下触发因此我们可以改用下列方法来实作:
不论是直接拉 @IBAction 或使用 addTarget(_:action:for:) 此种方式皆可达到元件与 action 分离解耦之效。
target
The target object—that is, the object whose action method is called. If you specify nil, UIKit searches the responder chain for an object that responds to the specified action message and delivers the message to that object.
根据官方文档,target 如果被设置为 nil,UIKit 会在 responder chain 中查找能够响应指定动作消息的 object,并将 action 传递给该 object 进行处理。以上图来说,我们带入 target 的参数为 self,即所在的 ViewController。
在 iOS 中,responder chain 用于处理事件和响应使用者的操作。当事件发生时,UIKit 会沿着 responder chain 向上查找第一个能够响应该事件的对象,并将事件传递给其 action 进行处理。
可以看到当 target 参数设为 nil 时,仍能正确呼叫到 sayHi() 这个 function。
Responder chain 参考示意图
selector
Selector 这个参数,官方列举了三种形式:
event
侦测什么样的事件,例如我们最常使用的 .touchUpInside,其余可 可参考官方文档。
小结
使用 target-action 可以让开发者可以专注于处理事件,决定使用者流程,并可使控制元件与 action 分离,提升程式码的可维护性。