- 闭包和OC中的Block差不多,也是保存一段代码,在适当的时候执行,一般用于一些耗时操作,也可以传递值
- 区别:block类似于匿名函数,而闭包就是用来定义函数的,Swift中的函数其实就是闭包
- 注意:Swift中,要求一个类的属性必须有初始值,如果没有,可以写个问号表示是可选类型。也就是说,如果类的属性不是可选类型,那么必须有初始化值
- 所以,在viewController中定义一个闭包,也必须有个初始值,或者加个问号。
- 注意问号的位置,如果直接放在了后面一个括号后面,表示返回值是可选类型,而我们现在要表示闭包是可选类型。所以要把闭包整体括起来,把问号放在后面
// 定义一个闭包属性(类似OC的strong属性)
// 返回值为空,那么可以用()代替
var finished: (() -> ())?
- 定义完属性,在viewDidLoad中给他初始化
- 格式:
//类型的格式:(形参列表) -> 返回值类型
//值的格式:
{
(形参列表) -> 返回值类型
in // in的作用是分隔需要执行的代码
需要执行的代码
}
self.finished = {
() -> ()
in
print("我被调用了")
}
// 调用,此处加叹号是因为必须保证闭包中有值才能调用(类似OC中block也必须先判断block是否有值)
self.finished!()
- 注意点
- Swift中self用的比较少,一般仅用于闭包中,所以看到self就要想起闭包
具体使用
dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
print("执行了耗时操作")
print(NSThread.currentThread())
dispatch_async(dispatch_get_main_queue(), { () -> Void in
print("更新了UI")
print(NSThread.currentThread())
})
}
- 用闭包封装一个创建scrollView的方法,并且在scrollView里创建子控件。创建的子控件类型和个数由调用者决定
- 这个示例中,闭包的返回值由调用者传给方法,而闭包中的参数是由方法传给调用者
func creatScrollView(btnCount: ()-> Int, btnWithIndex: (index:Int) ->UIView) -> UIScrollView
{
let sc = UIScrollView(frame: CGRect(x: 0, y: 100, width: 375, height: 44))
sc.backgroundColor = UIColor.redColor()
let count = btnCount()
for i in 0..<count
{ // subView由调用者决定,而index由这里决定并传给调用者
let subView = btnWithIndex(index: i)
sc.addSubview(subView)
sc.contentSize = CGSize(width: CGFloat(count) * subView.bounds.width, height: 44)
}
return sc
}
// 调用
let sc = creatScrollView({ () -> Int in
return 15
}) { (index) -> UIView in
let width = 80
let btn = UIButton()
btn.backgroundColor = UIColor.greenColor()
btn.setTitle("标题\(index)", forState: UIControlState.Normal)
btn.frame = CGRect(x: index * width, y: 0, width: width, height: 44)
return btn
}
view.addSubview(sc)
闭包的简写
func loadData(finished: ()->())
{
print("执行了耗时操作")
// 调用闭包
finished()
}
loadData ({ () -> () in
print("耗时操作执行完毕")
})
- 如果闭包没有参数,那么in和in之前的闭包格式符号可以省略
- 如果闭包是函数形参列表的最后一个形参,那么可以把闭包写到圆括号后面
- 如果形参列表只有闭包一个参数,那么圆括号可以省略(系统就是这么做的)
- 于是调用上面那个函数就变成了这样
loadData {
print("耗时操作执行完毕")
}
闭包的循环引用
- 在闭包中使用外接对象,为了在调用闭包的时候保证这个对象没有被释放,必须对这个外界对象加一个self.意思是对其进行强引用
- 如果把一个闭包保存为控制器的属性,在这个闭包中又调用了控制器的view,用到了self(控制器),那么就会导致强引用循环
- 类似于OC,设置一个weakSelf即可解决这个问题
- 注意在闭包中使用weakSelf的时候要加问号。因为是weak的表示弱引用,随时可能释放,可能是nil;而只要有可能是nil的对象必须是可选类型。可以直接强制解包告诉它一定有值,问号改成感叹号
weak var weakSelf = self
loadData ({ () -> () in
print("耗时操作执行完毕")
weakSelf!.view.backgroundColor = UIColor.redColor
})
- 还有个写法:可以在闭包的形参列表圆括号前加上
[weak self]
,表示在此闭包中调用的self都是weak的
loadData ({ [weak self] () -> () in
print("耗时操作执行完毕")
self!.view.backgroundColor = UIColor.redColor
})
- 解决方式二:用__unsafe_unretained
- OC中weak的特点:如果对象被释放,会自动赋值为nil
- OC中__unsafe_unretained的特点:如果对象被释放,不会赋值为nil,而是指向了一块坏内存。可以用这个来解决block的循环引用问题
- Swift可以用
unowned
来达到相同的效果。注意不用加问号或者叹号了,因为释放后不会是nil
loadData ({ [unowned self] () -> () in
print("耗时操作执行完毕")
self.view.backgroundColor = UIColor.redColor
})