美文网首页
swift 总结

swift 总结

作者: f8d1cf28626a | 来源:发表于2022-11-22 00:32 被阅读0次

Swift 经验总结

  1. SWift 对象的内存结构(HeapObject)有两个属性:metadat refcount 共占16字节、

  2. MetaDataKind 所有类型的最终基类(类似于NSObject)

  3. UnsafePointer<int,int> -> 转换成原生指针UnsafeRawPointer -> 再假定内存绑定的指针类型assumingMemoryBound(int.self) 或 强制转换指针类型bindMemory(to: int.self) 或 零时重绑定指针类型withMemoryRebound(to: int.self)

  4. InlineRefcount(强引用) 或 SideTableRefcount(包含weakBitsT) 都是继承自RefcountBitsT

week、 通过创建散列表

unowned 直接操作位域信息

  1. 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]
  1. 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,因为在堆空间引用关系
  1. 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()
  1. 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
  1. 结构体中的内存对齐字节数由最大成员类型决定,按位填充或补齐,最后对齐得出结构体的大小
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
  1. 结构体大小
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
  1. 自定义运算符 infix operator prefix operator postfix operator (分别对应 中缀前缀后缀 运算符的创建标识)

注意:有必要时可以结合 precedencegroup 使用

  1. 尽量使用隐式解包的方式,提高代码的可读性,只需要在属性或成员类型后面加‘!’

  2. T.self 实力对象返回本身,类返回元类型metadata

  3. Self 作为方法的返回类型 或 类和结构体中即元类型

  4. AnyObject & Protocol

正确使用

注意:如果是struct会报错

protocol MyProtocol:AnyObject {
    
}

class MyContainer:MyProtocol {
    
}
  1. 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]
  1. 认识协议 (协议可以理解为约束类的共同行为)

  2. 复习方法调度

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)

  1. 协议方法的调度 在

先看代码

protocol HB {
    func run(_ count:Int)
}

extension HB {
    func run(_ count:Int){
        print("浪起来")
    }
}

struct Person:HB {
    
    func run1(_ count: Int) {
        print("跑起来")
    }
    
}
  1. 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(动态派发)

  1. 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

  1. 存在容器(Existential Container) 对于静态类型和动态类型创建的对象会统一放到一个40字节的容器中(不管是类还是结构体,不管是值类型还是引用类型),因为在编译阶段无法推导出动态类型确定内存空间的大小

  2. value buffer(小容量数据) 前24字节 仅是值类型的结构(如果<=24字节,值类型存储)

  3. value buffer(大容量数据) 前24字节 保存堆区内存的地址(如果>24字节 开辟堆区存储)

  4. 写时复制(或者写时创建内存) 值类型的大小超过了valuebuffer的小容量数据,会开辟堆区保存成员, 成员赋值时检查引用计数,如果引用计数大于1就会开辟新的内存空间,否则就不开辟(提高内存指针的利用率,降低堆区的内存消耗,提升性能)

相关文章

网友评论

      本文标题:swift 总结

      本文链接:https://www.haomeiwen.com/subject/sxkoxdtx.html