swift原理
字符串底层
-
// 字符串长度 <= 0xF,字符串内容直接存放在str1变量的内存中
var str1 = "012345678"
内存内容0x3736353433323130 0xe900000000000038 -
// 字符串长度 > 0xF,字符串内容存放在__TEXT.cstring中(常量区)// 字符串的地址值信息存放在str2变量的后8个字节中
var str2 = "0123456789ABCDEF"
内存内容0xd000000000000010(字符串长度) 0x800000010000b230(字符串地址值)后边地址值(0x10000b230)加0x20就是字符串真实地址值 -
// 由于字符串长度 <= 0xF,所以字符串内容依然存放在str1变量的内存中str1.append("ABCDE")
编译 常量区 内存已经确认,不允许修改,所以只要添加后 长度超过16,就开辟堆空间// 开辟堆空间 后边地址值(0x10000b230) 加0x20就是字符串真实地址值
str1.append("F")// 开辟堆空间str2.append("G")
array底层
array 地址指向堆空间 底层引用类型 当做值类型使用
let
let 修饰常量,常量的内存不可以修改。值类型都不可以修改。引用类型,可以修改属性值,不可以修改内存指向其他对象
闭包
func testClosure() -> (Int) -> (Int, Int) {
var num = 1
var num2 = 2
func plus(_ i: Int) -> (Int, Int) {
num += i
num2 += i
return (num, num2)
}
return plus
}
let fn1 = testClosure()
fn1(2)
let fn2 = testClosure()
fn2(3)
-
闭包想象成类的实例变量。引用类型,开辟堆空间,捕获的变量、常量就是对象的存储属性,组成闭包的函数就是类内部定义的方法,前8个字节存储相关信息 再8个字节存储引用计数相关,后面字节存储变量。
-
//fn1 fn2 占用16个字节 前八个字节一样,都是存储的plus函数间接地址。后八个字节存储的是各自分配的不同的堆空间。
-
//调用fn1 fn2,本质上是把i,还有堆空间地址值传进plus,然后再plus里面修改堆空间num的值
-
//捕获num值到堆空间,调用testClosure 返回plus之前
-
//为什么捕获num 保命 在函数中栈空间,函数运行完就释放了
-
//如果捕获两个变量,分配两个堆空间,前16个字节都一样(前8个类型信息,后8个引用计数),后边8个字节是变量的值
inout本质(引用传递)
- 如果实参有物理内存地址,且没有设置属性观察器。直接将实参的内存地址传入函数(实参进行引用传递)
- 如果实参是计算属性或者设置了属性观察器。采取copy in copy out。1.调用该函数,先复制实参的值(计算属性get方法,属性观察器的值),存储在临时局部变量。2.将临时局部变量的地址传入函数(引用传递),修改临时变量的值。3.函数返回后,再调用set方法将临时变量的值覆盖到实参的值。
- 属性观察器的存储属性为什么不直接覆盖属性值,因为要监听willSet didSet,必须调用set方法,所以不能直接覆盖地址值。
枚举,结构体。
- 内部实例方法修改存储属性的值,需要在方法前面加上mutating.(类型方法不需要,可直接修改类型属性,因为类型属性不占用枚举或者结构体内存)
- 下标:结构体作为返回值,如果不实现set方法,不能修改结构体变量,因为是值类型,内容拷贝一个临时变量。class作为返回值,可以修改,指针变量,引用类型。
class struct 区别
- class引用类型,实例对象是指针变量,存储堆空间地址值。struct 值类型,实例对象直接存储变量值。
- class 继承(重写override修饰),struct不能继承。
- struct 系统自动生成多个初始化器。class只自动有一个无参初始化器,重写指定初始化器就没有这个系统自动生成的。
- struct 内存空间就是所有存储属性的所占内存。class 还需要加上8个字节metedata相关,8个字节引用计数相关,接下来才是存储属性。
- struct 类型方法,属性只能是static修饰。class,static,class 都可以。只有class修饰的,类型计算属性,类型方法、下标才能被重写。
class 多态实现原理
class Animal {
func speak() {
print("Animal speak")
}
func eat() {
print("Animal eat")
}
func run() {
print("Animal run")
}
}
class Dog: Animal {
override func speak() {
print("Dog speak")
}
override func eat() {
print("Dog eat")
}
func look() {
print("Dog look")
}
}
var ani = Animal()
ani.speak()
ani.eat()
ani.run()
ani = Dog()
ani.speak()
ani.eat()
ani.run()
多态.png
多态实现原理
- 通过ani变量找到Dog对象的堆空间地址,取出前八个字节(Dog的类型信息地址)的地址值,即类型信息相关信息,然后偏移0x50,找到对应方法执行。
- 类型信息存放在全局区
指定(主要)初始化器 & 便捷初始化器 (两段式初始化,保证所有实例都能被初始化)
- 至少有一个指定初始化器(推荐一个),便捷初始化器可以有多个。
- 指定初始化器不能相互调用,便捷初始化器可以相互调用但是最终必须调用指定初始化器。
- 指定初始化器 必须首先初始化自己的实例变量,然后调用直系父类的指定初始化器。便捷初始化器不能调用父类的指定初始化器,只能调用自己的指定初始化器
- 重写父类的指定初始化器为指定初始化器或者便捷初始化器都需要加上override。因为父类的便捷初始化器,子类无法调用,所以子类无法重写父类便捷初始化器的方法,不需要加override。
- 子类如果没有自定义指定初始化器,自动继承父类所有指定初始化器。
- 子类通过继承或者重写父类的所有指定初始化器的实现,子类自动继承父类的所有便捷初始化器。
- 父类的属性在它自己的初始化器中赋值不会触发属性观察器,但在子类的初始化器中赋值会触发属性观察器。