标签:
闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似。
闭包采取如下三种形式之一:
闭包一般形式语法:
{ (parameters) -> returnType in statements }
OC中的闭包语法:^ 返回值类型 参数列表 表达式。示例如下:
int (^myBlock)(int) = ^int (int num){ return 3; };
// TODO: 从排序说起 let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]; //定义一个数组 func backwards(s1: String, s2: String) -> Bool { return s1 > s2 } //定义排序规则函数 var reversed = names.sort(backwards) //将排序规则函数作为参数传给sort // TODO: 闭包语法实现 //【注意】: //1:在内联闭包表达式中,函数和返回值类型都写在大括号内,而不是大括号外 //2:闭包的函数体部分由关键字【in】引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。 var reversed2 = names.sort({(s1: String, s2: String) -> Bool in return s1 > s2; }); // TODO: 进化1(根据上下文推断类型):因为排序闭包函数是作为sort(_:)方法的参数传入的,Swift 可以推断其参数和返回值的类型必须是(String, String) -> Bool类型的函数。这意味着(String, String)和Bool类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(->)和围绕在参数周围的括号也可以被省略: //【注意】: //1:实际在任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数或方法时,都可以推断出闭包的参数和返回值类型。 这意味着闭包作为函数或者方法的参数时,几乎不需要利用完整格式构造内联闭包。 //2:但是,如果完整格式的闭包能够提高代码的可读性,则可以采用完整格式的闭包。 var reversed3 = names.sort({s1, s2 in return s1 > s2}); // TODO: 进化2(单行表达式闭包隐式返回):单行表达式闭包可以通过省略return关键字来隐式返回单行表达式的结果 var reversed4 = names.sort({s1, s2 in s1 > s2}); // TODO: 进化3(参数名称缩写):Swift 自动为内联闭包提供了参数名称缩写功能,可以直接通过$0,$1,$2来顺序调用闭包的参数,以此类推。如果在闭包表达式中使用参数名称缩写,那么也可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。【in关键字也同样可以被省略】,因为此时闭包表达式完全由闭包函数体构成: var reversed5 = names.sort({$0 > $1}); // TODO: 进化4(运算符函数):Swift 的String类型定义了关于大于号(>)的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类型的值。而这正好与sort(_:)方法的参数需要的函数类型相符合。因此,可以简单地传递一个大于号,Swift 可以自动推断出想使用大于号的字符串函数实现: var resersed6 = names.sort(>);
func someFunctionThatTakesAClosure(closure: () -> Void) { // 函数体部分 } // 以下是不使用尾随闭包进行函数调用 someFunctionThatTakesAClosure({ // 闭包主体部分 }) // 以下是使用尾随闭包进行函数调用 someFunctionThatTakesAClosure() { // 闭包主体部分 }
类比OC中的Block:
- (void)blockTest:(void(^)(NSString *str))block { block(@"Hello"); } [self blockTest:^(NSString *str) { NSLog(@"%@", str); }];
学习尾随闭包的基本知识后,我们继续对上节的排序进行优化:
// TODO: 进化5(尾随闭包): var reversed7 = names.sort(){$0 > $1}; // TODO: 进化6(尾随闭包):如果函数只需要闭包表达式一个参数,当使用尾随闭包时,甚至可以把()省略掉: var reversed8 = names.sort{$0 > $1}; // TODO: 再看一个示例:将Int类型数组[16, 58, 510]转换为包含对应String类型的值的数组["OneSix", "FiveEight", "FiveOneZero"] let digitNames = [ 0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine" ]; let numbers = [16, 58, 510]; let string = numbers.map(){(number : Int) -> String in var output = ""; var temp = number; while temp > 0 { output = digitNames[temp % 10]! + output; temp /= 10; } return output; } //【说明】 //1.map(_:)为数组中每一个元素调用了闭包表达式; //2.闭包表达式在每次被调用的时候创建了一个叫做output的字符串并返回。 //3.通过尾随闭包语法,优雅地在函数后封装了闭包的具体功能,而不再需要将整个闭包包裹在map(_:)方法的括号内。
// TODO: 先看一个示例:incrementer()函数并没有任何参数,但是在函数体内访问了runningTotal和amount变量。这是因为它从外围函数捕获了runningTotal和amount变量的引用。捕获引用保证了runningTotal和amount变量在调用完makeIncrementer后不会消失,并且保证了在下一次执行incrementer函数时,runningTotal依旧存在。 func makeIncrementor(forIncrement amount: Int) -> () -> Int { var runningTotal = 0; func incrementor() -> Int { runningTotal += amount; return runningTotal; } return incrementor; } let incrementByTen = makeIncrementor(forIncrement: 10); incrementByTen(); //10 incrementByTen(); //20 incrementByTen(); //30 let incrementBySeven = makeIncrementor(forIncrement: 7); incrementBySeven(); //7 incrementByTen(); //40 incrementBySeven(); //14
// TODO: 示例1: class ClosureA { var iTemp = 0; func methodA(@noescape closureTemp: (Void->Void)) { closureTemp(); } func methodB() { methodA {() -> Void in iTemp = 1; //一般的closure都是要self.iTemp = 1,@noescape则不需要 }; } } //【说明】:引用到外部变量的闭包是不能加@noescape标记的。 class ClosureB { var varibleA: (Void -> Void)!; func methodA(closureTemp: (Void->Void)) { self.varibleA = closureTemp; //引用到属性varibleA } } //上面的闭包closureTemp是不能加@noescape标记的。
// TODO: 代码1: var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]; func serveCustomer(customerProvider: () -> String) { print("Now serving \(customerProvider())!"); } serveCustomer({customersInLine.removeAtIndex(0)}); //serveCustomer({(Void) -> String in // return customersInLine.removeAtIndex(0); //}); // TODO: 代码2 var customersInLine2 = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]; func serveCustomer2(@autoclosure customerProvider: () -> String) { print("Now serving \(customerProvider())!"); } serveCustomer2(customersInLine2.removeAtIndex(0)); //【说明】: //1.上面两段代码实现了同一个功能; //2.customerProvider参数自动转化为一个闭包,因为该参数被标记了@autoclosure特性。 //3.过度使用autoclosure会让代码变得难以理解,因此不太推荐应用。
标签:
原文地址:http://www.cnblogs.com/LeeGof/p/5674754.html