最近在学习swift,期间也看了很多教程和大神的博客,最后把笔记放这里,方便查看复习~
附上学习资料地址:w3cschool-Swift 教程 、 YungFan大神的简书(推荐看一下,有些知识点总结地简直通俗易懂)
一、函数
1、介绍
函数就是oc中的方法
常规格式:
func 函数名(参数列表) -> 返回值类型 {
代码块
return 返回值
}
2、多种情况下的格式
1、无参+无返回值
//常规写法
func about() -> Void {
print("iPhone Xs Max")
}
// 调用函数
about()
// 简单写法
// 如果没有返回值,Void可以写成()
func about1() -> () {
print("iPhone Xs Max")
}
about1()
// 如果没有返回值,后面的内容可以都不写
func about2() {
print("iPhone Xs Max")
}
about2()
2、无参+有返回值
func readMessage() -> String {
return "吃饭了吗?"
}
var str = readMessage()
print(str)
3、有参+无返回值
func call(phoneNum : String) {
print("打电话给\(phoneNum)")
}
call("18888888888")
4、有参+有返回值
func sum(num1 : Int, num2 : Int) -> Int {
return num1 + num2
}
var result = sum(num1: 20, num2: 30)
print(result)
5、返回值为复杂类型
//大致意思为,返回值是一个元组类型
func getInfo(info:String) -> (name:String, age:Int) {
let infos = info.components(separatedBy: ",")
//注意,第二个类型要转换一下,并且加!
return (infos[0], Int(infos[1])!)
}
let person:(name:String, age:Int) = getInfo(info: "zhangsan,20")
print(person.name)
print(person.age)
3、注意事项
1、函数的参数是常量;
2、形参标签+形参名称,也就是:外部参数名+内部参数名+类型;
函数参数的常规写法
func addFunc(num1 a: Int,num2 b: Int) -> Int {
return a + b
}
let result = addFunc(num1: 5, num2: 5)
print(result) //10
简写,省略
func addFunc(_ a: Int,_ b: Int) -> Int {
return a + b
}
let result = addFunc(5, 5)
print(result) //10
3、可以设置默认参数(在没有传入参数的情况下);
func makecoffee(type :String = "冰美式") -> String {
return "来一杯\(type)。"
}
let coffee1 = makecoffee(type: "拿铁")
print(coffee1) //来一杯拿铁。
let coffee2 = makecoffee()
print(coffee2) //来一杯冰美式。
4、可变参数:swift中接受参数的个数不确定,但类型要相同;
func total(numbers:Int...) -> Int {
var sum = 0
for i in numbers {
sum += i
}
return sum
}
let res1 = total()
let res2 = total(numbers:10)
let res3 = total(numbers:10,20)
let res4 = total(numbers:10,20,30)
print(res1) //0
print(res2) //10
print(res3) //30
print(res4) //60
5、inout引用类型:一般参数只是传递一个值进来,如果想改变外部参数就要用到inout,类似block中的__block;
func changeInt(a: inout Int, b: inout Int) {
a += 12
b += 100
}
var a = 10
var b = 20
print("调用前:a=\(a), b=\(b)")
//打印:调用前:a=10, b=20
changeInt(a: &a, b: &b)
print("调用后:a=\(a), b=\(b)")
//打印:调用后:a=22, b=120
6、函数的嵌套:内嵌的函数只能在该函数里面调用
4、函数的类型及嵌套函数
1、函数的引用类型 2、每个函数都有自己的类型 3、定义+使用->代码示例 4、嵌套函数(一个函数作为另一个函数的参数 、一个函数作为另一个函数的返回值)
函数类型的使用:
//定义一个函数
func addTwoInts(a : Int, b : Int) -> Int {
return a + b
}
//这个函数的类型是:(Int, Int) -> Int
//于是,我们可以这么使用:
var mathFunction : (Int, Int) -> Int = addTwoInts
//然后可以这么调用
let res = mathFunction(10, 20)
print(res)
一个函数作为另一个函数的参数:
//定义一个函数
func addTwoInts(a : Int, b : Int) -> Int {
return a + b
}
//这个函数的类型是:(Int, Int) -> Int
//作为一个参数传入另一个函数
//参数名: 函数类型
// 将函数的类型作为函数的参数
func printResult(a : Int, b : Int, calculateMethod : (Int, Int) -> Int) {
print(calculateMethod(a, b))
}
//调用自定义函数
printResult(a: 10, b: 20, calculateMethod: addTwoInts)
//也可以调用 手写一个闭包穿进去
printResult(a: 12, b: 22, calculateMethod: {(m: Int, n: Int) -> Int in return m + n})
一个函数作为另一个函数的返回值:
//定义两个函数
func addTwoInts(a : Int, b : Int) -> Int {
return a + b
}
func multiplyTwoInt(a : Int, b : Int) -> Int {
return a * b
}
//函数作为返回值
func getResult(a:Int) -> (Int, Int)->Int{
if a > 10 {
return addTwoInts
}
else{
return multiplyTwoInt
}
}
//调用返回的函数
let res1 = getResult(a: 2)(10,20)
print(res1) //200
let res2 = getResult(a: 12)(10,20)
print(res2) //30
二、闭包
1、介绍
闭包:是一个可以被传递、引用的独立模块;
它能够捕捉、存储 定义在它上下文中的任何常量和变量,也就是说:闭合并包裹 其里面的常量和变量
,因此叫做:闭包
闭包和函数一样,都是引用类型
分为三种形式:
- 全局函数,是一个有名字但不会捕捉任何值的闭包
- 内嵌函数,是一个有名字且可以从上层函数捕捉值的闭包
- 闭包表达式,是一个轻量级语法,可以捕捉其上下文中的常量和变量值的没有名字的闭包
2、与函数比较
计算一个数的特定值,比如平方
函数的写法
func square(num: Int) -> Int{
return num * num
}
//调用
square(num: 5)
闭包的写法
let squareCloure = { (num: Int) -> Int in
return num * num
}
//调用
squareCloure(6)
实战场景
条件
//这是一个函数,它里面有一个参数 需要传入一个函数/闭包
//从数组中筛选指出合适的数据组成新的数组
func getList(score:[Int], con:(Int)->Bool) -> [Int]{
var newScore:[Int] = [Int]()
for item in score {
//把每一个元素传入con方法,根据返回值判断是否执行{}
if con(item) {
newScore.append(item)
}
}
return newScore
}
//已知数组
let scoreArray = [75,60,95,45,85]
函数方式处理
//函数的方式传参,先声明一个函数
func method(num: Int) -> Bool{
return num > 50
}
let newArrayM = getList(score: scoreArray, con: method)
print(newArrayM) //[75, 60, 95, 85]
闭包方式处理
//闭包方式传参,写法1
let closure = {(num: Int) -> Bool in
return num > 50
}
let newArrayC = getList(score: scoreArray, con: closure)
print(newArrayC) //[75, 60, 95, 85]
//闭包方式传参,写法2
let newArrayC2 = getList(score: scoreArray, con:{(num: Int) -> Bool in return num > 50})
print(newArrayC2) //[75, 60, 95, 85]
3、主要知识点
1、由in
关键字,将闭包分成两个部分:参数与返回值、闭包体
2、与函数不同的一点:参数不能设置默认值,其他与函数一样
3、参数名称有缩写功能,通过$0、$1、$2
依次进行参数的调用
4、可以根据不同情况进行简写:
- 1、省略
->
和返回值类型
(根据后面表达式可以推断返回值是一个Bool)
let newArrayC2 = getList(score: scoreArray, con:{(num: Int) in return num > 50})
- 2、省略
参数类型
和括号
(根据函数的参数可推断传进来的必然是Int)
let newArrayC2 = getList(score: scoreArray, con:{num in return num > 50})
- 3、省略
return
(因为是单行语句,可以省略return)
let newArrayC2 = getList(score: scoreArray, con:{num in num > 50})
- 4、通过参数名称缩写,省略
参数声明
和in
let newArrayC2 = getList(score: scoreArray, con:{$0 > 50})
4、捕获
-
闭包可以从上下文环境中捕获常量、变量,并在自己的作用域内使用
-
Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数,嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量
//这里是一个嵌套函数,也可以看成 返回值是一个闭包
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
//在闭包里可以捕获到本方法外的变量
runningTotal += amount
return runningTotal
}
return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
print(incrementByTen())// 返回的值为10
5、尾随闭包
按个人理解就是为了增加可读性,闭包的写法改了个格式
条件:有一个函数,闭包是这个函数的最后一个参数
做法:省略闭包的形参,并把小括号提前,最后使用{}
进行闭包的传递
好处:增加可读性
示例:
//函数,同时 闭包是最后一个参数
func doSomething(info:String, clousre:(String)->Void){
clousre(info)
}
//正常调用
doSomething(info: "Hello", clousre: { str in print(str) })
//使用尾随闭包进行函数调用
doSomething(info: "World") { str in
print(str)
}
6、逃逸闭包
使用@escaping
放在闭包形参类型之前,表明这是一个逃逸闭包,这个闭包会在 函数结束之后
被调用。
用途:逃逸闭包常用于异步回调
//声明一个变量用于测试
var x = 10
//声明一个存放闭包的数组
var closureArray :[()->Void] = [()->Void]()
//为了对比,先定义一个函数,参数是一个正常闭包
func normolClosure(closure:()->Void){
closure()
}
//再定义一个函数,参数是一个逃逸闭包
//把闭包 存储到 闭包数组中,但没有调用。等最后调用数组中的闭包
func escapeClosure(closure: @escaping ()->Void){
print("函数开始")
closureArray.append(closure)
print("函数结束")
}
print("调用函数前,检测 x = \(x)")
/*
调用正常闭包函数
由于闭包是最后一个参数,可以增加可读性,使用尾随闭包
*/
normolClosure {
x = 100
}
//打印100 因为闭包在函数里面执行了
print("调用正常闭包函数后,检测 x = \(x)")
//调用逃逸闭包函数
escapeClosure {
x = 200
}
//此时还是100,因为没有调用 逃逸闭包
print("调用逃逸闭包函数后,检测 x = \(x)")
//在函数外面调用逃逸闭包
closureArray.first?()
print("函数外面调用逃逸闭包,检测 x = \(x)")
/*
打印:
调用函数前,检测 x = 10
调用正常闭包函数后,检测 x = 100
函数开始
函数结束
调用逃逸闭包函数后,检测 x = 100
函数外面调用逃逸闭包,检测 x = 200
*/
7、自动闭包
使用@autoclosure
放在闭包形参类型之前,表明这是一个自动闭包。
用于包装函数参数的表达式,不接受任何参数
,被调用时会返回被包装在其中的表达式的值
//自动闭包
func printIfTrue(predicate:@autoclosure ()->Bool){
if predicate() {
print("is true")
}
else{
print("is false")
}
}
//直接进行调用了,Swift 将会把 2 > 1 这个表达式自动转换为 () -> Bool。这样我们就得到了一个写法简单、表意清楚的表达式。
printIfTrue(predicate: 2>1)
printIfTrue(predicate: 2<1)
8、解决循环引用
方案一:weak
-
通过weak修饰self对当前类进行弱引用
-
self属于可能有值也可能没有值的情况,因此weakSelf是一个可选类型
-
又因为当前self一定存在,否则根本都不在这个方法里,所以在调用的时候可以使用强制解包(!),
weak var weakSelf = self
someView.block = {
//执行当前类的某个方法
weakSelf!.refreshView()
}
方案二:也是weak,属于方案一的简写
someView.block = { [weak self] in
//执行当前类的某个方法
self?.refreshView()
}
方案三:unowned
-
unowned有点类似oc中的unsafe_unretained
-
它表示:即使它原本引用的对象被释放了,扔会保持着对这个已经被释放的对象有一个
无效的引用
,因此它不能是可选值,也不会指为nil
someView.block = { [unowned self] in
//执行当前类的某个方法
self.refreshView()
}