1、闭包简介
闭包和OC中的Block非常相似
OC中的Block类似于匿名函数
闭包是用来定义函数
作用: Block是用于保存一段代码, 在需要的时候执行
闭包也是用于保存一段代码, 在需要的时候执行
做一个耗时操作
/*
OC: block类似于匿名函数, 用于封装代码块, 在特点的时候执行
执行一些耗时操作
类型: 返回值类型(^block名称)(形参列表)
值:
^(形参列表){
需要执行的代码
}
Swift: 闭包是用于定义函数(Swift中函数就是闭包, 闭包就是一个特殊的函数)
执行一些耗时操作
类型: (形参列表)->返回值类型
值:
{
(形参列表)->返回值类型
in
需要执行的代码
} // in 的含义是用于区分形参返回值和执行代码
*/
- 在讲解闭包之前,我们先讲解一下OC中的block
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self loadData:^{
NSLog(@"刷新UI");
}];
}
- (void)loadData:(void(^)())finished
{
// __weak : 如果对象释放, 会自动设置为nil
// __unsafe_unretained: 如果对象释放, 不会自动设置为nil
// 1.在子线程中加载数据
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@", [NSThread currentThread]);
NSLog(@"加载数据");
// 2.在主线程中回调, 刷新UI
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@", [NSThread currentThread]);
finished();
});
});
}
2、 闭包基本使用
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/*
闭包的几种格式
1. 完整写法
loadData ({ () -> () in
print("更新UI")
})
2.如果闭包没有形参, 那么in和in之前的代码都可以省略
loadData ({
print("更新UI")
})
3.如果闭包是函数的最后一个参数, 那么闭包可以写在函数()的后面
loadData (){
print("更新UI")
}
4.如果函数只有一个闭包参数, 那么函数的()可以省略
loadData {
print("更新UI")
}
*/
// in是用于区分代码和描述
loadData { () -> () in
print("更新UI")
}
}
func loadData(finished: ()->())
{
dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
print(NSThread.currentThread())
print("加载数据")
dispatch_async(dispatch_get_main_queue(), { () -> Void in
print(NSThread.currentThread())
// 回调通知调用者
finished()
})
}
}
3、闭包的参数和返回值
-
实例:
- 需求: 在控制器的View上添加一个UIScrollview, 然后在UIScrollview上添加15个按钮, 让按钮平铺
1、不使用闭包简单实现
// 1.创建UIScrollview
let sc = UIScrollView(frame: CGRect(x: 0, y: 200, width: UIScreen.mainScreen().bounds.width, height: 50))
sc.backgroundColor = UIColor.redColor()
// 2.通过循环创建15个按钮
let count = 15
let width:CGFloat = 80
let height = sc.bounds.height
for i in 0..<count
{
let btn = UIButton()
btn.setTitle("标题\(i)", forState: UIControlState.Normal)
btn.frame = CGRect(x: CGFloat(i) * width, y: 0, width: width, height: height)
btn.backgroundColor = UIColor.greenColor()
// 3.将按钮添加到UIScrollview上
sc.addSubview(btn)
}
sc.contentSize = CGSize(width: CGFloat(count) * width, height: height)
// 4.将UIScrollview添加到控制器view上
view.addSubview(sc)
- 2、使用闭包封装方法:快速创建
// 2.1 自定义方法:创建子控件并返回UIScrollView
// 技巧: 在编写闭包代码时, 不管三七二十一先写上 ()->(), 然后再修改
/*
形参1 getNumber: ()->Int 指定添加子控件的个数闭包 返回Int类型;
形参2 createView: (index: Int)->UIView) 用来根据index索引来创建子控件返回UIView类型闭包;
函数的返回值:UIScrollView类型
*/
func createScrollview(getNumber: ()->Int, createView: (index: Int)->UIView) -> UIScrollView
{
// 1.创建UIScrollview
let sc = UIScrollView(frame: CGRect(x: 0, y: 200, width: UIScreen.mainScreen().bounds.width, height: 50))
sc.backgroundColor = UIColor.redColor()
// 2.通过循环创建15个按钮
let count = getNumber()
let width:CGFloat = 80
let height = sc.bounds.height
for i in 0..<count
{
/*
let btn = UIButton()
btn.setTitle("标题\(i)", forState: UIControlState.Normal)
btn.frame = CGRect(x: CGFloat(i) * width, y: 0, width: width, height: height)
btn.backgroundColor = UIColor.greenColor()
*/
let subView = createView(index: i)
subView.frame = CGRect(x: CGFloat(i) * width, y: 0, width: width, height: height)
// 3.将按钮添加到UIScrollview上
sc.addSubview(subView)
}
sc.contentSize = CGSize(width: CGFloat(count) * width, height: height)
return sc
}
// 2.1 使用自定义方法创建UIScrollView返回并添加到view视图中
override func viewDidLoad() {
super.viewDidLoad()
// 调用自定义方法:createScrollview(getNumber: ()->Int, createView: (index: Int)->UIView) -> UIScrollView
let sc = createScrollview({ () -> Int in
return 20
}) { (index) -> UIView in
// let btn = UIButton()
// btn.setTitle("标题\(index)", forState: UIControlState.Normal)
// btn.backgroundColor = UIColor.greenColor()
let label = UILabel()
label.text = "标题\(index)!!!"
label.backgroundColor = (index % 2 == 0) ? UIColor.greenColor() : UIColor.purpleColor()
return label
}
view.addSubview(sc)
}
3、闭包循环引用
/*
闭包中使用了外部属性self,就对对其进行强引用,同OC中block一样会出现循环引用的问题,如何解决
OC中如何解决: __weak typeof(self) weakSelf = self;
Swift中如何解决: weak var weakSelf = self
对应关系: __weak == weak __unsafe_unretained == unowned
注意:
__weak : 如果对象释放, 会自动设置为nil
__unsafe_unretained: 如果对象释放, 不会自动设置为nil
*/
import UIKit
class ViewController: UIViewController {
// 在Swift中, 如果在某个类中定义一个属性, 那么这个属性必须要初始化, 否则就会报错
// 如果占时不想初始化, 那么可以在后面写上一个?号
// 注意: 在设置闭包属性是可选类型时一定更要用一个()括住闭包的所有的类型, 否则只是指定了闭包的返回值是可选的
// 错误写法: var callback: ()->()?
var callback: (()->())? // 定义一个闭包属性
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// OC中如何解决: __weak typeof(self) weakSelf = self;
// Swift中如何解决: weak var weakSelf = self
// 对应关系: __weak == weak __unsafe_unretained == unowned
// 1、bug:闭包与self会循环引用的问题出现,两者都无法释放掉。
// 因为:闭包中使用了外部属性self,就对对其进行强引用,同OC中block一样会出现循环引用的问题
loadData {() -> () in
print("被回调了")
// 在Swift开发中, 能不写self就不写slef
// 一般情况下只有需要区分参数, 或者在闭包中使用
self.view.backgroundColor = UIColor.greenColor()
}
// 2、解决方式一:weak解决
// 2.1
weak var weakSelf = self // 注意:weak修饰的weakSelf时可选的,如果我们使用可选类型数据,必须要强制解包 weakSelf!
loadData {() -> () in
print("被回调了")
weakSelf!.view.backgroundColor = UIColor.greenColor() // 可选数据weakSelf 强制解包 weakSelf!
}
// 2.2
// [weak self] ,这样说明闭包里面使用的self不会被强引用了。但是是可选类型,所以我们使用self的时候就需要自己手动强制解包 “!” => self!.view.backgroundColor
loadData {[weak self] () -> () in
print("被回调了")
self!.view.backgroundColor = UIColor.greenColor() // 可选数据weakSelf 强制解包 weakSelf!
}
// 3、解决方式二:unowned解决
// 好处:相对weak, [unowned self] 修饰的self不是可选类型,这样就不用像上面weak修饰的self那样自己手动利用"!"强制解包了。
/*
loadData { [unowned self] () -> () in
print("被回调了")
// 注意:unowned修饰self不是可选类型,一定有值,所以不用手动强制解包了
self.view.backgroundColor = UIColor.greenColor()
}
*/
}
func loadData(finished: ()->())
{
callback = finished
// 1.加载数据
print("加载数据")
// 2.执行回调
finished()
}
// deinit 相当于OC中的dealloc方法
// 只要一个对象释放就会调用deinit方法
deinit
{
print("88")
}
}