Copy-on-write in struct

As discussed at WWDC 2015 Building Better Apps with Value Types in Swift, I tried to test copy on write by myself:


struct BezierPath {
    private var _path = UIBezierPath()

    var pathForReading: UIBezierPath {
        return _path
    }

    var pathForWriting: UIBezierPath {
        mutating get {
            _path = _path.copy() as! UIBezierPath
            return _path
        }
    }
}

extension BezierPath {
    var isEmpty: Bool {
        return pathForReading.empty
    }
    mutating func addLineToPoint(point: CGPoint) {
        pathForWriting.moveToPoint(point)
    }
}


Basically, pretty much the same as the presentation. Only difference is at line 21 where I changed from addLineToPoint() to moveToPoint(). The change was reuiqred as addLineToPoint() fails if there's no point registered prior to the call.


Now, I ran some test as follows:


var path = BezierPath()
var path2 = path
if path.isEmpty { print("path(before) is empty") }
path.addLineToPoint(CGPoint(x: 10, y: 20))
if path.isEmpty { print("path(after) is empty") }
if path2.isEmpty { print("path2 is empty") }


And you get:


path(before) is empty
path(after) is empty
path2 is empty


It's obvious that the COW is not working for struct. After doing some more tests, I found that property _path is not actually updated in pathForWriting. I've tested this with Swift 1.2 and Swift 2.0 in Xcode 7 beta.


Mutating in func works fine. But mutating in computed property, seems to be not working.


Does anyone have any idea?

It looks like copy isnt really copying in the UIBezierPath object, just returning the same pointer. The best way to ensure this is to use


convenience init(CGPath

CGPath
: CGPath)


where the CGPath is recoved from the existing path


_path = UIBezierPath(_path.CGPath)

I tried your code in the playground and I added some print statements. You are right. The "pathForWriting" property creates and returns a new copy but the internal variable _path is unchanged. If you use a mutating func instead, it works.


Did you try this in the playground or as a compiled program? Just wondering if this might be a playground only bug.

Hi Taki,


Sorry for the trouble. Try adding a set to the computed property, like this:


    var pathForWriting: UIBezierPath {
        mutating get {
            _path = _path.copy() as! UIBezierPath
            return _path
        }
        set {
            _path = newValue
        }
    }


This works as intended in a project. I'll post again shortly with a version that works in a playground as well.

HI Taki,


I can't figure out how to post a file to the forum. The trick is to put the definition of the BezierPath struct into the aux sources in the playground.


Let me know if something doesn't work for you.

Yes, I tried this in the playground. Seems like as dudney mentioned, it seems like we need some trick to make it work in the playground.

Hi Dudney,


Thank you for your help. I'll try as you suggest.

I hope that is a kludge to get around a current compiler bug, and not the way it is actually supposed to work.

Hey Drinky Bird,


There are two bugs here

- the set is required in a computed property with a mutating get

- Playgrounds don't execute properly when there is a mutating get in a computed property


Putting set into the computed property works around the compiler bug.

Putting the BezierPath into aux srcs works around the Playground execution bug.


Hope that helps.

Yes, that helped quite a bit. Thanks!

Copy-on-write in struct
 
 
Q