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
[SR-9074] NSKeyValueObservation swizzle is broken #3609
Comments
Whoops. If anyone would know this problem, it's you, Lily! @Catfish-Man, this wasn't intentional, was it? |
Last night I whipped up a fix for this (and for the other issues I filed), but I haven't tested it yet. I would really like to know what the radar says though. |
The original bug was an error message when using
This would have been a compiler bug at the time, and it also definitely suggests that we weren't trying to swizzle NSObject's implementation. The workaround:
The Radar itself is now marked as Verify, by me, so maybe we can just go back to the simple thing? |
What happens if you simply omit the I did notice the stdlib seems to be using explicit |
You're right about |
I would definitely like to remove the workaround if we can! |
That said, iirc the swizzling was intentional (sorry, it's been a bit since I wrote this). The case it was trying to handle is when a class hierarchy has a mix of objc-style and swift-style KVO, to make sure that each observation callback is "visible" to both. So the ObjC ones bubble up the class hierarchy, and then the Swift ones get invoked, and then if nobody handled it it crashes as usual. Crashing in that bogus a way was definitely not the intent though, and it's certainly possible I got something else about it wrong. |
@Catfish-Man It sounds like you're describing the swizzling for |
And that's what I get for commenting with a headache and not thinking clearly 🙂 In that case, yes, kill it with fire if the compiler allows now |
FWIW looks like the compiler bug is not yet fixed. Omitting the |
I've submitted PR #20103 for this issue. |
Merged: apple/swift@7c51419 |
Environment
Current Swift master (14d4235b571d7a4e2ae51854f0d665f96959d182)
Additional Detail from JIRA
md5: 0f5da0eb292c39647bd44dceb3feb212
Issue Description:
NSKeyValueObservation
has a rather weird implementation detail. Instead of implementingobserveValue(forKeyPath:of:change:context:)
directly, it implements a method_swizzle_me_observeValue(forKeyPath:of:change:context:)
instead, and then swizzles it overobserveValue(forKeyPath:of:change:context:)
. There's a comment referencing a radar rdar://problem/31640524 but as I am not an Apple employee I don't know what that radar says.In any case, this swizzle is broken. It's not replacing a method on
NSKeyValueObservation
, it's actually replacing the IMP for the root method onNSObject
. This means that ifNSObject
's method is ever invoked, it will actually callNSKeyValueObservation
's version, which will then attempt to read a Swift property that doesn't exist onNSObject
, interpret it as a weak object, convert it to a strong object, and compare it against the observed object. Assuming this itself doesn't crash, including the comparison (which BTW is using==
instead of the expected===
), if the comparison actually succeeds then it will read a second property (that doesn't exist) and attempt to invoke it as a block, which definitely will crash.Granted,
NSObject
's behavior here is to throw an exception, but an exception with specific information about the observation that wasn't properly handled is far far better than potentially interpreting garbage memory as an object, passing it to-[NSObject isEqual:]
, and crashing.Given all this, three fixes should be made:
A dummy implementation of
observeValue(forKeyPath:of:change:context:)
should be provided onNSKeyValueObservation
so the swizzle only affectsNSKeyValueObservation
instead of affecting all ofNSObject
. If this isn't possible (I'm really curious what that radar says), thenclass_addMethod
should be used to create this method with the desired IMP rather than exchanging it withNSObject
's.The
==
test should be replaced with===
, because we don't want to run-[NSObject isEqual:]
, we want to ensure it's literally the same object.If the equality test fails, this should call
super
rather than just bailing, so we get a proper exception. This covers the rather stupid scenario of someone adding a KVO observation passing theNSKeyValueObservation
object as the observing object.The text was updated successfully, but these errors were encountered: