同一个方法中只能有一个可变参数,而且不限制可变参数在所有参数中的位置。在OC中可变参数只能作为方法中参数的最后一个。
func sum(input: Int...) -> Int {
return input.reduce(0, +)
}
sum(input: 1,2,3,4,5) // 15
inout
func switching(a: inout Int, b: inout Int) {
(a, b) = (b, a)
}
var a = 10
var b = 20
switching(a: &a, b: &b)
inout
表示Swift的可修改参数。而且在这个例子中,通过tuple
是a和b的值直接进行了交换,而在OC中我们还需要使用一个第三者变量来进行进行临时存储。
柯里化
(Currying)是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回一个新的函数,这个函数能够处理剩余参数。
你可以理解它为一种返回值为闭包的函数,或者理解为一种函数对函数的调用。
看下面的例子:
func someFunc(_ index: Int) -> (Int) -> Int {
return { num in
return num + index
}
}
let newFunc = someFunc(10)
let result1 = newFunc(5) // 15
let result2 = someFunc(9)(8) // 17
再来看一个比大小的例子:
func firstMoreThanSecond(_ num: Int) -> (Int) -> Bool {
return {$0 > num}
}
let result3 = firstMoreThanSecond(10)(11) // false
let result4 = firstMoreThanSecond(8)(10) // true
柯里化带来的优势:
对于柯里化,它可以将你的函数碎片化,将函数分层调用,所以提高了代码灵活性、复用性、降低依赖,而且代码也会相对简洁。
总之,柯里化可以很好地体现出函数式编程思想。
最后借用Instance Methods are “Curried” Functions in Swift中的一个例子,体会体会好坏。
protocol TargetAction {
func performAction()
}
struct TargetActionWrapper: 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(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()
}
}
class DCSnail {
func excute() {
let button = Control()
button.setTarget(target: self, action: DCSnail.tapAction, controlEvent: .TouchUpInside)
button.performActionForControlEvent(controlEvent: .TouchUpInside)
}
func tapAction() {
print("tapped!!!")
}
}
DCSnail().excute()
这里所说的函数嵌套与柯里化中所提到的函数分层调用不是一个概念,而是在函数中继续定义函数。
来看下面一个函数:
func generateObjec(type: Int) -> String {
if 0 == type {
return zeroType()
} else if 1 == type {
return oneType()
} else {
return defaultType()
}
}
func zeroType() -> String {
return "Zero"
}
func oneType() -> String {
return "One"
}
func defaultType() -> String {
return "Two"
}
如果使用函数嵌套将会如下效果:
func generateObjec(type: Int) -> String {
func zeroType() -> String {
return "Zero"
}
func oneType() -> String {
return "One"
}
func defaultType() -> String {
return "Two"
}
if 0 == type {
return zeroType()
} else if 1 == type {
return oneType()
} else {
return defaultType()
}
}
函数嵌套在你函数主体内容过长,且本模块的功能与外部逻辑没有任何关系时,能发挥非常大的作用。它会使你的单个函数不在冗长,且将它们分成几个小型的模块,且定义在主函数之内,并不影响外部的关系。
所以这样的访问权限和这样的模块化会提高代码可读性和维护性。
class A {
func block() {
//...
}
}
class B {
var letter: A?
}
let b = B()
let optionResult = b.letter?.block() // nil
B类中有个可选类型的letter
变量,当通过可选链对其函数进行调用时,将会得到nil
。这是毋庸置疑的,那么继续看:
let bBlock1 = {(b: B) -> () in
b.letter?.block()
}
let bBlock2 = {(b: B) -> ()? in
b.letter?.block()
}
bBlock1(b) // ()
bBlock2(b) // nil
新建了两个block,它们的返回值分别为()
和()?
,最后将b
传入得到的结果却是分别为()
和nil
。这个结果够不够出乎你的意料?
在包含可选链的block中,返回值是()
或者Void
的,返回值永远都不为nil
;而返回值是()?
或者Void?
返回值为nil
。所以虽然()
和Void
是一回事,但在这里并不是一回事。
@autoclosure
假如有一个尾随闭包参数的函数,只是根据闭包的返回值判断真假:
func trueOrFalse(_ condition: () -> Bool) {
if condition() {
print("pass")
}
}
那么你可以定义个闭包来进行传值:
let conditionBlock = { () -> (Bool) in
return 2 > 1
}
你也可以直接使用尾随闭包的属性,而且还可以让闭包从语境中推断类型,一直简化:
trueOrFalse({ return 2 > 1 })
trueOrFalse({ 2 > 1 })
最后一直简化成这样:
trueOrFalse{ 2 > 1 }
好了,引入正题,将入使用了@autoclosure
对闭包进行修饰:
func trueOrFalse2(_ condition: @autoclosure () -> Bool) {
if condition() {
print("pass")
}
}
我们可以这样使用:
trueOrFalse2(2 > 1)
这是因为,@autoclosure
修饰的闭包可以自动将 2>1
转换为()->bool
,以供函数使用。
@escaping
func perform(block: ()->()) {
block()
}
func performAsync(block: @escaping ()->()) {
DispatchQueue.main.async {
block()
}
}
class Test {
var ID = "id"
func method1() {
perform {
print(ID)
}
print("before method1")
ID = "method1"
}
func method2() {
performAsync {
print(self.ID)
}
print("before method2")
ID = "method2"
}
func method3() {
performAsync {
[weak self] in
print(self?.ID ?? "nil")
}
print("before method3")
ID = "method3"
}
}
Test().method1() // id
Test().method2() // method
Test().method3() // nil
在形式参数前写 @escaping
来明确闭包是允许逃逸的,闭包可以逃逸的一种方法是被储存在定义于函数外的变量里。所以一定要注意启动异步任务回调所带来对调用时机的不同,要在合适的作用域和时机中对持有的变量进行使用。
另外,关于@escaping
还需要注意,当你在协议或者父类中包含一个@escaping的闭包,那么在实现协议或者父类的子类中,该方法也必须被声明为@escaping,否则两个方法会被认为为两个函数。
Swift 为我们提供了一组非常有用的接口,用来将字面量转换为特定的类型。
ExpressibleByArrayLiteral
ExpressibleByBooleanLiteral
ExpressibleByDictionaryLiteral
ExpressibleByFloatLiteral
ExpressibleByIntegerLiteral
ExpressibleByStringLiteral
ExpressibleByUnicodeScalarLiteral
ExpressibleByExtendedGraphemeClusterLiteral
以ExpressibleByStringLiteral
为例,来自定义字符串的字面量:
class Dog: ExpressibleByStringLiteral {
let name: String
init(name value: String) {
self.name = value
}
required convenience init(stringLiteral value: String) {
self.init(name: value)
}
required convenience init(extendedGraphemeClusterLiteral value: String) {
self.init(name: value)
}
required convenience init(unicodeScalarLiteral value: String) {
self.init(name: value)
}
}
let dog: Dog = "Husky"
dog.name // Husky
相关资料:
Swift 之关键字总结上篇
Swift 之关键字总结下篇