Block块是随OS X v10.6和iOS 4.0一同发布并可用的功能,其功能类似于C语言的函数。但是与C语言函数不同在于Block可以存取与之在同一个作用域的变量。
2)采用Block指针方式,类似函数调用方式
1)完整格式的写法
//此处仅有闭包参数,因此调用方法的一对小括号被省略
1、Block块/Swift闭包实体定义
- Object-C语法
Block指针定义:
返回值 (^Block名字)(参数列); 如:
int (^cube)(int a)
Block实体定义:
^(参数列表) {行为主体},例如:
^(int a) { return a * a * a; }
- Swift语法
(形参列表) -> 返回值类型,Swift中函数就是闭包,闭包是函数的一种特殊形式。闭包的几种格式为:
1)、完整格式
funA( { () -> () in
print("test")
})
2)、无形参的写法
funA( {
print("test")
} )
3)、闭包为最后一个参数
funA() {
print("test")
}
4)、只有闭包一个参数
funA {
print("test")
}
2、Block块使用例子
1)直接使用,而不用Block指针定义方式
int result = ^(int a) { return a * a * a; }(5); //计算3次幂,将5作为参数传入
NSLog(@"result = %d", result);
这个地方"(5)"后面的分号是必须要有的,表示执行这个Block块代码,即相当于函数调用
2)采用Block指针方式,类似函数调用方式
int (^cube)(int); //声明一个cube的Block指针(是不是很像函数指针,其实意思一样)
cube = ^(int a) { return a * a * a; }; //将Block实体指定给cube
int result = cube(5); //调用cube,看起来比刚才更整洁,易于理解
NSLog(@"result = %d", result); //输出125
3、Swift闭包使用例子
1)完整格式的写法
let cube:(Int) -> Int = { (a) -> Int in
return a * a * a
}
print("result = " + "\(cube(5))")
2)无形参格式的写法
let test = { print("test") }
test()
3)闭包作为最后一个参数的写法
我们首先定义一个带闭包参数的函数,如下:
//传入两个整形数,输出这个两个数之和,加上这两个数之积,得到的结果
func outputResult(a: Int, b: Int, multiply: (Int, Int) -> Int) {
print("a + b + a * b = " + "\(a + b + multiply(a, b))")
}
调用函数outputResult,如下:
//此处最后一个闭包参数,被直接写在outputResult函数调用之外,乍一看还以为是outputResult函数的函数体内容
oupputResult(3, b: 4) { (a, b) -> Int in
return a * b
}
4)闭包参数作为唯一参数的写法
我们首先定义一个仅带唯一闭包参数的函数,如下:
func closuresWithOneParameter(multiply: (Int, Int) -> Int) {
print("multiply a * b = " + "\(multiply)")
}
调用函数closuresWithOneParameter,如下:
//此处仅有闭包参数,因此调用方法的一对小括号被省略
closuresWithOneParameter { (a, b) -> Int in
return a * b
}
4、闭包"逃逸"问题
闭包"逃逸"是指,当一个闭包被作为参数传入函数时,这个闭包在函数返回后才会被执行,即闭包不会在函数体内执行。Swift1.2中增加了@noescape属性来防止闭包"逃逸"问题。
什么情况下,我们可以使用@noescape属性防止闭包参数"逃逸"出函数体呢?通常的原则是,该闭包确保在函数执行结束之后就没有用了,我们可以加@noescape属性。举例如sort(_:)函数,它接收一个排序闭包作为参数来进行排序,排序结束后该闭包肯定就没有用了,这时就可以添加@noescape属性。其声明如下:
public func sort(@noescape isOrderedBefore: (Self.Generator.Element, Self.Generator.Element) -> Bool) -> [Self.Generator.Element]
import UIKit
class ViewController: UIViewController {
var x: Int = 10
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
doSomething()
print("x = " + "\(self.x)")
}
func doSomething() {
funcWithEscapingClosure { () -> Void in
x = 100
}
}
//此处的@noescape强调了closure闭包在函数返回后即无用了,同时可以保证隐式使用self变量
func funcWithEscapingClosure(@noescape closure: () -> Void) {
closure()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
既然闭包可以"逃逸",那么就会存在不可"逃逸"的情况。那么什么情况下闭包不可以"逃逸"呢,答案是函数返回之后,闭包仍然需要使用的情况下。举个例子,如开发过程中最常见的,网络请求,通常都是异步的,等服务器返回结果后,我们通常才会调用成功后的闭包进行诸如界面的刷新工作等。如下代码:
var completionHandlers: [() -> Void] = []
func funcWithEscapingClosure(completionHandler: () -> Void) {
completionHandlers.append(completionHandler) //先将闭包参数加入到数组中,以待将来使用
}
注:在swift的闭包参数中,默认就是可"逃逸"的,无需特殊说明
5、自动闭包
Swift1.2中又增加了另外一个新属性@autoclosures,说白了该属性就是为了在调用闭包时,少写一对{}而已。@autoclosures修饰符使用要求是:
1)修饰无参数的闭包
2)闭包中仅有一条执行语句,多了不灵
定义两个函数,一个带@autoclosures,另一个不带:
func printWithAutoClosureName(@autoclosure nameProvider: () -> String) {
print("name = \(nameProvider())")
}
func printNoAutoClosureName(nameProvider: () -> String) {
print("name = \(nameProvider())")
}
调用语句如下:
var names: [String] = ["张三", "李四"]
printWithAutoClosureName(names.removeAtIndex(0)) //输出"name = 张三"
printNoAutoClosureName { () -> String in
names.removeAtIndex(0) //输出"name = 李四"
}
本文只介绍如何定义和使用Block块/Swift闭包,下一篇重点介绍Block块/Swift闭包中的各种参数内存管理及循环引用等使用过程中需要注意的问题。