美文网首页
swift-类和结构体

swift-类和结构体

作者: AlliumLiu | 来源:发表于2019-04-21 14:29 被阅读0次

    结构体和类

    • swift 中储存结构化的数据 可以用 结构体,枚举,类及使用闭包捕获变量。
    • 类和结构体不同点: 1.结构体是值类型类是引用类型,编译器可以保证结构体的不变性,类要我们自己来保证 2.内存管理方式不同接头体可以直接持有及访问,类的实例只能通过地址间接访问,结构体是唯一的,类能有多个持有者。3.类可以代码共享,结构体不能被继承
    1. 值类型
    • 我们经常处理一些有生命周期的类型 ViewController 有init,delloc等等,而有的则不需要生命周期例如 URL 它一旦创建就不会被改变,它们结束时不需要额外操作。比较两个URL的值时 我们不关心是否指向同一地址,而是它们的属性是否一样。这种类型我们称之为值类型。NSURL 是一个不可变对象 而URL 是一个结构体。
    • 值类型具有天然的线程安全性。不可变的东西是可以多线程共享的。、
    • 结构体中也能定义 var 类型 但这个可变性只体现在变量自己身上。当我们改变结构体中的属性时 它总是生成一个全新的结构体来取代
    • 结构体只能有一个持有者,当我们把它传递给一个函数是它被复制了一份 函数只能改变这个副本,这被叫做 值语义。而对象传递的是地址指针,称为引用语义。
    • 结构体只有一个持有者所以不会造成循环引用,除非结构体包含类属性,否则就不需要考虑引用计数问题,let声明的结构体 一个字节也不会改变
    1. 可变性
    • 可变性是造成bug主要原因之一,而swift可以让我们写出安全代码的同时,保留可变代码的风格
    var mutabelArray: NSMutableArray = [1,2,3]
    for _ in mutabelArray {
        mutabelArray.removeLastObject()
    }
    
    • 我们知道数组的变化会破坏迭代器的内部结构 这个是不被允许的,我们知道这一点,不会犯这种错误。 然而我们不能保证 removeLastObject 不被其他地方调用。这种错误很难被发现
      swift 就避免了这种错误
    var mutabelArray = [1,2,3]
    for _ in mutabelArray {
        mutabelArray.removeLast()
    }
    
    • 赋值问题 引用类型会改变赋值它的变量的属性 这是很强大的特性,但有时也会造成bug
    var mutabelArray: NSMutableArray = [1,2,3]
    var new = mutabelArray
    new.add(4)
    

    我们实现一个扫描器

    class BinaryScanner {
        var position: Int
        let data: Data
        init(data: Data) {
            self.position = 0
            self.data = data
        }
    }
    
    extension BinaryScanner {
        func scanByte() -> UInt8? {
            guard position < data.endIndex else {
                return nil
            }
            position += 1
            return data[position - 1]
        }
    }
    
    正常可运行
    func scanRemainingBytes(scanner: BinaryScanner) {
        while let byte = scanner.scanByte() {
            print(byte)
        }
    }
    
    有可能偶发线程不安全
    for _ in 0..<Int.max {
        let newScanner = BinaryScanner(data: "hi".data(using: .utf8)!)
        DispatchQueue.global().async {
            scanRemainingBytes(scanner: newScanner)
        }
        scanRemainingBytes(scanner: newScanner)
    }
    
    
    1. 结构体
    • 几乎在所有编程中 标量都是值类型
    • 当我们把一个结构体赋值给另一个时,swift 自动对它进行赋值,听起来很昂贵, 但编译器会对这种赋值进行优化,称为写时复制。
    struct Point {
        var x: Int
        var y: Int
    }
    
    let origin = Point(x: 0, y:0)
    origin.x = 10 // ❎
    
    var otherPoint = Point(x: 0, y:0)
    otherPoint.x = 10 // ✅
    
    当我们把一个结构体赋值给另一个时
    var otherPoint = origin
    otherPoint.x = 10 // ✅
    otherPoint // x: 10, y: 0
    origin // x: 0, y: 0
    
    struct Size {
        var width: Int
        var height: Int
    }
    
    静态变量
    extension Point {
        static let origin = Point(x: 0, y: 0)
    }
    
    struct Rectangle {
        var origin: Point
        var size: Size
    }
    
    写在扩展中的初始化方法,系统会保留原始的初始化方法。
    extension Rectangle {
        init(x: Int = 0, y: Int = 0, width: Int, height: Int) {
            origin = Point(x: x, y: y)
            size = Size(width: width, height: height)
        }
    }
    
    
    var screen = Rectangle(width: 320, height: 480) {
        didSet {
            print("screen did Changed \(screen)")
        }
    }
    
    screen.origin.x = 10
    我们只是改变了结构体的数量 但是它的didset会被触发
    
    var array = [screen] {
        didSet {
            print("array did Changed")
        }
    }
    
    array[0].origin.x = 10
    数组是结构体 数组内元素的变化 数组本身 也会触发 didset
    如果Rectangle是类 那么 didset就不会被触发
    
    
    func + (lhs: Point, rhs: Point) -> Point {
        return Point(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
    }
    
    extension Rectangle {
        func translate(by offset: Point) {
            origin = origin + offset 
        // 这个方法系统会报错 self是不可变得 需要声明mutating
        }
    }
    
    extension Rectangle {
        mutating func translate(by offset: Point) {
            origin = origin + offset
        }
    }
    
    标记了mutating 意味着 self是可变的 表现的想一个 var
    当然 let 声明的结构体 依然是不可变得
    
    很多情况下 方法都有可变和不可变两种版本 ,sort() 原地排序 sorted() 返回新的数组
    
    extension Rectangle {
        func translated(by offset: Point) {
                var copy = self
                    copy.translate(offset: offset)        
                return copy
        }
    }
    
    事实上 mutating 方法只是结构体上 普通的方法 只是 self 被隐士的标记为 inout 了 &
    

    我们再来看上面的scanner 问题 如果 BinaryScanner 是 结构体那么 每个方法调用的结构体都是一个 副本这样 就可以安全的迭代了而不用担心被其他线程更改。

    1. 写时复制 copy-on-write

    var x = [1,2,3]
    var y = x
    如果创建一个y 把并把x赋值它时 会发生复制
    x 和 y 含有独立的结构体。
    但是 数组内含有指向 元素位置的指针 在这时 x 和 y 共享了他们的部分储存 不过 当x改变时 这种共享 会被检测到,内存将被复制。
    只有在 有一个 发生改变时 内存才被复制 成为写时复制

    • 昂贵方式
    struct Mydata {
        fileprivate var _data: NSMutableData
        var _dataFOrWriting: NSMutableData {
            mutating get {
                _data = _data.mutableCopy() as! NSMutableData
                return _data
            }
        }
    
        init(data: NSData) {
            self._data = data.mutableCopy() as! NSMutableData
        }
    }
    
    
    • 高效方式

    在 swift 中 可以用 isKnownUniquelyReferenced(&<#T##object: T##T#>)
    来检查引用的唯一性。
    获取时 可以通过 是否被唯一引用来决定是否 复制

    得益于写时复制和相关联的编译器优化 大量不必要的操作被移除了。
    如果我们写一个 结构体 而不能保证其中属性的不变性,我们可以考虑使用类来实现。

    • 写时复制陷阱

    我们使用array[0] 时 直接用下标访问 是不会发生写时复制的 因为它直接访问了内存中元素的位置。而字典和set 不同

    1. 闭包和可变性
      一个函数 每次生成一个唯一的整数直到Int.max可以将状态移动到函数外部,换句话说函数对i进行了闭合
    var i = 0
    
    func uniqueInteger() -> Int {
        i += 1
        return i
    }
    

    swift 函数也是引用类型

    let otherFunction = uniqueInteger
    传递函数它会已引用方式存在 并共享了 其中的状态

    func uniqueIntegerProvider() -> () -> Int {
        var i = 0
        return {
            i += 1
            return i
        }
    }
    返回一个从零开始的方法
    也可以封装成 AnyIterator 一个整数发生器
    func uniqueIntegerProvider() -> AnyIterator<Int> {
        var i = 0
        return AnyIterator {
            i += 1
            return i
        }
    }
    
    • 结构体 一般被储存在栈上,而非堆上这其实是一种优化,默认结构体是在堆上的,但大多数情况下 优化都会生效
    • 当一个结构体被函数闭合了 它就在堆上就算函数退出了作用域,它仍然存在,同样结构体过大 也会放在堆上
    1. 内存 swift 的强引用和循环引用类似于OC weak(必然是可选值) unowned [weak self]

    相关文章

      网友评论

          本文标题:swift-类和结构体

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