Swift 关键字整理

174 阅读4分钟

mutating 异变方法

Swift中class和struct都能定义方法,但是有一点区别是默认情况下,值类型属性 不能被自身的实例方法修改。 如下:

    struct Point{
        var x = 0.0
        var y = 0.0
        //不允许这么操作,会报错
        func moveBy(x deltaX:Double,y deltaY:Double){
            x += deltaX
            y += deltaY
        }
        // 使用 mutating
        mutating func  moveBy2(x deltaX:Double,y deltaY:Double){
            x += deltaX
            y += deltaY
        }
    }
    

异变方法的本质: image.png 对于异变方法,传入的self 被标记为inout 参数,无论mutating方法内部发生什么, 都会影响外部依赖类型的一切

inout 输入输出参数

如果我们想函数能够修改一个形式参数的值,而且希望这些改变在函数结束之后依然生效, 那么我们需要将形式参数定义为输入输出形式参数。在形式参数定义开始的时候在前面 添加一个inout 关键字可以定义一个输入输出参数。

var x = 10
modifyAge(age:&x)
print(x) // 11
//形式参数默认是let 不使用inout  age +=1 会报错
func modifyAge(age: inout  Int){
   age += 1
}

final

使用它修饰的变量、方法、类不可继承。

private

访问级别所修饰的属性或者方法只能在当前类里访问

截屏2022-01-01 22.21.57.png

fileprivate

所修饰的属性或者方法在当前的源文件里可以访问 同一个文件内访问,跨文件不能访问

截屏2022-01-01 22.28.29.png

截屏2022-01-01 22.28.39.png

lazy 延迟存储属性

class User{
    lazy var age:Int = 20
}
let u = User()
print(u.age)

lazy 修饰的属性初始化时机

image.png

通过上面可以看出,lazy修饰的属性,在没有使用的时候并没有被赋值,存储的是0 使用后赋值18

  • 使用 lazy 可以定义一个延迟存储属性,在第一次用到属性的时候才会进行初始化。
  • lazy 属性必须是 var,不能是 let,因为 let 必须在实例的初始化方法完成之前就拥有值。

通过sil分析延迟属性的原理

截屏2022-01-08 13.41.26.png 通过观察可以发现,lazy属性会被声明称可选类型,也就是说,lazy属性在初始化是会被赋值为Optional.none,另外,lazy属性会被标记为final,所以不能被重写

lazy属性的getter调用

image.png 通过上图可以看到,有值的话直接调用bb1,没有值的话调用bb2,所以得出以下结论:

  • 如果多条线程同时第一次访问 lazy 属性,无法保证属性只被初始化 1 次。
  • 当结构体包含一个延迟存储属性时,只有 var 修饰实例 才能访问延迟存储属性,因为延迟属性初始化时需要改变结构体的内存

self

T.self: T如果是实例变量,当前的T.self返回的就是他本身实例对象 如果T是类,当前T.self返回的就是元类型

Self

  • Self类型不是特定的类型,而是让您方便的引用当前类型,而无需重复或者知道该类型的名称,在协议声明或者协议成员声明中,Self类型是值最终符合协议的类型
  • 作为方法的返回类型,作为只读下标的返回类型,作为只读计算属性的类型

AnyObject

代表任意类的实例,类的类型,仅类遵守的协议

Xnip2022-01-13_19-04-31.jpg

如果我们在一个协议后面标记了AnyObject,然后struct去实现这个协议,编译器会报错 截屏2022-01-13 19.13.37.png 修改为class,不报错 截屏2022-01-13 19.15.28.png 我们在代码编写的过程中有时候不知道具体的类型,用AnyObject来表示,那如果我们知道了类型,该如何把AnyObject转换成具体的类型,这里我们使用三个关键字

as    as?    as!

var t:AnyObject = Teacher()
if let t1 = t as? Student{
    //如果你不确定他的类型是不是Student,那么这里我们就直接通过可选as?来进行转换,如果失败,t1返回nil
}

Any

Any:代表任意类型包括Function类型或者Optional类型

//报错,因为Int在Swift中是值类型,我们是没法用AnyObject来表示的
var array = [AnyObject] = [1]
//正确
var array = [Any] = [1]

AnyClass

AnyClass代表任意实例的类型

public typealias AnyClass = AnyObject.Type

guard

guard expression else { 
    //语句
    //必须包含一个控制语句:return,break,continue或throw。
}

这里,expression是一个布尔表达式(返回true或者false
如果对表达式求值falseguard则执行代码块内的语句
如果对表达式求值trueguard则从执行中跳过代码块内的语句

//使用选项保护
func changeOptionalStringToUpperCase() {

    var name:String?
    guard let temp = name else {
        print("Name is nil. Cannot process")
        return
    }
    print("Uppercased:\(temp.uppercased())")
}

//具有多个条件的保护
func changeOptionalStringToUpperCase() {
	var name:String? = ""
	guard let temp = name , temp.count > 0  else {
		print("Name is nil or an empty string. Cannot process")
		return
	}
	print("Uppercased:\(temp.uppercased())")
}

?? 空合并运算符

a ?? b 将对可选类型a 进行空判断,如果a包含一个值就进行解包,否则就返回一个默认值b

  • 表达式a必须是optional类型
  • 默认值b的类型必须要和a存储值的类型保持一致