Swift 基础

Swift Structures and Classes, St

2020-08-05  本文已影响0人  幸运者_Lucky

common:

  1. 属性定义
  2. 方法定义
  3. 定义下标 (subscripts), []
  4. 定义初始函数
  5. 扩展的使用
  6. 协议的使用

diffenrence:

  1. 类可以继承, 结构体不可以
  2. 类可以在运行时, 检查或者转换类型, 结构体不可以
  3. 析构函数
  4. 类有引用计数, 可以多个指针指向同一个对象, 结构体不可以

Swift struct 的实现

  1. 当结构体中包含引用类型
    当结构体包含引用类型, 在 copy 的时候, 它的效率还不如 class 本身, 看下面代码,
    当 b2 = b1 的时候, b2 在复制 b1 中的 a 和 aa 这两个引用类型的时候, 都会造成 a 和 aa 的引用计数加一, 如果 B 是类, 则只会 b1 这个对象的引用计数加一, 不会对它的属性造成影响, 所以说, 如果在 swift 结构体中, 使用引用类型, 还不如直接使用类的效率高.
class A {}

struct B {
    let a = A()
    let aa = A()
    let c = 1
}

let b1 = B()
let b2 = b1
  1. valueBuffer 存储值
    结构体的实现包含了一个 valueBuffer, 伪代码是 let valueBuffer = [x1, x2, x3]
    当结构的属性都是值类型, 并且数量小于等于3个的时候, 属性就会直接存放在 valueBuffer 中, 如下面 code, 当 p2 = p1 的时候, p1 会直接把 valueBuffer 中的值赋值给 p2, 到目前为止, struct 是不涉及到任何堆操作的.
struct Point {
    let x: Float
    let y: Float
}

let p1 = Point(x: 0, y: 0)
let p2 = p1
  1. valueBuffer 指向堆, Copy-On-Write
    在第一点提到使用引用类型效率低的问题, 这里就是解决的办法.
    当属性大于3的时候, valueBuffer 没法存储, 是通过 VWT(Value Witness Table) 来实现的, 数据存储在堆上, 通过 valueBuffer 来记录堆上的地址, 通过 VWT 来管理生命周期, 如下面的结构体 T, 就会按照这个方式来存储, 但是在发生复制的情况是如何处理的, t2 = t1, t3 = t1..., 按照值类型的复制概念, 每次都是深拷贝, 那会申请大量的堆内存.
    结构体也有引用计数, 这是针对数量大于3这种情况来说的, 因为存储到堆上, 每次复制的时候, 只是新增了指向堆内存的指针, 但是这样当修改属性值的时候, 所有指向这个堆内存的值都会变吗? 所以在某个属性有写操作的时候, 才做深拷贝操作, 这样, 它的修改就不会应该其他的.
    到这里引出了一个新概念 Copy-On-Write
    如下代码: 当 t2.w = 10, 发生的时候, 首先 t2 会做深拷贝, 此时 t2 的 valueBuffer 指向了新的堆内存, 并且把 w, x, y, z 都 copy 进去了, 这样t1, t3就不会有任何问题
struct T {
    var w: Float
    var x: Float
    var y: Float
    var z: Float
}

var t1 = T(w: 0, x: 0, y: 0, z: 0)
var t2 = t1
var t3 = t1

t2.w = 10

note: 自定义的 struct 是没有实现 Copy-On-Write 的, 大概实现的思路是下面这样的

final class Ref<T: Hashable>: Hashable {
    var val : T
    init(_ v : T) {val = v}
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(val)
        hasher.finalize()
    }
    
    static func == (lhs: Ref, rhs: Ref) -> Bool {
        return lhs.hashValue == rhs.hashValue
    }
}

struct Box<T: Hashable>: Hashable {
    var ref : Ref<T>
    init(_ x : T) { ref = Ref(x) }
    
    var val: T {
        get { return ref.val }
        set {
            if (!isKnownUniquelyReferenced(&ref)) {
                print("set1")
                ref = Ref(newValue)
            } else {
                print("set2")
                ref.val = newValue
            }
        }
    }
    
    mutating func run() {
        print("isKnownUniquelyReferenced: ", isKnownUniquelyReferenced(&ref))
    }
    
    var cc = 10
}
let view = UIView()
var b = Box(view)
b.run()
var c = b
b.run()

if c == b {
    print("==")
}

// print
isKnownUniquelyReferenced:  true
isKnownUniquelyReferenced:  false
==

isknownuniquelyreferenced
understanding-swift-copy-on-write-mechanisms

总结:
struct 不会像 class 那样, 背后有大量的代码来管理一个对象的生命周期
struct 中包含 class 属性, 同样走引用计数
struct 使用 Copy-On-Write 来优化大值的内存问题
struct 不会造成多线程唯一的问题, 和内存泄露, 不存在循环引用的问题

Swift 5 源码实现 Copy-On-Write

internal final class _MutableHandle<MutableType : NSObject>
  where MutableType : NSCopying {
    fileprivate var _pointer : MutableType
    
    init(reference : __shared MutableType) {
        _pointer = reference.copy() as! MutableType
    }
    
    init(adoptingReference reference: MutableType) {
        _pointer = reference
    }
    
    /// Apply a closure to the reference type.
    func map<ReturnType>(_ whatToDo : (MutableType) throws -> ReturnType) rethrows -> ReturnType {
        return try whatToDo(_pointer)
    }
    
    func _copiedReference() -> MutableType {
        return _pointer.copy() as! MutableType
    }
    
    func _uncopiedReference() -> MutableType {
        return _pointer
    }
}

/// Describes common operations for Foundation struct types that are bridged to a mutable object (e.g. NSURLComponents).
internal protocol _MutableBoxing : ReferenceConvertible {
    var _handle : _MutableHandle<ReferenceType> { get set }
    
    /// Apply a mutating closure to the reference type, regardless if it is mutable or immutable.
    ///
    /// This function performs the correct copy-on-write check for efficient mutation.
    mutating func _applyMutation<ReturnType>(_ whatToDo : (ReferenceType) -> ReturnType) -> ReturnType
}

extension _MutableBoxing {
    @inline(__always)
    mutating func _applyMutation<ReturnType>(_ whatToDo : (ReferenceType) -> ReturnType) -> ReturnType {
        // Only create a new box if we are not uniquely referenced
        if !isKnownUniquelyReferenced(&_handle) {
            let ref = _handle._pointer
            _handle = _MutableHandle(reference: ref)
        }
        return whatToDo(_handle._pointer)
    }
}
上一篇 下一篇

猜你喜欢

热点阅读