本文介绍: 之前在代码书写过程中,我们已经或多或少的接触过函数函数本身也有自己类型,它由形式参数类型返回类型组成。如果将函数传递a变量,那么就可以借助a调用方法,这里addTwoInts代表函数类型。如果出现了同名函数,但是返回值或者参数不一样,那么就需要指明函数类型。否则就会报 ambiguous user of *** 错误,因为这个时候编译器知道要给a赋值一个参数。把函数赋值a,那么a里面储存的是什么呢?运行打印a这里看懂存储的是函数类型.这里函数类型也是引用类型,当把函数赋值给a的时候

1. 函数类型

之前在代码书写过程中,我们已经或多或少的接触过函数,函数本身也有自己的类型,它由形式参数类型,返回类型组成。如果将函数传递给a变量,那么就可以借助a来调用方法,这里addTwoInts代表函数类型。如果出现了同名函数,但是返回值或者参数不一样,那么就需要指明函数类型。否则就会报 ambiguous user of *** 错误,因为这个时候编译器知道要给a赋值一个参数。把函数赋值给a,那么a里面储存的是什么呢?

func addTwoInts(_ a: Int, _ b: Int) -> Int { return a + b
}
var a = addTwoInts
a(10,20)
func addTwoInts(_ a: Double, _ b: Double) -> Double { return a + b
}
func addTwoInts(_ a: Int, _ b: Int) -> Int { return a + b
}
var a: (Double, Double) -> Double = addTwoInts
a(10, 20)
var b = a
b(20 ,30)

运行打印a,这里看懂存储的是函数类型.这里函数类型也是引用类型,当把函数赋值给a的时候,就把函数的metadata赋值给了a.
在这里插入图片描述
这里打印b,看到存储信息是一样的,证明了函数类型是引用类型。
在这里插入图片描述
源码查看函数的metadata,之前的学习可以知道,直接搜索名字functiontype,就找到了函数在源码中的结构体。看到继承自TargetMetadata,那么就会有kind,也就是oc中的isa。其次还有flags
在这里插入图片描述
flags的类型点进去可以看到看到这里enum,这些关键字标注了函数的类型。
在这里插入图片描述
函数类型里面还有一个ResultType,也就是返回值类型。最后就是参数了,这里看到是用连续的内存空间存放参数读取参数是用getParameters方法读取
在这里插入图片描述
模仿出function结构体,这里flags与上掩码可以得到有多少个参数。
在这里插入图片描述
那么这里就可以使用unsafeBitCast将函数转换为TargetFunctionTypeMetadata结构体,然后调用numberArguments方法看到有几个参数了。
在这里插入图片描述

2. 闭包

闭包一个捕获了上下文的常量或者是变量的函数,即使常量或者变量作用域已经不在,闭包仍然可以修改他们。当每次修改捕获值的时候,其实是修改堆区的值。闭包没有block哪有全局,堆,栈的概念了。

在函数makeIncrementer里面定一个函数incrementerincrementer里面使用外部函数的变量runningTotal。当调用makeIncrementer() 复制给a的时候
,明显的可以知道incrementer生命周期makeIncrementer要长,而runningTotal是属于外部函数的变量外部函数返回之后,外部函数就消失了,而内部函数要想使用runningTotal,那么就必须把runningTotal捕获incrementer的内部,这样才能在使用incrementer的使用正确使用runningTotal的值,incrementer和匹配runningTotal的变量,构成了闭包

func makeIncrementer() -> () -> Int {
  var runningTotal = 10
  func incrementer() -> Int {
      runningTotal += 1
      return runningTotal
      
  }
  return incrementer
}
var a = makeIncrementer()

闭包表达式

{ (param) -> (returnType) in //do something
}

OC 中的 Block 其实是一个匿名函数,闭包也可以当成是匿名函数,而这个表达式要具备

var closure : (Int) -> Int = { (age: Int) in return age}

同样的我们也可以把我们的闭包声明一个可选类型:

//错误写法
var closure : (Int) -> Int? closure = nil
//正确写法
var closure : ((Int) -> Int)? closure = nil

还可以通过 let 关键字将闭包声明位一个常量(也就意味着一旦赋值之后就不能改变了)

let closure: (Int) -> Int
closure = {(age: Int) in return age
}
closure = {(age: Int) in return age
}

同时也可以作为函数的参数

func test(param : () -> Int){
    print(param())
}
var age = 10
test { () -> Int in
    age += 1
return age

}

3. 尾随闭包

当我们把闭包表达式作为函数的最后一个参数,如果当前的闭包表达式很⻓,我们可以通过尾随闭包的书写方式提高代码的可读性。

func test(_ a: Int, _ b: Int, _ c: Int, by: (_ item1: Int, _ item2: Int, _ item3: Int) -> Bool) -> Bool {
   return  by(a, b, c)
}
///原结构
test(10, 20, 30, by: {(_ item1: Int, _ item2: Int, _ item3: Int) -> Bool in
    return (item1 + item2 < item3)
})
///后置闭包结构
test(10, 20, 30) {
}

其中闭包表达式是 Swift 语法。使用闭包表达式能更简洁的传达信息。当然闭包表达式的好处 有很多:

var array = [1, 2, 3]
array.sort(by: {(item1 : Int, item2: Int) -> Bool in return item1 < item2 } array.sort(by: {(item1, item2) -> Bool in return item1 < item2 }) array.sort(by: {(item1, item2) in return item1 < item2 }) array.sort{(item1, item2) in item1 < item2 }
array.sort{ return $0 < $1 }
array.sort{ $0 < $1 }
array.sort(by: <)

4. 捕获

回顾一下oc中的捕获值,这里会打印2,1,2,对于block来说,这里会当作自己的变量,捕获i瞬时的值,这个时候外面对i进行改变,不会改变block中的值。如果用 __block修饰,那么就会改变block中变量的值了,这个时候就会打印2,2,2.

- (void)testBlock{
 NSInteger i = 1;
void(^block)(void) = ^{ 
NSLog(@"block %ld:", i);
};
i += 1;
NSLog(@"before block %ld:", i);
 block();
NSLog(@"after block %ld:", i);
}

而对于闭包来说,这里就会打印2,2,2了。这里闭包和block不一样,在闭包中遇到i变量,这里是全局变量,那么闭包就会直接去取i的地址,拿到i的值。如果是在函数中,也就是局部变量,那么就会把变量捕获堆区中,并且变量会成为closure的一部分。而对于引用类型来说,就直接把其在堆区地址存放到闭包的数据结构里面。

在这里插入图片描述
下面会打印11,12,13,这里相当于操作同一个对象

func makeIncrementer() -> () -> Int {
    var runningTotal = 10
    func incrementer() -> Int {
        runningTotal += 1
        return runningTotal
    }
    return incrementer
}

let makeInc = makeIncrementer()

print(makeInc())

print(makeInc())

print(makeInc())

而这里打印的则都是11了,因为这里相当于每次创建一个新的实例对象

func makeIncrementer() -> () -> Int {
    var runningTotal = 10
    func incrementer() -> Int {
        runningTotal += 1
        return runningTotal
    }
    return incrementer
}

let makeInc = makeIncrementer()

print(makeIncrementer()())

print(makeIncrementer()())

print(makeIncrementer()())

5. 闭包本质

6. OC Block 和 Swift闭包相互调用

我们在OC中定义的Block,在Swift中是如何调用的呢?

typedef void(^ResultBlock)(NSError *error);

@interface LSTest : NSObject

+ (void)testBlockCall:(ResultBlock)block;

@end

@implementation LSTest

+ (void)testBlockCall:(ResultBlock)block {
    NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:400 userInfo:nil];
    block(error);
}

@end

在 Swift 中我们可以这么使用

LSTest.testBlockCall { error in
    let errorcast = error as NSError
    print(errorcast)
}

func test(_ block: ResultBlock) {
    let error = NSError.init(domain: NSURLErrorDomain, code: 400, userInfo: nil)
    block(error)
}

比如我们在 Swift里这么定义,在OC中也是可以使用的

class LSTeacher: NSObject {
    @objc static var closure: (() -> ())?
}
+ (void)test {
    LSTeacher.closure = ^{
        NSLog(@"end");
    };
}

7. defer

defer {} 里的代码会在当前代码块返回之后执行,无论当前代码块是从哪个分支 return 的,即使程序抛出错误,也会执行。如果多个 defer 语句出现在同一作用域中,则它们出现的顺序与它们执行的顺序相反,也就是 先出现的后执行。那么defer有什么作用呢?defer一般用来关闭数据库/文件来使用,当打开数据库或者文件,我们需要及时的关掉它,而等一个方法里面的逻辑代码太多,就需要用到defer关闭数据库/文件
在这里插入图片描述
有一点需要注意的是,如果有guard的话,应该defer写在guard前面,否则defer不会执行。
在这里插入图片描述

8.逃逸闭包

逃逸闭包的定义:当闭包作为一个实际参数传递给一个函数的时候,并且是在函数返回之后调 用,我们就说这个闭包逃逸了。当我们声明一个接受闭包作为形式参数的函数时,你可以在形式 参数前写 @escaping 来明确闭包是允许逃逸的。逃逸闭包的三个条件

一般逃逸闭包有2种情况

9. 自动闭包

自动闭包是一种用来把实际参数传递给函数表达式打包的闭包,不接受任何实际参数,当其调用时,返回内部表达式的值。用普通表达式代替闭包的写法语法糖的一种。使用了自动闭包之后,那么就可以传入一个string或者是一个闭包。这里如果传入string就相当于把string用一个闭包包裹起来。

func debugOutPrint(_ condition: Bool , _ message: @autoclosure () -> String){
    if condition {
      print("debug:(message())")
    }
}
debugOutPrint(true,getResult() )
debugOutPrint(true, getResult )

func getResult()->String{
    return "Application Error Occured"
}

原文地址:https://blog.csdn.net/LinShunIos/article/details/125563929

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_26284.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注