swift-类和结构体

2019-04-21  本文已影响0人  AlliumLiu

结构体和类

  1. 值类型
  1. 可变性
var mutabelArray: NSMutableArray = [1,2,3]
for _ in mutabelArray {
    mutabelArray.removeLastObject()
}
var mutabelArray = [1,2,3]
for _ in mutabelArray {
    mutabelArray.removeLast()
}
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. 结构体
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]
上一篇下一篇

猜你喜欢

热点阅读