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
}
}
异变方法的本质:
对于异变方法,传入的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
访问级别所修饰的属性或者方法只能在当前类里访问
fileprivate
所修饰的属性或者方法在当前的源文件里可以访问 同一个文件内访问,跨文件不能访问
lazy 延迟存储属性
class User{
lazy var age:Int = 20
}
let u = User()
print(u.age)
lazy 修饰的属性初始化时机
通过上面可以看出,lazy修饰的属性,在没有使用的时候并没有被赋值,存储的是0 使用后赋值18
- 使用
lazy可以定义一个延迟存储属性,在第一次用到属性的时候才会进行初始化。 lazy属性必须是var,不能是let,因为let必须在实例的初始化方法完成之前就拥有值。
通过sil分析延迟属性的原理
通过观察可以发现,
lazy属性会被声明称可选类型,也就是说,lazy属性在初始化是会被赋值为Optional.none,另外,lazy属性会被标记为final,所以不能被重写
lazy属性的getter调用
通过上图可以看到,有值的话直接调用bb1,没有值的话调用bb2,所以得出以下结论:
- 如果多条线程同时第一次访问 lazy 属性,无法保证属性只被初始化 1 次。
- 当结构体包含一个延迟存储属性时,只有 var 修饰实例 才能访问延迟存储属性,因为延迟属性初始化时需要改变结构体的内存
self
T.self: T如果是实例变量,当前的T.self返回的就是他本身实例对象 如果T是类,当前T.self返回的就是元类型
Self
- Self类型不是特定的类型,而是让您方便的引用当前类型,而无需重复或者知道该类型的名称,在协议声明或者协议成员声明中,Self类型是值最终符合协议的类型
- 作为方法的返回类型,作为只读下标的返回类型,作为只读计算属性的类型
AnyObject
代表任意类的实例,类的类型,仅类遵守的协议
如果我们在一个协议后面标记了AnyObject,然后struct去实现这个协议,编译器会报错
修改为class,不报错
我们在代码编写的过程中有时候不知道具体的类型,用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)。
如果对表达式求值false,guard则执行代码块内的语句。
如果对表达式求值true,guard则从执行中跳过代码块内的语句
//使用选项保护
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存储值的类型保持一致