swift (2)


# 函数

swift的函数语法算是比较独特的了, 而且它还很重要哦, 因为最终你所有的代码都是要归结到某个函数里,也就是所谓的` action`.

##函数参数和返回值

```
func sum (_ x:Int, _ y:Int) -> Int {
  let result = x + y
  return result
}
```

看上去很不错,而且没有语法错误,但是你并没有真正懂得它到底发生了什么. 下面我们来断行解析一下:

```
func sum                //①
  (_ x:Int, _ y:Int)    //②③
  -> Int {              //④⑤
    let result = x + y  //⑥
    return result       // ⑦
    
}
```

## 没有返回值和参数

函数可以没有返回值和参数. 但是调用函数的时候会别忘记了`()`.
其实函数没有返回值并不是说它什么都没有返回,而是返回额一个` Void` 的类型的值.

## 函数签名

定义函数的时候一时想不起好名字完全可以省略,而是只有它实际的类型:

```
(Int, Int) -> Int
```

具有相同的参数类型以及返回值,他们就是`函数类型`. 

**一个合法的函数签名必须具有参数和返回值部分,如果它没有参数和返回值,那简写为

- (Void) -> Void
- () -> ()


## 外部参数名

一个函数可以给它的参数指定外在的名字, 这样在调用这个函数的时候,就需要给它的参数带上这个外在的名字. 这样做有几个好处就是:

- 它能够说明这个参数的目的. 通过这个参数的标签就能够简单说明它是什么动作;
- 区别于其他的函数
- 更好的和 oc 或者 cocoa 协作.

对于 swift 来说默认的所有参数都是自动被`参数外部化`. 所以当如果内部名和外部名一样的话那就说明都不需要做,而如果和默认的行为不一样就需要:

- 修改外部名

  如果想要修改外部名, 那就在外部名和内部名之间加一个空格.
  
- 抑制外部名的产生

  这个情况直接通过使用`下划线空格`的形式.
  
```

func funcwithexternal(shit s:String) -> String {
    return s
}

let s = funcwithexternal(shit: "hello, world");

print(s)

func funcwithsupresexternal(_ s:String, shit ss:String) -> String  {
  return s+ss

}
let ss = funcwithsupresexternal("hello", shit:"world")
print(ss)


```

**参数名**

## 重载

swift 支持函数的重载, 也就是说可以具有相同的签名和返回类型:

```

func say (_ what:String) {
}
func say (_ what:Int) {
}

```

> 对于返回值,需要明确的它的类型.

如果我们同时定义了:

```
func say() -> String {
    return "one"
}
func say() -> Int {
    return 1
}
```

而在调用的时候直接这样` let result = say()` 编译器就汇报哦错.

而要是这样:

```
func giveMeAString(_ s:String) {
    print("thanks!")
}
```

我们把`giveMeAString(say())` 作为参数传入` giveMeString` 就会没事.

## 默认参数值

```
class Dog {
    func say(_ s:String, times:Int = 1) {
        for _ in 1...times {
            print(s)
        }
    }
}

```

我们可以这样调用它:

```
let d = Dog()
d.say("woof")
```

## 可变参数

```
func sayStrings(_ arrayOfStrings:String ...) {
    for s in arrayOfStrings { print(s) }
}
```

这样使用:

```
sayStrings("hey", "ho", "nonny nonny no")
```

##可忽略参数

如果一个参数名是下划线 `_`那么它就是一个忽略的参数.但是调用的时候是需要给它一个值的. 例如这样一个函数:

```
func say(_ s:String, times:Int, loudly _:Bool)
```

其他在函数体内没有使用这个` loudly`名的参数, 但是在调用这个函数的时候还是要传递.

```
say("hi", times:3,loudly:true)
```
同样如果我们不希望给它参数名的情况下还是要传递:

```
func say(_ s:String, times:Int, _:Bool) {
//But the caller must still supply it:

say("hi", times:3, true)
```

为什么要这样做呢? 虽然编译器并不会抱怨一个参数在函数体内没有使用,我只是用它来做一个标记提醒.

## 可修改参数

```
func removeCharacter(_ c:Character, from s: inout String) -> Int { // ① inout 的使用
    var howMany = 0
    while let ix = s.characters.index(of:c) {
        s.remove(at:ix)
        howMany += 1
    }
    return howMany
}
```

调用的时候:

```
var s = "hello"
let result = removeCharacter("l", from:&s)
```

这样,我们通过`&`在传递` s`的时候直接使用了它的应用,在函数体内直接修改它的值, 结果就是得到了结果还同时修改了 s 的值.

>通常我们会说类是引用类型,而其他的类型是值类型的.当传递一个` struct` 的类型作为参数它就是先回拷贝一个值然后传递.

## 函数里的函数

函数可以定义在任何地方,包括函数体内, 它对外界是完全不可见的. 函数体内定义的函数可以在当前的作用域于之后使用.
这个设计对于函数来说非常漂亮, 因为它可以用来辅助其他的函数,如果一个函数只有一个函数调用那就可以把它最为内部函数写进去.

## 递归

略

## 函数作为值

看这段代码:

```
func doThis(_ f:()->()) {
    f()
}
```

这个` doThis` 函数需要一个参数,没有返回值,而参数是一个`函数`, 因为有`()->()`(无参数无返回值的函数)函数的签名在. 也就是说` doThis` 接受一个函数作为参数,然后在内部调用这个函数.
如何具体使用这个` doThis` 函数呢, 其实就是给他一个不需要参数没有返回值的函数就好了:

```
func doThis(_ f:()->()) {
    f()
}
func whatToDo() {
    print("I did it")
}
doThis(whatToDo)
```

## 匿名函数

想想之前的函数作为参数使用的例子, 

```
func whatToAnimate() { // self.myButton is a button in the interface
    self.myButton.frame.origin.y += 20
}
func whatToDoLater(finished:Bool) {
    print("finished: \(finished)")
}
UIView.animate(withDuration:
    0.4, animations: whatToAnimate, completion: whatToDoLater) //①

```
 
 - ① 这里我们每次都要把函数名字写全了这样是不是很烦.

我们需要匿名函数.

```
UIView.animate(withDuration:0.4,
    animations: {
        () -> () in
        self.myButton.frame.origin.y += 20
    },
    completion: {
        (finished:Bool) -> () in
        print("finished: \(finished)")
    }
)
```

再来个例子感受下:


```
func drawing() {
    let p = UIBezierPath(
        roundedRect: CGRect(x:0, y:0, width:45, height:20),
        cornerRadius: 8)
    p.stroke()
}
let image = imageOfSize(CGSize(width:45, height:20), drawing)
```

通过匿名函数:


```
let image = imageOfSize(CGSize(width:45, height:20), {
    () -> () in
    let p = UIBezierPath(
        roundedRect: CGRect(x:0, y:0, width:45, height:20),
        cornerRadius: 8)
    p.stroke()
})

```


## 定义执行一体

swift 中同样提供了一个非常实用的匿名函数写法并且立即执行的写法:

```
{
   // code goes here
}()
```
>有没有点 javascript 里的意思

通常我们都是这样写的:


```
let para = NSMutableParagraphStyle() //①
para.headIndent = 10
para.firstLineHeadIndent = 10
// ... more configuration of para ...
content.addAttribute( // content is an NSMutableAttributedString
    NSParagraphStyleAttributeName,
    value:para,  //②
    range:NSRange(location:0, length:1))
    
```

- ①② 都是分别先取值然后赋值

而通过立即执行的写法:


```
content.addAttribute(
    NSParagraphStyleAttributeName,
    value: {
        let para = NSMutableParagraphStyle()
        para.headIndent = 10
        para.firstLineHeadIndent = 10
        // ... more configuration of para ...
        return para
    }(),
    range:NSRange(location:0, length:1))


```




## 闭包

swift 的函数式闭包,也就是说它可以截取到同一个作用域里函数体内的外部变量的引用.

```
class Dog {
    var whatThisDogSays = "woof"
    func bark() {
        print(self.whatThisDogSays)
    }
}
```

上面的代码意思是说函数` bark` 它能够拿到` whatthisdogsays` 而这个变量是相对于` bark` 的外部的函数体内.因为它定义在想对于它的外部,而他们又是在同一个作用域. 因为在` bark` 内部是可以看到这个外部的变量.
而且内部的函数体引用了这个变量. 看上去不错哦, 因为我们知道其实` bark` 是可以作为值传递,也就是说它可以从一个环境转移到另外的环境. 发生了什么或者针对` whatThisDogSays` 的引用发生了什么变化呢.

```
func doThis(_ f : (Void) -> Void) {
    f()
}
let d = Dog()
d.whatThisDogSays = "arf"
let barkFunction = d.bark
doThis(barkFunction) // arf
```

执行代码,发现最后得到的结果是 **arf**. 可能从结果上看并没有什么意外的,但是到底发生了什么, 我们并没有直接去滴啊用`d.bark()`,我们实例化了`Dog` 的实例,然后把它的` bark` 函数作为值传递给了` doThis`.
然后调用` doThis`, 而` whatThisdogsays` 他是一个 **具体的狗的实例的属性**, 而在` doThis` 的函数体内没有` whatthisdogsays`. 事实上在 doThis 的内部并没有 Dog 实例的存在. 尽管如此调用函数`f()` 还是可以工作, 也就是函数`d.bark`它可以访问到变量` whatthisdogsays`. 即使它自己的执行环境里并没有 Dog 实例或者任何的实例属性 whatthisdogsays.

但是不止于此,如果把` d.whatthisdogsays = "arf" `挪到 `let barkFunction = d.dark` 之后呢, 结果还是一样的.

```
func doThis(_ f : (Void) -> Void) {
    f()
}
let d = Dog() //①
let barkFunction = d.bark //②
d.whatThisDogSays = "arf" // ③
doThis(barkFunction) // arf  //④
```

- ①的时候 d 是一个 Dog 的实例,而这个时候 d.whatthisdogsays 是 woof
- ②的时候把 d.bark 赋值给一个变量
- ③的时候将 whatthisdogsays 赋值新的值
- ④的时候实际是将 d 实例的函数执行了.而 **执行的上下文环境**就是对应的 d 这个实例.

也就是说②这里的定义的变量barkFunction它是一直维护者对应真实的 Dog (实例)的引用. 而不管后来在什么环境下去代用执行barkFunction,它都维护者对应这个实例的引用.
这里说的`截取(capture)` 也就是说当将一个函数作为值传递使用的时候,它始终保持着对于外部变量的引用. 这个用法就是将函数作为闭包.


## 怎么使用闭包来优化代码


## 函数返回函数

```
func makeRoundedRectangleMaker(_ sz:CGSize) -> () -> UIImage { // 1
    func f () -> UIImage { // 2
        let im = imageOfSize(sz) {
            let p = UIBezierPath(
                roundedRect: CGRect(origin:CGPoint.zero, size:sz),
                cornerRadius: 8)
            p.stroke()
        }
        return im
    }
    return f // 3
}
```

Let’s analyze that code slowly:

1
The declaration is the hardest part. What on earth is the type (signature) of this function makeRoundedRectangleMaker? It is (CGSize) -> () -> UIImage. That expression has two arrow operators. To understand it, keep in mind that everything after each arrow operator is the type of a returned value. So makeRoundedRectangleMaker is a function that takes a CGSize parameter and returns a () -> UIImage. Okay, and what’s a () -> UIImage? We already know that: it’s a function that takes no parameters and returns a UIImage. So makeRoundedRectangleMaker is a function that takes a CGSize parameter and returns a function — a function that itself, when called with no parameters, will return a UIImage.

2
Now here we are in the body of the function makeRoundedRectangleMaker, and our first step is to declare a function (a function-in-function, or local function) of precisely the type we intend to return, namely, one that takes no parameters and returns a UIImage. Here, we’re naming this function f. The way this function works is simple and familiar: it calls imageOfSize, passing it an anonymous function that makes an image of a rounded rectangle (im) — and then it returns the image.

3
Finally, we return the function we just made (f). We have thus fulfilled our contract: we said we would return a function that takes no parameters and returns a UIImage, and we do so.


让我们来分析一下上面的这段代码先:

- 首选函数名就是`makeRoundedRectangleMaker`, 但是`(_ sz:CGSize) -> () -> UIImage `这个怎么解释呢? 首选记住一点的就是每一个剪头的后面都是 ** 返回值**. 所以可以先简单的认为这个函数拿`(_ sz:CGSize)`做参数,然后返回了`() -> UIImage`, 但是它是什么呢, 我们按照之前说的就是它不要参数而返回一个` UIImage`.

> 重新来, 就是这个函数``makeRoundedRectangleMaker`它拿`(_ sz:CGSize)`作为参数,然后返回来一个 **函数**, 这个函数呢不需要参数,而返回一个 **UIImage**.

- 现在我们到了函数`makeRoundedRectangleMaker`体内部,我们第一步就是定义一个函数具有我们想要的返回值, 也就是说不需要参数而返回一个` UIImage`, 这里函数名定为` f`, 这个函数很简单,它调用` iamgeOfSize` 函数,然后将它传递给一个匿名函数,它会创建一个圆角的长方形,然后返回这个 image.

- 最后我们返回这个函数` f`. 我们已经完成了我们的约定就是返回一个函数,它不需要参数,而返回一个 UIImage.

## 闭包设置一个捕捉的变量


## 闭包提供一个捕捉的环境


## 封装闭包

## curried 函数(柯里化函数)


```
func makeRoundedRectangleMaker(_ sz:CGSize) -> () -> UIImage {
    return {
        imageOfSize(sz) {
            let p = UIBezierPath(
                roundedRect: CGRect(origin:CGPoint.zero, size:sz),
                cornerRadius: 8)
            p.stroke()
        }
    }
}
``` 

我不喜欢上的写法,因为圆角的值是通过参数` sz` 来的,而`cornerRadius`确实硬编码进去的. 我想要把它替换掉,这里有2个方法可以事先.第一个方法就是简单地将它作为一个参数传入:

```

func makeRoundedRectangleMaker(_ sz:CGSize, _ r:CGFloat) -> () -> UIImage {
    return {
        imageOfSize(sz) {
            let p = UIBezierPath(
                roundedRect: CGRect(origin:CGPoint.zero, size:sz),
                cornerRadius: r)
            p.stroke()
        }
    }
}
```

所以我们调用的时候可以是这样子的:

```
let maker = makeRoundedRectangleMaker(CGSize(width:45, height:20), 8)
```

第二种方法就是:


```
func makeRoundedRectangleMaker(_ sz:CGSize) -> (CGFloat) -> UIImage {
    return {
        r in
        imageOfSize(sz) {
            let p = UIBezierPath(
                roundedRect: CGRect(origin:CGPoint.zero, size:sz),
                cornerRadius: r)
            p.stroke()
        }
    }
}
```

```
let maker = makeRoundedRectangleMaker(CGSize(width:45, height:20))
self.myImageView.image = maker(8)
```




## 函数引用和选择器

## 函数引用和作用域


## 选择器