当前位置:首页 » 《关注互联网》 » 正文

iOS开发笔记之八十九——Swift中的闭包_iOS开发笔记

24 人参与  2021年08月20日 10:23  分类 : 《关注互联网》  评论

点击全文阅读


*****阅读完此文,大概需要5分钟******

一、闭包的基本概念与写法

1、Swift中闭包需要对比OC的block

  • OC的基本定义与写法:

返回值(^闭包名称)(参数类型 参数名) = ^(参数类型 参数名){函数体};

void(^XXBlock)(int a) = ^(int a){
    NSLog(@"hello");
};
  • Swift基本定义与写法:   

{(参数:参数类型)->返回值类型 in 函数体}如果设置了返回值,记得返回对应类型的值

let test = {(a:Int)->Int in
    return a;
};

个人感觉Swift的闭包写法较为精简并且好记。如果手写,相信很多人应该深有感触,面试时手写block简直是噩梦。相比较而言,Swift的闭包更容易手写出来。

2Swift的闭包实例应用

  • 闭包作为属性的实例应用:
// KXRequest.swift
typealias RequestBlock = (_ name:String)->Void;
var block : RequestBlock?;
func show() {
    self.block?("kx")
}

// ViewController.swift
let request = KXRequest();
request.block = { (name:String) in
    print(name)
};
request.show()
              
  • 闭包作为函数参数
func requestInfo(complection:((Bool, Array<String>) -> Void)? = nil) {
    complection?(false, [])
}

self.requestInfo { [weal self] (sucess, result) in
	guard let self = self else {return}
    If !sucess {
        return 
    }
    print(result)
}

二、后置闭包、自动闭包、逃逸闭包等

1、后置闭包

又称尾随闭包,是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。

let names = ["AT", "AE", "D", "S", "BE"]
var reversed = names.sorted() { $0 > $1 }
print(reversed)
  • 单个后置闭包的应用1:
//单个闭包
UIView.animate(withDuration: 0.3, animations: {() in

})
  • 单个后置闭包的应用2:
class MessageContentModel: AnyObject {
    var timestamp: TimeInterval = 0
    var identifier: String = "MessageModel"
    …
}

let sortedMessageModels = messageModels.sorted(by: {
	$0.timestamp < $1.timestamp
})
  • 多个后置闭包应用1:
//多个闭包
UIView.animate(withDuration: 0.3. animations: { () in 
	// …
}, completion: { (finish) in
	// ….
})
  • 多个后置闭包应用2
let request = ABRequest(info)
request.startWithCompletionBlock { [weak self] (req) in
    guard let self = self, let request = req as? ABRequest else {return}
    self.handleResponce(req: request, userId: info.userId)
} failure: { [weak self] (req) in
    guard let self = self, let request = req as? ABRequest else {return}
    self.handleResponce(req: request, userId: info.userId)
}
//或者
let request = ABRequest(info)
request.startWithCompletionBlock(success: { [weak self] (req) in
    guard let self = self, let request = req as? ABRequest else {return}
    self.handleResponce(req: request, userId: info.userId)
}) { [weak self] (req) in
     guard let self = self, let request = req as? ABRequest else {return}
     self.handleResponce(req: request, userId: info.userId)
}

2、自动闭包@autoclosure

autoclosure顾名思义,这是一种可以“自动”的Closure,即可以让表达式的类型转为相应的Closure的类型,在原本的调用处,可以省略Closure参数的大括号。其只可以修饰作为参数的 Closure,但该 Closure 必须为无参,返回值可有可无。

func kClosureExample(_ compression: () -> Bool) {
    if compression() {
        print("true")
    }
}

kClosureExample { 1 < 2 }

func kAutoClosureExample (_ compression: @autoclosure () -> Bool) {
	if compression() {
		print("true")
	}
}

kAutoClosureExample( 1 < 2 )

3、逃逸闭包与非逃逸闭包

Swift 3.0之后,传递闭包到函数中的时候,系统会默认为非逃逸闭包类型@noescaping,逃逸闭包在闭包前要添加@escaping关键字。它们是根据闭包的调用时的时机划分的。

  • 非逃逸闭包

不需要其它特殊时机,只能在函数作用域内函数执行结束前被调用。非逃逸闭包的生命周期与函数相同:

A. 把闭包作为参数传给函数,

B. 函数中调用闭包,

C. 退出函数,结束。

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.myTest {
            print("非逃逸(noescaping)")
        }
    }
    
    func myTest(callback:()->()) {
        print("before")
        callback()
        print("after")
    }
}
  • 逃逸闭包

不需要在函数结束前被调用,可以等到特定时机时才被调用。逃逸闭包的生命周期:

A. 闭包作为参数传递给函数,

B. 退出函数,

C. 闭包被调用,闭包生命周期结束

class ViewController: UIViewController {

    var escapingCallback : (()->())?

    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.myTest {
            print("逃逸(escaping)")
        }
        
        escapingCallback?()
    }
    
    func myTest(callback:@escaping ()->()) {
        escapingCallback = callback
    }
   
}

给当前类声明一个闭包变量escapingCallback,将myTest函数内的闭包参数赋值给这个变量,此时这个闭包就可以在函数执行结束后被调用。

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.myTest{
            print("逃逸(escaping)")
        }
    }
    
    func myTest(callback:@escaping()->()) {
        print("before”)
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
            callback()
        }
         print("after")
    }
    
}

不需要在myTest函数结束前调用,等到1秒后再调用闭包callback。

三、闭包中的循环引用与解决方法

类似OC的Block,循环引用的方式有三种:

  • 使用weak修饰变量, 打破强引用,实例代码:
weak var weakSelf = self
loadData { (textString) -> () in
    print("\(textString) \(weakSelf?.view)")
}
  • 使用[weak self] 修饰闭包原理跟__weak类似, 这样在闭包中使用self, 就是弱引用
// [weak self]
loadData { [weak self] (textString) -> () in
    //闭包中的self 都是弱引用的
    print("\(textString) \(self?.view)")
}
  • 使用[unowned self ] 修饰闭包, 跟__unsafe_unretained类似, 慎用
loadData { [unowned self] (dataString) -> () in
    print("\(dataString) \(self.view)")
} 
  • [weak self] 与__weak,[unowned self ]与__unsafe_unretained

__weak 表示的是对象的弱引用关系,__weak修饰的对象被释放后,指向对象的指针会自动置为空,也就是指向nil。__unsafe_unretained表示的是弱引用关系,__unsafe_unretained修饰的对象被释放后,指针不会置为空,变成一个野指针,如果后续再访问这个对象可能就会crash。所以[unowned self ]与__unsafe_unretained都要慎用。[unowned self ]与__unsafe_unretained解除循环引用,不被释放, 一般必须是自己的实例属性。

四、与OC的block的不同

上面可以看出OC的block与Swift的闭包基本一致,但是它们也有区别的地方。

  • Swift的闭包和OCBlock里值的捕获的区别

OC代码捕获实例:

NSInteger a = 100;
void(^block)(void) = ^{
    NSLog(@"block = %ld:", a);
};

a += 1;
NSLog(@"out1 = %ld:", a);
block();
NSLog(@"out2 = %ld:", a);

结果打印如下:

2021-08-17 11:27:13.846743+0800 MDProject[30746:23593763] out1 = 101
2021-08-17 11:27:13.846885+0800 MDProject[30746:23593763] block = 100
2021-08-17 11:27:13.847002+0800 MDProject[30746:23593763] out2 = 101

在OC编译器走到第三行的时候,实际上已经完成了对a的拷贝。

Swift代码捕获实例:

var a = 100
let closure = {
    print("closure = \(a)")
}
a += 1
print("out 1 = \(a)")
closure()
print("out 2 = \(a)")

结果打印如下:

out1 = 101
closure = 101
out 2 = 101

Swift闭包都是捕获的是“引用”,而不是他们引用的对象。

  • 闭包中修改值:
var a = 100
let closure = {
    a += 1
     print("closure = \(a)")
}
a += 1
print("out 1 = \(a)")
closure()
print("out 2 = \(a)")

在闭包里多了一行 a += 1 编译,没有警告,运行结果如下:

out 1 = 101
closure = 102
out 2 = 102

也就是说,在Swift里,闭包就像是oc给外部变量默认添加了__block或者__weak

  • Swift里的捕获列表(capturing list)

Swift里实现和OC一样的值捕获,看如下代码:

var a = 100
let closure = {
    [a] in
    print("closure = \(a)")
}
a += 1
print("out 1 = \(a)")
closure()
print("out 2 = \(a)")

闭包内部多了一行[a] in 语法为中括号[]里面添加捕获的变量,然后用in 分割上下分。结果打印如下:

out 1 = 101
closure = 100
out 2 = 101

五、参考文档

以上部分片段摘录自以下文档:

https://www.jianshu.com/p/33707bd27500

https://www.jianshu.com/p/61ee76234357

https://blog.csdn.net/qq_30932479/article/details/80517518

https://www.jianshu.com/p/cbd0b7390bcf

https://www.jianshu.com/p/65d0d6a1ced1

https://www.dazhuanlan.com/abiosis/topics/1242173

https://www.runoob.com/swift/swift-closures.html

感谢作者们的分享。


点击全文阅读


本文链接:http://zhangshiyu.com/post/25806.html

函数  逃逸  调用  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1