-
Notifications
You must be signed in to change notification settings - Fork 283
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
GSFFIInvocation: Use objc_msg_lookup
in -invokeWithTarget: to avoid skipping hidden classes
#473
Conversation
The new KVO implementation for libobjc2/clang, located in Source/NSKVO*, reuses or installs a hidden class and subsequently adds the swizzled method to the hidden class. Make sure that the invocation mechanism calls the swizzled method.
The current implementation skips hidden classes, which breaks KVO. It turns out that GSGetMethod + method_getImplementation is about 50% slower than objc_msg_lookup (gnustep-2.2 ABI).
GCC does not support private ivar definitions in the implementation block.
Seems like legacy KVO is not happy with the new tests. Should I mark them as hopeful?
|
Assuming that #474 (comment) is refering to this PR: libobjc2 provides a safe way to do IMP caching: You lookup the slot and check the cache version, before invoking IMP. If there is a mismatch, a new lookup is required. We can probably write a shim for the GCC runtime which defines slots. |
Here are the APIs (located in /**
* Look up a slot, without invoking any forwarding mechanisms. The third
* parameter is used to return a pointer to the current value of the version
* counter. If this value is equal to `objc_method_cache_version` then the
* slot is safe to reuse without performing another lookup.
*/
OBJC_PUBLIC
extern struct objc_slot2 *objc_get_slot2(Class, SEL, uint64_t*)
OBJC_NONPORTABLE;
/**
* Look up a slot, invoking any required forwarding mechanisms. The third
* parameter is used to return a pointer to the current value of the version
* counter. If this value is equal to `objc_method_cache_version` then the
* slot is safe to reuse without performing another lookup.
*/
OBJC_PUBLIC
extern struct objc_slot2 *objc_slot_lookup_version(id *receiver, SEL selector, uint64_t*)
OBJC_NONPORTABLE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this all seems good to me, and yes the tests that fail under the old kvo implementation should be marked as hopeful since we aren't expecting them to pass.
When observing properties via a proxy object, all messages are encapsulated into an NSInvocation object.
From the example in #472, the following messages are forwarded to the underlying object:
addObserver:forKeyPath:options:context:
removeObserver:forKeyPath:options:context:
setName:
When adding a observer for the first time (assuming that there are no associated objects), a hidden subclass is created and attached (ISA changes) to the TObject instance.
setName:
is then swizzled and added to the hidden subclass.The current invocation mechanism uses APIs that skip over hidden classes during method lookup, resulting in the original IMP being called instead of the swizzled one.
This is documented in NSKVOSwizzling:
libs-base/Source/NSKVOSwizzling.m
Lines 96 to 111 in 9cdb4f9
Here is a quick micro benchmark to compare the old mechanism with
objc_msg_lookup
. Please note, that I have not benchmarked the legacy GCC runtime.System