Sunday, July 6, 2014

Swizzling and Touch Forwarding

Peter Steinberger:

This works as expected in most cases, but has the issue that the original implementation will be called with a different _cmd than it expects. This can be a problem when _cmd is actually used, such as in the touch forwarding logic. I learned this the hard way after swizzling touchesMoved:withEvent: to inject additional logic. The app crashed with the popular doesNotRecognizeSelector: exception.

Instead of exchanging the method implementations, one could replace the old method and store its IMP outside the runtime. But then:

We are now modifying the touchesBegan:withEvent: method of our custom UIView subclass. There is no default implementation yet, so we get the IMP from UIResponder and then manually call this. Imagine if at some later point, somebody else would swizzle touchesBegan:withEvent: on UIView directly using the same technique. Assuming UIView has no custom touch handling code, they would get the IMP from UIResponder and add a new method to UIView. But then our method gets called, which already captured the IMP of UIResponder and completely ignores the fact that we modified UIView as well.

Perhaps I’m missing something, but it seems like it would be possible to exchange the method implementations. When you need to call the old method, use the new selector to fetch the old IMP. Then call the old IMP passing in the old selector. Then the old method would still receive its expected selector, and you would still be safe if other code swizzles the same method.

Comments RSS · Twitter

Leave a Comment