[Pitch] Observation

There's a number of issues we ran into with KVO, all around concurrency:

  • If you're using locks to protect your state, you want to send KVO notifications outside the lock (recursive locks aren't sufficient because they can just dispatch_sync to another queue and then deadlock), which means if you send will- and did-change and want to avoid sending change notifications when nothing changed you need to do:

    lock
    check if the new value is different from the old value
    unlock
    will change
    lock
    change
    unlock
    did change
    

    and even that is thorny because someone else could come in when you unlock to send will change, and set a different new value, like this:

    thread 1: check if the new value is different from the old value
    thread 2: set the same new value as thread 1 is trying to
    thread 1: send will change
    recipient: look up current (supposedly pre-change) value
    thread 1: set new value
    thread 1: send did change
    recipient: look up new value, see it's the same as the old value
    developer of recipient: why is this framework so buggy
    

    (or the reverse situation where we decide to not notify because it's redundant, and it becomes non-redundant between the will and the did)

  • If you're getting the updated value "from the outside" and don't have it cached, then you may simply be unable to do this. This came up with iCloud UserDefaults, where we could get a change notification from another device, and by the time it made it to the client process the old value was gone.

  • With manual willChange/didChange, it's possible for developers to mis-nest them, like

    willChange: A
    willChange: B
    didChange: A
    didChange: B
    

    which greatly complicates the internal machinery for passing state between matched will and did pairs. The KVO test suite has a bunch of truly horrendous tests for edge cases like "what happens if many threads simultaneously mis-nest will/did while reentrantly setting the property being observed from inside the observation callback and occasionally throwing and catching exceptions?"

Most of these issues matter way less when you're only using observation for UI updates on the main thread of course. I don't think these are necessarily fatal flaws, but it's worth thinking things through carefully.

19 Likes