ReactiveKit和Bond Part 1.1
Swift中的Target-Action模式
在我的上一篇文章中,我承诺要深入了解如何在反应式框架中制作香肠,以便更好地了解ReactiveKit和Bond中的情况。 这将是一个多步骤的过程,第一步是了解目标动作模式以及如何在Swift中实现它。 我将大量借鉴Ole Begemann在该主题上的文章。 如果您从未检查过他与克里斯·艾德霍夫(Chris Eidhof)所写的书,那我就百分之十推荐。
这是关于Swift的怪异/酷事:您可以创建对方法的引用,然后使用该方法所属的类的实例调用该方法。
在这里,马克·华伯格让我告诉你我的意思。 Ole使用“银行帐户”类进行解释,但这很无聊。 我们将使用蝙蝠侠类:
蝙蝠侠班:{
var罪犯Biffed:Int = 0
func biffCriminals(amount:Int){
罪犯+ =金额
}
}
让bruceWayne = Batman()
bruceWayne.biffCriminals(金额:2)
打印(bruceWayne.criminalsBiffed)
//打印“ 2”
好。 超级容易。 现在是棘手的部分。 让我们创建对罪犯的引用Biffed(amount:Int)
让biffer = Batman.biffCriminals
“ biffer”不是对方法的调用。 这是一个常量,它引用方法Batman.biffCriminals。 Biffer的类型为(蝙蝠侠)->(内部)->()。 然后,我们可以创建一个新引用,该引用将Batman的实例作为参数:
let wayneBiffer = biffer(布鲁斯·韦恩)
然后,我们可以使用参数调用新方法:
wayneBiffer(数量:10)
打印(bruceWayne.biffedCriminals)
//现在打印出“ 12”
可以缩短为一行:
biffer(布鲁斯·韦恩)(10)
这就是所谓的咖喱函数。 在最终方法被调用之前,部分应用了函数并设置了一些参数。 当我们使用一个函数时,我们将返回另一个部分实现的函数。
真正有用的地方在于以一种类型安全的方式实现Target-Action模式。 Target-Action是在事件发生后执行回调的一种不错的半响应方式。 正如Ole所指出的,这可能是对使用闭包的一种改进,因为它迫使您在实现时处理任何强引用和弱引用,而不是在调用函数时将它们留待处理。
在处理UI元素时,您始终会看到Target-Action模式:
让按钮= UIButton()
button.addTarget(self,action:#selector(buttonTapped),用于:.touchUpInside)
func buttonTapped(){
打印(“点击按钮!”)
}
#selector在这里是因为Target-Action的这种实现依赖于Objective-C。 我们可以在Swift中使这种类型安全。 这是Ole对Target-Action的实现,并添加了我自己的注释:
protocol TargetAction {
func performAction()
}
struct TargetActionWrapper : TargetAction {
weak var target: T?
//这是我们的咖喱函数
let action: (T) -> () -> ()
func performAction() -> () {
if let t = target {
action(t)() //when performAction is called, we're calling on the curried function stored in the TargetActionWrapper
}
}
}
enum ControlEvent {
case touchUpInside
case valueChanged
// ...
}
class Control {
// This dictionary will store the actions for each control event
var actions = [ControlEvent: TargetAction]()
//Adds a target and links it to an action
func setTarget(target: T, action: (T) -> () -> (), controlEvent: ControlEvent) {
actions[controlEvent] = TargetActionWrapper(target: target, action: action)
}
//Removes a target
func removeTargetForControlEvent(controlEvent: ControlEvent) {
actions[controlEvent] = nil
}
//This function should be called when an event occurs - eg a button tap
func performActionForControlEvent(controlEvent: ControlEvent) {
actions[controlEvent]?.performAction()
}
}
因此,让我们进行一些重构以了解其工作原理。 这是一个API Client类,具有从API获取令牌的功能:
最终课程ApiClient {
//初始化,声明属性等
func getToken(completion:@转义(String)-> Void){
让端点=“ https://token.endpoint”
警卫让url = URL(string:端点)else {return}
var request = URLRequest(url:url)
let session = URLSession(配置:
URLSessionConfiguration.default)
let task = session.dataTask(with:request){(data,response,error)in
如果让数据=数据{
做{
//解析数据以获取令牌
完成(令牌)
}抓{}
}其他{}
}
task.resume()
}
}
这是针对Target-Action重构的方式。
最后一课ApiClient: 控件 {
//初始化,声明属性等
var令牌:字符串?
func getToken(completion:@转义(String)-> Void){
让端点=“ https://token.endpoint”
警卫让url = URL(string:端点)else {return}
var request = URLRequest(url:url)
let session = URLSession(配置:
URLSessionConfiguration.default)
let task = session.dataTask(with:request){ [弱自身] (数据,响应,错误)
如果让数据=数据{
做{
//解析数据以获取令牌
self.token =令牌
self.performActionForControlEvent(controlEvent:.tokenReceived)
}抓{}
}其他{}
}
task.resume()
}
}
现在,在我们的视图控制器中,我们需要获取令牌:
类ViewController:UIViewController {
让apiClient = ApiClient()
覆盖func viewDidLoad(){
super.viewDidLoad()
apiClient.setTarget(target:self,action:
LoginViewController.printToken,controlEvent:.tokenReceived)
apiClient.getToken()
}
func printToken(){
打印(apiClient.token)
}
}
所以你有它。 如前所述,这很好,因为它无需处理代码高层中的参考循环。 下次,我们将看到它如何使我们更接近反应式编程。 有什么想法吗? 把它们留在下面!