[swift-users] Problem with mutable views and COW

Zhao Xin owenzx at gmail.com
Fri Nov 18 09:05:03 CST 2016


protocol Copyable {

    func copy() -> Self

}


final class Storage:Copyable {



    var keys: [String] = []

    var values: [Int] = []



    func copy() -> Storage {

        let s = Storage()

        s.keys = keys

        s.values = values



        return s

    }

}


public struct Document:Copyable {



    var _storageReference: Storage



    public init() {



        self._storageReference = Storage()

    }



    public init(_ values: DocumentValues) {



        self._storageReference = values._storageReference

    }



    public var values: DocumentValues {



        get { return DocumentValues(self) }



        set { self = Document(newValue) }

    }



    public func copy() -> Document {

        var d = Document()

        d._storageReference = _storageReference.copy()



        return d

    }

}


var document = Document()


let copy = document.copy()


// just assume we already added some values and can mutate safely on a
given index

// mutation in place

document.values[0] = 10 // <--- this will only mutate `document` but not
`copy`


You use `class` property inside `struct`, so the `struct` is no longer
copyable. You have to do it yourself.


Zhaoxin

On Fri, Nov 18, 2016 at 10:09 PM, Adrian Zubarev <
adrian.zubarev at devandartist.com> wrote:

> Ups sorry for CCing the post to the evolution list. That happens from time
> to time. :/
>
>
>
> --
> Adrian Zubarev
> Sent with Airmail
>
> Am 18. November 2016 um 15:07:43, Adrian Zubarev (
> adrian.zubarev at devandartist.com) schrieb:
>
> I apologize about the weak/unowned issue I mentioned. I kinda missed that
> portion from the docs Weak references do not affect the result of this
> function..
>
> Okay it’s clear to me now why the result is evaluated as false.
>
> But how do I solve the COW problem for mutable views?
>
>
> --
> Adrian Zubarev
> Sent with Airmail
>
> Am 18. November 2016 um 15:04:55, Adrian Zubarev (
> adrian.zubarev at devandartist.com) schrieb:
>
> This doesn’t make any sense.
>
> Somewhere from the Swift book:
>
> Weak and unowned references enable one instance in a reference cycle to
> refer to the other instance *without keeping a strong hold on it*. The
> instances can then refer to each other without creating a strong reference
> cycle.
>
> From the sdlib of the function isKnownUniquelyReferenced:
>
> Returns a Boolean value indicating whether the given object is a class
> instance known to have *a single strong reference*.
>
> unowned doesn’t increase the reference count, so the view in the examples
> I showed should have no strong reference to that class instance. The only
> strong reference that exists is from Document. So why exactly is the
> result of the second isKnownUniquelyReferenced call false again?
>
>
> --
> Adrian Zubarev
> Sent with Airmail
>
> Am 18. November 2016 um 14:50:57, Zhao Xin (owenzx at gmail.com) schrieb:
>
> >Why is the second check false, even if the property is marked as unowned for
> the view?
>
> Please search the mailing list, this is not the first time it comes as a
> new question. Shortly speaking, it is `false` only because you used
> `unowned`. If you you can grantee it always exists. Just use it directly,
> this is what `unowned` for. If you can't grantee that. You should use
> `weak` and check it with `if let` or `if foo == nil`
>
> Zhaoxin
>
>
> On Fri, Nov 18, 2016 at 8:05 PM, Adrian Zubarev via swift-users <
> swift-users at swift.org> wrote:
>
>> Hi there,
>>
>> I just can’t get my head around mutable views and COW.
>>
>> Here is a small example:
>>
>> final class Storage {
>>
>>     var keys: [String] = []
>>     var values: [Int] = []
>> }
>>
>> public struct Document {
>>
>>     var _storageReference: Storage
>>
>>     public init() {
>>
>>         self._storageReference = Storage()
>>     }
>>
>>     public init(_ values: DocumentValues) {
>>
>>         self._storageReference = values._storageReference
>>     }
>>
>>     public var values: DocumentValues {
>>
>>         get { return DocumentValues(self) }
>>
>>         set { self = Document(newValue) }
>>     }
>> }
>>
>> public struct DocumentValues : MutableCollection {
>>
>>     unowned var _storageReference: Storage
>>
>>     init(_ document: Document) {
>>
>>         self._storageReference = document._storageReference
>>     }
>>
>>     public var startIndex: Int {
>>
>>         return self._storageReference.keys.startIndex
>>     }
>>
>>     public var endIndex: Int {
>>
>>         return self._storageReference.keys.endIndex
>>     }
>>
>>     public func index(after i: Int) -> Int {
>>
>>         return self._storageReference.keys.index(after: i)
>>     }
>>
>>     public subscript(position: Int) -> Int {
>>
>>         get { return _storageReference.values[position] }
>>
>>         set { self._storageReference.values[position] = newValue } // That will break COW
>>     }
>> }
>>
>> First of all the _storageReference property is unowned because I wanted
>> to check the following:
>>
>> var document = Document()
>>
>> print(CFGetRetainCount(document._storageReference)) //=> 2
>> print(isKnownUniquelyReferenced(&document._storageReference)) // true
>>
>> var values = document.values
>>
>> print(CFGetRetainCount(values._storageReference)) //=> 2
>> print(isKnownUniquelyReferenced(&values._storageReference)) // false
>>
>> Why is the second check false, even if the property is marked as unowned
>> for the view?
>>
>> Next up, I don’t have an idea how to correctly COW optimize this view.
>> Assume the following scenario:
>>
>> Scenario A:
>>
>> var document = Document()
>>
>> // just assume we already added some values and can mutate safely on a given index
>> // mutation in place
>> document.values[0] = 10
>>
>> VS:
>>
>> Scenario B:
>>
>> var document = Document()
>>
>> let copy = document
>>
>> // just assume we already added some values and can mutate safely on a given index
>> // mutation in place
>> document.values[0] = 10 // <--- this should only mutate `document` but not `copy`
>>
>> We could change the subscript setter on the mutable view like this:
>>
>> set {
>>
>>     if !isKnownUniquelyReferenced(&self._storageReference) {
>>
>>         self._storageReference = ... // clone
>>     }
>>     self._storageReference.values[position] = newValue
>> }
>>
>> There is only one problem here. We’d end up cloning the storage every
>> time, because as shown in the very first example, even with unowned the
>> function isKnownUniquelyReferenced will return false for scenario A.
>>
>> Any suggestions?
>>
>> PS: In general I also wouldn’t want to use unowned because the view
>> should be able to outlive it’s parent.
>>
>>
>> --
>> Adrian Zubarev
>> Sent with Airmail
>>
>> _______________________________________________
>> swift-users mailing list
>> swift-users at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-users
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20161118/26988292/attachment.html>


More information about the swift-users mailing list