What exactly to you mean by unsafe/invalide? If you refer to unowned than yes 
this is incorrect. That was only part of the first question about 
isKnownUniquelyReferenced, where I myself missed the part from the docs that 
says that weak references don’t affect the result.

In general DocumentValues has a strong reference to the storage and should be 
able to outlive Document.

I borrowed the main idea from:

proposal: 
https://github.com/natecook1000/swift-evolution/blob/nc-dictionary-collections/proposals/0000-dictionary-key-and-value-collections.md
implementation: 
https://github.com/apple/swift/compare/master…natecook1000:nc-dictionary
But it uses some buildin stuff that I don’t have. :/



-- 
Adrian Zubarev
Sent with Airmail

Am 18. November 2016 um 16:43:40, Karl (razie...@gmail.com) schrieb:


On 18 Nov 2016, at 16:40, Karl <raziel.im+swift-us...@gmail.com> wrote:


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
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) }

Oops.. actually, I think this should be:
        defer { _fixLifetime(self) }

        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