> On 18 Nov 2016, at 13:05, Adrian Zubarev via swift-users 
> <swift-users@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@swift.org <mailto:swift-users@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-users 
> <https://lists.swift.org/mailman/listinfo/swift-users>


This is kind of an invalid/unsafe design IMO; DocumentValues may escape the 
scope of the Document and the underlying storage may be deallocated.

Instead, I’d recommend a function:

func withDocumentValues<T>(_ invoke: (inout DocumentValues)->T) -> T {
        var view = DocumentValues(self)
        defer { _fixLifetime(view) }
        return invoke(&view)
}

(unfortunately, this isn’t completely safe because somebody could still copy 
the DocumentValues from their closure, the same way you can copy the pointer 
from String’s withCString, but that’s a limitation of Swift right now)

CC: John McCall, because I read his suggestion in the thread about contiguous 
memory/borrowing that we could have a generalised @noescape. In this example, 
you would want the DocumentValues parameter in the closure to be @noescape.

- Karl
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to