Swift 轻松掌握喵神高阶心法 I

2017-01-13 14:55:45来源:http://www.jianshu.com/p/f2b318da0a83作者:Doubles_Z人点击

第七城市

随着论文临进尾声,下周就要进行答辩, 终于有机会和大家继续一起分享和学习。之前的Lifestyle项目短短10篇就得到了不少同学的喜欢和关注,甚至有受到腾讯广州研发部的青睐,真是非常感谢大家的支持!但经过深思熟虑本人决定停止更新,原因如下:1)想把注意力花在自身成长上,而不是重复造轮子。2)随着Swift3.0的来临及Apple官方组成了Swift Sever端小组,以及Apple对于AR技术的热衷,对于未来Swift的前景非常看好。3)想对自己深入学习的东西做一些记录,总结。



说起Swift 是Apple2014年6月WWDC上发布的新时代的编程语言,本人就是因为Swift的发布而转战进入iOS开发的行列,再此我要感谢对于我学习帮助最大的两位iOS大神——MJ和瞄神,这个系列我会和大家一起学习瞄神的《Swifter - Swift必备tips》第三版,希望能够走上编程的进阶之路。


Tip1 柯里化(Currying)

Swift 里可以将方法进行柯里化 (Currying),这是也就是把接受多个参数的方法进行一些变形,使其更加灵活的方法。函数式的编程思想贯穿于 Swift 中,而函数的柯里化正是这门语言函数式特点的重要表现。
举个例子,下面的函数简单地将输入的数字加 1:


func addOne(num: Int) -> Int {
return num + 1
}

这个函数所表达的内容非常有限,如果我们之后还需要一个将输入数字加 2,或者加 3 的函数,可能不得不类似地去定义返回为 num + 2 或者 num + 3 的版本。有没有更通用的方法呢?我们其实可以定义一个通用的函数,它将接受需要与输入数字相加的数,并返回一个函数。返回的函数将接受输入数字本身,然后进行操作:


func addTo(_ adder: Int) -> (Int) -> Int {
return {
num in
return num + adder
}
}

有了 addTo,我们现在就能轻易写出像是 addOne 或者 addTwo 这样的函数了


let addTwo = addTo(2) // addTwo: Int -> Int
let result = addTwo(6) // result = 8


好了说实在的,在对Swift只有浅显认知的我,看到这里就已经懵逼了。。。对于函数后的双箭头以及return后面所带的一连串大括号感到深深的无力,对于我这种仅仅熟练掌握Objective-C的小白来说这明显超出了我的认知范围,好在我马上认识到了我的无知,耗费了7天的时间将Swift 3.0.1的官方文档只字不差的阅读并通过Playground将文档中的例子练习并加深印象,今天才能上手喵神的大作!


我们先来解读这个函数双箭头的含义:
func addTo(_ adder: Int) -> (Int) -> Int
func addTo(_ adder: Int) -> ((Int) -> Int)

加上括号是不是就和我一样豁然开朗了呢,没错这个其实就是返回值类型是函数,好多编程语言都有这个思想,但就是因为省略了一个括号,马上就自乱阵脚,看来修行的路还很长啊~~


我们再来看return后面的一串:
return { 
num in
return num + adder
}

在Swift中 { }其实就是一个匿名函数,也称之为闭包,和OC中的Block比较相似。由之前函数双箭头的分析得知,返回值是函数就说的过去啦。


以下三种调用函数(闭包)的方式是等价的哦!
let addTwo = addTo(2) //函数写法
let addTwo = {(_ num) in return num + 2} //闭包写法
let addTwo = {$0 + 2} //简便写法


再举一个例子,我们可以创建一个比较大小的函数:


func greaterThan(_ comparer: Int) -> (Int) -> Bool {
return {$0 > comparer}
}
let greaterThan10 = greaterThan(10)
greaterThan10(13) // => true
greaterThan10(9) // => false

柯里化是一种量产相似方法的好办法,可以通过柯里化一个方法模板来避免写出很多重复代码,也方便了今后维护。


有了之前的分析,上述的代码也就能够驾轻就熟了吧~ return 直接返回的就是一个简写闭包 闭包的意思是,传入闭包的第一个参数大于传入函数的参数。


举一个实际应用时候的例子,在 Selector 一节中,我们提到了在 Swift 中 Selector
只能使用字符串在生成。这面临一个很严重的问题,就是难以重构,并且无法在编译期间进行检查,其实这是十分危险的行为。但是 target-action 又是 Cocoa 中如此重要的一种设计模式,无论如何我们都想安全地使用的话,应该怎么办呢?一种可能的解决方式就是利用方法的柯里化。Ole Begemann 在这篇帖子里提到了一种很好封装,这为我们如何借助柯里化,安全地改造和利用 target-action 提供了不少思路。


protocol TargetAction {
func performAction()
}
struct TargetActionWrapper<T : AnyObject> : TargetAction {
weak var target: T?
let action: (T) -> () -> ()
func performAction() -> () {
if let t = target {
action(t)()
}
}
}
enum ControlEvent {
case TouchUpInside
case ValueChanged
}
class Control {
var actions = [ControlEvent : TargetAction]()
func setTarget<T: AnyObject>(target: T, action: @escaping(T) -> () -> (), controlEvent:ControlEvent) {
actions[controlEvent] = TargetActionWrapper(target: target, action: action)
}
func removeTargetForControlEvent(controlEvent: ControlEvent) {
actions[controlEvent] = nil
}
func performActionForControlEvent(controlEvent: ControlEvent) {
actions[controlEvent]?.performAction()
}
}


这个例子比较复杂,根据模块分为1)TargetAction协议,2)遵守TargetAction协议的TargetActionWrapper结构体,结构体里有两个重要的属性:泛型的target,以及参数为泛型返回值为闭包的函数的action,结构体内的performAction()是遵守协议的函数,功能是当target有值时执行action闭包,3)ControlEvent枚举,4)Control类,类中有一个actions的字典,keys是枚举,value是遵循协议的任意类型,及三个函数(见名知意,就不一一列举了)书中没有例子,我来给大家写一个例子以便理解:


class View {
var backgroundColor = "default"
func changeColorToRed() {
backgroundColor = "red"
}
}
var view = View()
var control = Control()
control.setTarget(target: view/*泛型参数*/, action: view.changeColorToRed, controlEvent: .TouchUpInside)
control.performActionForControlEvent(controlEvent: .TouchUpInside) //执行函数,修改字符串属性。


好了,今天就到这里啦,这个系列和之前一样随时上新,希望对Swift有兴趣的同学们持续关注哟!根据情况,我也会时不时的给各位加餐的哟!




第七城市

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台