Swift 经验总结
-
SWift 对象的内存结构(HeapObject)有两个属性:metadat refcount 共占16字节、
-
MetaDataKind 所有类型的最终基类(类似于NSObject)
-
UnsafePointer<int,int> -> 转换成原生指针UnsafeRawPointer -> 再假定内存绑定的指针类型assumingMemoryBound(int.self) 或 强制转换指针类型bindMemory(to: int.self) 或 零时重绑定指针类型withMemoryRebound(to: int.self)
-
InlineRefcount(强引用) 或 SideTableRefcount(包含weakBitsT) 都是继承自RefcountBitsT
week、 通过创建散列表
unowned 直接操作位域信息
- enum 的大小取决于 是否有额外的空间存储 关联值,如果没有 那就是 N个byte + 关联值本身的大小
enum 大小等于
size_t size
if (payloadNumExtraInhabitans >= emptyCases){
size = payloadSize; (默认UInt8一字节,bool是一字节)
// 未使用的bit位 = 关联值的位数 减去 Cases
unusedNumExtraInhabitans = payloadNumExtraInhabitans - emptyCases;
}
else {
// 关联值
size = payloagSize + getEnumTagCounts(payloadSize,emptyCases - payloadNumExtraInhabitans,1).numTagBytes;
}
enum 简单使用 Optional (访问尽量使用可选链? & ?? 或 if let & guard let)
enum MineOptional<H> {
case some(H)
case none
}
func getOldValue(_ value:Int) -> MineOptional<Int> {
if value % 2 == 0 {
return .some(value)
}
else {
return .none
}
}
var numbers = [1,2,3,4,5,6]
for e in numbers {
let value = getOldValue(e)
switch value {
case .some(let val): numbers.remove(at: numbers.firstIndex(of: val)!)
case .none: print("nil")
}
}
print(numbers)
// 打印结果
nil
nil
nil
[1, 3, 5]
- indirect 其作用就是将值类型分配到堆空间上
enum Numers {
case one(Int)
case two(Int)
case tree
}
print(MemoryLayout<Numers>.stride) // 等于16字节 8字节对齐 超8则加8
print(MemoryLayout<Numers>.size)
// 打印结果
16
9
indirect enum BinaryTree<T> {
case empty
case node(left: BinaryTree,value: T,right: BinaryTree)
}
var node = BinaryTree<Int>.node(left: BinaryTree<Int>.empty, value: 100, right: BinaryTree<Int>.empty)
print(MemoryLayout<BinaryTree<Int>>.stride)
print(MemoryLayout<BinaryTree<Int>>.size)
print(MemoryLayout<BinaryTree<String>>.stride)
print(MemoryLayout<BinaryTree<String>>.size)
// 打印结果
8
8
8
8
// 结论 :case中的关联值相当于一个类,首地址是8字节,为啥是8而不是9,因为在堆空间引用关系
- UnsafeMutableRawPointer advanced & allocate & load & deallocate
let count = 4
let pp = UnsafeMutableRawPointer.allocate(byteCount: MemoryLayout<Int>.size * count, alignment: MemoryLayout<Int>.stride)
// 保存
for i in 0 ..< count {
pp.advanced(by: MemoryLayout<Int>.stride * i).storeBytes(of: i, as: Int.self)
}
// 取出
for i in 0 ..< count {
let value = pp.load(fromByteOffset: i * MemoryLayout<Int>.stride, as: Int.self)
print("index:\(i) == value:\(value)")
}
pp.deallocate()
- UnsafeMutablePointer allocate & deallocate & deinitialize(count:)
案例1 结构体绑定
struct ContainerList {
var element:Any;
var next:Any;
}
print(MemoryLayout<ContainerList>.size)
print(MemoryLayout<ContainerList>.stride)
var pp = UnsafeMutablePointer<ContainerList>.allocate(capacity: 4)
for i in 0 ..< 4 {
pp.advanced(by: i).withMemoryRebound(to: ContainerList.self, capacity: MemoryLayout<ContainerList>.stride) { (context: UnsafeMutablePointer<ContainerList>) in
return context.initialize(to: ContainerList(element: i + 1 * 10, next: i + 1))
}
}
print(pp[0])
print(pp[1])
print(pp[2])
print(pp[3])
pp.deinitialize(count: 4)
pp.deallocate()
// 打印结果
ContainerList(element: 10, next: 1)
ContainerList(element: 11, next: 2)
ContainerList(element: 12, next: 3)
ContainerList(element: 13, next: 4)
案例2 数组绑定 defer
var bytes: [UInt8] = [39, 77, 111, 111, 102, 33, 39, 0]
print(bytes.capacity)
print(bytes.count)
var uint8Pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: bytes.capacity)
uint8Pointer.initialize(from: &bytes, count: bytes.count)
for i in 0 ..< bytes.count {
print(uint8Pointer[i])
}
defer {
uint8Pointer.deinitialize(count: bytes.capacity)
uint8Pointer.deallocate()
}
// 打印结果
39
77
111
111
102
33
39
0
- 结构体中的内存对齐字节数由最大成员类型决定,按位填充或补齐,最后对齐得出结构体的大小
print(MemoryLayout<CChar>.stride)
print(MemoryLayout<Int>.stride)
print(MemoryLayout<String>.stride)
print(MemoryLayout<CChar>.size)
print(MemoryLayout<Int>.size)
print(MemoryLayout<String>.size)
// 打印结果
1
8
16
1
8
16
- 结构体大小
struct Std1 {
var char1:CChar;
var number:Int;
var char2:CChar;
}
struct Std2 {
var number:Int;
var char1:CChar;
var char2:CChar;
}
print(MemoryLayout<Std1>.size)
print(MemoryLayout<Std1>.stride) // 24
print(MemoryLayout<Std2>.size)
print(MemoryLayout<Std2>.stride) // 16
// 打印结果
17
24
10
16
- 自定义运算符 infix operator prefix operator postfix operator (分别对应 中缀前缀后缀 运算符的创建标识)
注意:有必要时可以结合 precedencegroup 使用
-
尽量使用隐式解包的方式,提高代码的可读性,只需要在属性或成员类型后面加‘!’
-
T.self 实力对象返回本身,类返回元类型metadata
-
Self 作为方法的返回类型 或 类和结构体中即元类型
-
AnyObject & Protocol
正确使用
注意:如果是struct会报错
protocol MyProtocol:AnyObject {
}
class MyContainer:MyProtocol {
}
- Mirror & Json
protocol OperatorJson {
func optJson() -> Any
}
extension Int : OperatorJson {}
extension String : OperatorJson {}
extension OperatorJson {
func optJson() -> Any {
let mirror = Mirror.init(reflecting: self);
guard !mirror.children.isEmpty else { return self}
var rs = [String:Any]()
for child in mirror.children {
if let value = child.value as? OperatorJson {
if let key = child.label {
rs[key] = value.optJson()
}
else {
print("no keys")
}
}
else {
print("value not conform OperatorJson protocol")
}
}
return rs
}
}
struct JsonObject:OperatorJson {
var age = 500
}
print(JsonObject().optJson())
// 打印结果
["age": 500]
-
认识协议 (协议可以理解为约束类的共同行为)
-
复习方法调度
class struct enum 都有metadata
方法的调度可分为 动态派发 静态派发
class对象的方法 在 V-table(动态派发函数表派发) metadata + offset
struct对象的方法 在 汇编bl 直接派发(静态派发)编译链接之后当前的地址就已经确定了(因其值类型没有继承关系,所有的方法都是私有的)
extension中(class,struct)的方法 属 直接派发(静态派发)因其不能确定是父类或者子类重构了方法(这会带来额外的复杂动作,影响V-table的结构),所以直接派发
final 修饰的函数 属 直接派发(静态派发)
@objc 修饰的函数 属 V-table (动态派发函数表派发)metadata + offset(想暴露给OC使用,还需继承自NSObject)
dynamic 修饰class中的函数(可以动态替换) 属 V-table (动态派发函数表派发)metadata + offset(这个偏移信息和类的描述信息有关)
dynamic 修饰struct中的函数(可以动态替换) 属 直接派发 (静态派发)
class Person:HB {
dynamic func run1(_ count: Int) { // 即添加dynamic修饰的函数可以用来被替换
print("跑起来")
}
}
extension Person {
@_dynamicReplacement(for: run1)
func run2(_ count:Int){
print("骚起来")
}
}
let person = Person()
person.run1(10);
// 打印
骚起来
@objc + dynamic 属runtime消息机制 objc_msgSend 动态派发 (意味着可以使用runtime api)
- 协议方法的调度 在
先看代码
protocol HB {
func run(_ count:Int)
}
extension HB {
func run(_ count:Int){
print("浪起来")
}
}
struct Person:HB {
func run1(_ count: Int) {
print("跑起来")
}
}
- class + protocol + method
创建对象1: let person:Person = Person() 方法 在 V-table (动态派发函数表派发)
person.run(10);
静态类型 和 动态类型(方法的调用取决于最后的动态类型),如果没有协议:extension里面的方法优先实现
创建对象2: let person:HB = Person() 方法 在 witness-table 或 V-table
person.run(10);
注意:如果协议是@objc,那么它的方法在V-table(动态派发)
注意:如果协议不是@objc,那么它的方法在witness-table(动态派发)
- struct + protocol + method
创建对象1: let person:Person = Person() 方法 在 witness-table (动态派发函数表派发)
person.run(10);
创建对象2: let person:HB = Person() 方法 在 witness-table (动态派发)
person.run(10);
注意:结构体没有V-table,所以都是witness-table
-
存在容器(Existential Container) 对于静态类型和动态类型创建的对象会统一放到一个40字节的容器中(不管是类还是结构体,不管是值类型还是引用类型),因为在编译阶段无法推导出动态类型确定内存空间的大小
-
value buffer(小容量数据) 前24字节 仅是值类型的结构(如果<=24字节,值类型存储)
-
value buffer(大容量数据) 前24字节 保存堆区内存的地址(如果>24字节 开辟堆区存储)
-
写时复制(或者写时创建内存) 值类型的大小超过了valuebuffer的小容量数据,会开辟堆区保存成员, 成员赋值时检查引用计数,如果引用计数大于1就会开辟新的内存空间,否则就不开辟(提高内存指针的利用率,降低堆区的内存消耗,提升性能)
网友评论