Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Key-Value Observing: Add support for observing through a proxy #479

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

hmelder
Copy link
Contributor

@hmelder hmelder commented Dec 17, 2024

While not explicitly allowed, observing an object from a stand-in, a NSProxy subclass, works on macOS.
As outlined in #472, we are currently facing two issues preventing this to work:

  1. Class methods are not forwarded by design
  2. KVO currently tries to swizzle the stand-in, and not the underlying object

I've tried not to introduce additional overhead, and implemented - _underlyingClass in a NSObject and NSProxy category.
The implementation in NSObject simply returns [self class], while -[NSProxy _underlyingClass] calls [self valueForKey: @"class"] to retrieve the underlying class.

When swizzling, we check for a mismatch between -class and -_underlyingClass.

@hmelder hmelder requested a review from rfm as a code owner December 17, 2024 13:52
Copy link
Contributor

@rfm rfm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all looks reasonable, but the immediate question to me is whether it's actually doing the 'right' thing by altering the underlying object rather than the proxy?
Here we are basically saying we want to override/bypass the proxy for purposes of observing, rather than allowing the proxy to do what it's normally supposed to do (insulate the underlying object from other code).
It seems to me that we have two opposed systems in play: proxies are expected to protect objects from direct access, while KVC/KVO is expected to hack around normal protections to get at them internals of objects, and it's not immediately obvious which should take precedence.
I guess my question is which approach is Apple taking?

@hmelder
Copy link
Contributor Author

hmelder commented Dec 20, 2024

it's actually doing the 'right' thing by altering the underlying object rather than the proxy?

Thought about the same thing.

It seems to me that we have two opposed systems in play: proxies are expected to protect objects from direct access, while KVC/KVO is expected to hack around normal protections to get at them internals of objects, and it's not immediately obvious which should take precedence.

Apple's Foundation effectively strips away the stand-in, as -addObserver... is forwarded to the underlying object. This worked on GNUstep as well, but KVO then gathers information about dependent keys by invoking the class method +keyPathsForValuesAffectingValueForKey:. This is invoked directly with the underlying object's metaclass in Apple's Foundation. KVO is not even implemented for NSProxy.

@hmelder
Copy link
Contributor Author

hmelder commented Dec 20, 2024

But you are right that KVO on an NSProxy breaks encapsulation in various ways, and I personally think that this is a terrible API.

@rfm rfm requested review from rfm and removed request for rfm December 23, 2024 18:37
Copy link
Contributor

@rfm rfm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems good to go then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants