《Swift進階》ReadingNotes_4

weixin_34129145發表於2017-02-08

結構體與類:

不同點:

1,結構體是值型別,類是引用型別。
2,記憶體管理的方式不同。結構體可以直接被持有並訪問,但類不行,類的例項只能通過引用間接的訪問。結構體不能被引用,但可以被複制。所以結構體的持有者唯一,但類的持有者可以有好多個。
3,使用類我們可以用繼承來讓子類共享程式碼,但是結構體(以及列舉)沒有繼承。想要在不同的結構體或者列舉之間共享程式碼,我們需要使用不同的技術,比如像是組合、泛型以及協議擴充套件等。

關於mutating修飾詞:

mutating修飾的方法,用let宣告的例項將無法呼叫。其實被mutating修飾的方法也是一個普通的方法,只不過該方法隱式引數self被標為了inout,所以可以在函式內改變self。

寫時複製:

寫時複製技術,是指值型別在賦值的時候,如果兩個持有者是相同的內容,則共享同一記憶體,如果發生改變,在改變的瞬間只有一個持有者,則在原記憶體上進行,如果多個持有者,則複製一份記憶體,在新的記憶體上做變化,以保證事件的獨立。

高效的實現寫時複製方式:

應用了isKnownUniquelyReferenced(&object: T>)函式,去判斷當前物件是否有多個引用指向,如果並沒有其他變數強引用則返回true。
由於上面的函式對OC的物件直接返回false,所以需要結構體包含OC物件時需要把其包裝為Swift物件。

包裝類:
final class Box<A> {
var unbox: A
    init(_ value: A) { self.unbox = value }
}
struct MyData {
    fileprivate var _data: Box<NSMutableData>
    var _dataForWriting: NSMutableData {
        mutating get {
//改變了結構體的內容,相當於賦值了新的self
            if !isKnownUniquelyReferenced(&_data) {
                _data = Box(_data.unbox.mutableCopy() as! NSMutableData)
                print("Making a copy")
            }
            return _data.unbox
        }
    }
    init(_ data: NSData) {
        self._data = Box(data.mutableCopy() as! NSMutableData)
    }
}


extension MyData {
    mutating func append(_ other: MyData) {
        _dataForWriting.append(other._data.unbox as Data)
    }
}

驗證;

let someBytes = MyData(NSData(base64Encoded: "wAEP/w==", options: [])!)
        var change = someBytes
        change.append(someBytes)
        print(someBytes._data.unbox) //<c0010fff>
        print(change._data.unbox) //<c0010fff c0010fff>

寫時複製的陷阱:

需要注意的是,Array的下標使用了特別的處理,來讓寫時複製生效,但其他型別還沒有使用這種技術。
Array使用的特殊的技術就是使用地址其(addressors)的方式去實現下標。地址器是允許直接訪問記憶體的。陣列的下標並不是返回元素本身,而是返回是對應的地址器。所以可以直接訪問原地址,不需要進行復制 。

  final class Empty{}
//容器change方法可以檢測是否發生複製
struct COWStruct {
    var ref = Empty()
    mutating func change()->String{
        if isKnownUniquelyReferenced(&ref){
            return "no copy"
        }else{
            return "copy"
        }
    }
}
//這個容器型別包裝後可以通過下標訪問,也可以通過屬性訪問
struct ContainerStruct<A> {
    var storage: A
    subscript(s:String)->A{
        get{ return storage}
        set{storage = newValue}
    }
}



      var array = [COWStruct()]
        print(array[0].change())//no copy
        
        var dict = ["struct": COWStruct()]
        print(dict["struct"]!.change())//copy
        
        var container = ContainerStruct(storage: COWStruct())
        print(container.storage.change())//no copy
        print(container["test"].change())//copy

閉包和可變性:

函式式引用型別,所以傳遞函式會共享外部變數。
swift的結構體一般都被儲存在棧上,這其實是一種優化。預設結構體是被儲存在堆上,絕大多數情況會被優化儲存到棧上。但結構體變數被函式閉合的時候,優化失效。

weak和unowned

兩者都是宣告弱引用的,不同點在於:
weak處理弱引用變數時,我們必須將它宣告為可選值。
unowned處理時不會引用物件,但是會假定該引用一直有效,需要注意的是,被unowned持有的變數需要保持生命週期比持有者長,否則,訪問會造成crash。

對於每個被unwoned引用的物件,swift執行時將為這個物件維護另外一個引用計數器。當所有強引用消失之後,該物件的所有資源都會被釋放掉,但記憶體依然在,直到所有的unowned應用也消失。這部分記憶體的會被標識為無效(也叫殭屍記憶體)。當我們嘗試去訪問者塊記憶體時,自然會發生crash。0

當然還是多選用weak,因為我們即使現在確定的有效引用也不能保證在之後的重構中不會變為無效。

相關文章