首页 » iOS编程基础:Swift、Xcode和Cocoa入门指南 » iOS编程基础:Swift、Xcode和Cocoa入门指南全文在线阅读

《iOS编程基础:Swift、Xcode和Cocoa入门指南》2.8 函数中的函数

关灯直达底部

可以在任何地方声明函数,包括在函数体中声明。声明在函数体中的函数(也叫作局部函数)可以被相同作用域的后续代码所调用,不过在作用域之外则完全不可见。

对于那些旨在辅助其他函数的函数,这是个非常优雅的架构。如果只有函数A需要调用函数B,那么函数B就可以放在函数A中。

如下示例来自于我所写的应用(仅保留了结构):


func checkPair(p1:Piece, and p2:Piece) -> Path? {    // ...    func addPathIfValid(midpt1:Point, _ midpt2:Point) {    // ...    }    for y in -1..._yct {        addPathIfValid((pt1.x,y),(pt2.x,y))    }    for x in -1..._xct {        addPathIfValid((x,pt1.y),(x,pt2.y))    }    // ...}  

第1个循环(for y)与第2个循环(for x)所做的事情是一样的,不过起始值集合是不同的。我们可以在每个for循环中编写整个功能,不过这么做没有必要,并且会导致重复(这种重复违背了DRY原则,即“不要重复自己”)。为了防止这种重复,我们可以将重复代码重构到实例方法中,然后由两个for循环调用,不过这么做会将该功能所公开的范围扩大,因为它只会由checkPair中的这两个for循环所调用。对于这种情况,局部函数就是很好的折中。

有时,即便函数只会在一个地方调用,使用局部函数也是值得的。如下示例也来自于我所编写的代码(它是同一个函数的另外一个部分):


func checkPair(p1:Piece, and p2:Piece) -> Path? {    // ...    if arr.count > 0 {        func distance(pt1:Point, _ pt2:Point) -> Double {            // utility to learn physical distance between two points            let deltax = pt1.0 - pt2.0            let deltay = pt1.1 - pt2.1            return sqrt(Double(deltax * deltax + deltay * deltay))        }        for thisPath in arr {            var thisLength = 0.0            for ix in 0..<(thisPath.count-1) {                thisLength += distance(thisPath[ix],thisPath[ix+1])            }           // ...        }    }    // ...}  

上述代码的结构很清晰(不过代码使用了尚未介绍的一些Swift特性)。进入函数checkPair中,我有一个路径的数组(arr),我需要知道每条路径的长度。每条路径本身都是点的一个数组,因此为了获取其长度,我需要计算出每两个点之间的距离总和。为了得到两个点之间的距离,我使用了勾股定理。我可以使用勾股定理,在for循环(for ix)中进行计算。不过,我将其作为单独的一个函数distance,这样在for循环中就可以调用该函数了。

这么做并没有减少代码的行数;事实上,声明distance还会增加行数!严格来说,我并没有重复自己;勾股定理会使用多次,不过代码中只出现了一次,位于for循环中。不过,将代码抽象为更加通用的距离计算功能使代码变得更加整洁了:实际上,我是先说明要做什么(要计算两个点之间的距离),然后再去做。函数名distance为代码赋予了含义;这么做相比于直接写出距离计算步骤来说,可理解性与可维护性都更好。

局部函数就是带有函数值的局部变量(本章后面将会介绍这个概念)。因此,局部函数不能与相同作用域中的局部变量同名,相同作用域中的两个局部函数也不能同名。