didSet and time when propagation of mutation happens


(Diego Sánchez) #1

Hi all,

The following snippet summarises an issue I was investigating:

struct Observable<T> {
    var value: T {
        didSet {
            print("Observable.didSet")
            callback?()
        }
    }
    var callback: (() -> Void)?
}

class MyClass {
    var myString: Observable<String> {
        get {
            return _myString
        }
        set {
            print("MyClass.Setter")
            self._myString = newValue
        }
    }

    private var _myString: Observable<String>
    init (string: Observable<String>) {
        self._myString = string
        print("MyClass.init.end")
    }
}

let myClass = MyClass(string: Observable<String>(value: "1", callback: nil))
myClass.myString.callback = {
    print(myClass.myString.value)
}
myClass.myString.value = "2"

*Output:*
MyClass.init.end
MyClass.Setter
Observable.didSet
*1*
MyClass.Setter

Obviously I wasn't expecting to get "1", but "2" in the callback's print,
and this happens because MyClass.setter is called after Observable.didSet
completes.

Is it feasible that all the *set*s are called first, and then all the
*didSet*s?

Cheers,
Diego


(Zhao Xin) #2

I think the order is right.

value.set >> value.didSet >> string.set >> string.didSet

you expected value.set >> string.set >> value.didSet >> string.didSet is
not correct.

The value "1" is not you expected. However, that is something that I think
is tricky. For closure

{
    print(myClass.myString.value)
}

it captured the value. However, unless Objective-C, Swift will decide if it
is a static capture or an inout capture. So it should be consider right to
be "1" or "2"? I am not sure about that.

Zhaoxin

···

On Thu, Jul 14, 2016 at 8:36 AM, Diego Sánchez <swift-users@swift.org> wrote:

Hi all,

The following snippet summarises an issue I was investigating:

struct Observable<T> {
    var value: T {
        didSet {
            print("Observable.didSet")
            callback?()
        }
    }
    var callback: (() -> Void)?
}

class MyClass {
    var myString: Observable<String> {
        get {
            return _myString
        }
        set {
            print("MyClass.Setter")
            self._myString = newValue
        }
    }

    private var _myString: Observable<String>
    init (string: Observable<String>) {
        self._myString = string
        print("MyClass.init.end")
    }
}

let myClass = MyClass(string: Observable<String>(value: "1", callback:
nil))
myClass.myString.callback = {
    print(myClass.myString.value)
}
myClass.myString.value = "2"

*Output:*
MyClass.init.end
MyClass.Setter
Observable.didSet
*1*
MyClass.Setter

Obviously I wasn't expecting to get "1", but "2" in the callback's print,
and this happens because MyClass.setter is called after Observable.didSet
completes.

Is it feasible that all the *set*s are called first, and then all the
*didSet*s?

Cheers,
Diego

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


(Diego Sánchez) #3

I don't know... when didSet is called the struct is already mutated so I
would expect all the references to be updated as well.

Regarding the capture in the closure it should always capture a reference
to "myClass", and call the accessors in the moment the closure is executed.
Adding

myClass.myString.value = "3"

will print "2".

because myClass.myString still holds the previous struct.

I will solve this problem by making Observable a reference type, but I was
wondering if someone could share more details about this behaviour.
Cheers,
Diego.

···

2016-07-14 5:19 GMT+02:00 Zhao Xin <owenzx@gmail.com>:

I think the order is right.

value.set >> value.didSet >> string.set >> string.didSet

you expected value.set >> string.set >> value.didSet >> string.didSet is
not correct.

The value "1" is not you expected. However, that is something that I think
is tricky. For closure

{
    print(myClass.myString.value)
}

it captured the value. However, unless Objective-C, Swift will decide if
it is a static capture or an inout capture. So it should be consider right
to be "1" or "2"? I am not sure about that.

Zhaoxin

On Thu, Jul 14, 2016 at 8:36 AM, Diego Sánchez <swift-users@swift.org> > wrote:

Hi all,

The following snippet summarises an issue I was investigating:

struct Observable<T> {
    var value: T {
        didSet {
            print("Observable.didSet")
            callback?()
        }
    }
    var callback: (() -> Void)?
}

class MyClass {
    var myString: Observable<String> {
        get {
            return _myString
        }
        set {
            print("MyClass.Setter")
            self._myString = newValue
        }
    }

    private var _myString: Observable<String>
    init (string: Observable<String>) {
        self._myString = string
        print("MyClass.init.end")
    }
}

let myClass = MyClass(string: Observable<String>(value: "1", callback:
nil))
myClass.myString.callback = {
    print(myClass.myString.value)
}
myClass.myString.value = "2"

*Output:*
MyClass.init.end
MyClass.Setter
Observable.didSet
*1*
MyClass.Setter

Obviously I wasn't expecting to get "1", but "2" in the callback's print,
and this happens because MyClass.setter is called after Observable.didSet
completes.

Is it feasible that all the *set*s are called first, and then all the
*didSet*s?

Cheers,
Diego

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


(Zhao Xin) #4

I think the problem is that you should not call callback?() in didSet() as
the values in the closure are updated depending on the function, which
means, it is not updated until the didSet() function finished.

Zhaoxin

···

On Fri, Jul 15, 2016 at 6:17 AM, Diego Sánchez <diego.sanchezr@gmail.com> wrote:

I don't know... when didSet is called the struct is already mutated so I
would expect all the references to be updated as well.

Regarding the capture in the closure it should always capture a reference
to "myClass", and call the accessors in the moment the closure is executed.
Adding

myClass.myString.value = "3"

will print "2".

because myClass.myString still holds the previous struct.

I will solve this problem by making Observable a reference type, but I was
wondering if someone could share more details about this behaviour.
Cheers,
Diego.

2016-07-14 5:19 GMT+02:00 Zhao Xin <owenzx@gmail.com>:

I think the order is right.

value.set >> value.didSet >> string.set >> string.didSet

you expected value.set >> string.set >> value.didSet >> string.didSet
is not correct.

The value "1" is not you expected. However, that is something that I
think is tricky. For closure

{
    print(myClass.myString.value)
}

it captured the value. However, unless Objective-C, Swift will decide if
it is a static capture or an inout capture. So it should be consider right
to be "1" or "2"? I am not sure about that.

Zhaoxin

On Thu, Jul 14, 2016 at 8:36 AM, Diego Sánchez <swift-users@swift.org> >> wrote:

Hi all,

The following snippet summarises an issue I was investigating:

struct Observable<T> {
    var value: T {
        didSet {
            print("Observable.didSet")
            callback?()
        }
    }
    var callback: (() -> Void)?
}

class MyClass {
    var myString: Observable<String> {
        get {
            return _myString
        }
        set {
            print("MyClass.Setter")
            self._myString = newValue
        }
    }

    private var _myString: Observable<String>
    init (string: Observable<String>) {
        self._myString = string
        print("MyClass.init.end")
    }
}

let myClass = MyClass(string: Observable<String>(value: "1", callback:
nil))
myClass.myString.callback = {
    print(myClass.myString.value)
}
myClass.myString.value = "2"

*Output:*
MyClass.init.end
MyClass.Setter
Observable.didSet
*1*
MyClass.Setter

Obviously I wasn't expecting to get "1", but "2" in the callback's
print, and this happens because MyClass.setter is called after
Observable.didSet completes.

Is it feasible that all the *set*s are called first, and then all the
*didSet*s?

Cheers,
Diego

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