Swift function swizzling / runtime

北城以北 提交于 2019-12-17 15:43:27

问题


Before Swift, in Objective-C I would swizzle or hook methods in a class using <objc/runtime.h>.

If anyone has any info on the topic of modifying Swift's runtime and hooking functions like CydiaSubstrate and other libraries that helped in this area, please inform me.


回答1:


I've succeed with method swizzling in Swift. This example shows how to hook description method on NSDictionary

My implementation:

extension NSDictionary {
     func myDescription() -> String!{
        println("Description hooked")
        return "Hooooked " + myDescription();
    }
}

Swizzling code:

func swizzleEmAll() {
        var dict:NSDictionary = ["SuperSecret": kSecValueRef]
        var method: Method = class_getInstanceMethod(object_getClass(dict), Selector.convertFromStringLiteral("description"))

        println(dict.description) // Check original description

        var swizzledMethod: Method = class_getInstanceMethod(object_getClass(dict), Selector.convertFromStringLiteral("myDescription"))
        method_exchangeImplementations(method, swizzledMethod)

        println(dict.description) //Check that swizzling works
    }

Edited: This code will work for any custom Swift class that inherits from NSObject (but will not work for classes that don't.) More examples - https://github.com/mbazaliy/MBSwizzler




回答2:


You would likely be able to swizzle swift-generated classes that inherit from Objective-C classes with no problem, since they appear to use dynamic method dispatch all the time. You may be able to swizzle methods of swift-defined classes that exist in the Objective-C runtime by virtue of being passed across the bridge, but the Objective-C side methods are likely to just be proxies back across the bridge to the swift-side runtime, so it's not clear that it'd be particularly helpful to swizzle them.

"Pure" swift method calls do not appear to be dispatched dynamically via anything like objc_msgSend and it appears (from brief experimentation) that the type safety of swift is implemented at compile time, and that much of the actual type information is absent (i.e. gone) at runtime for non-class types (both of which likely contribute to the purported speed advantages of swift.)

For these reasons, I expect that meaningfully swizzling swift-only methods will be significantly harder than swizzling Objective-C methods, and will probably look a lot more like mach_override than Objective-C method swizzling.




回答3:


I'm answering this question more than one year later because none of the other answers provide the definitive set of requirements for method swizzling for every kind of class.

What is described by other, while it will work flawlessly for extensions to foundation/uikit classes (like NSDictionary), will simply never work for your own Swift classes.

As described here, there is an additional requirement for method swizzling other than extending NSObject in your custom class.

The swift method you want to swizzle must be marked dynamic.

If you don't mark it, the runtime will simply continue to call the original method instead of the swizzled one, even if the method pointers appear to have been swapped correctly.

Update:

I've expanded this answer in a blog post.




回答4:


I had a Xcode 7 iOS project written in Swift 2, using Cocoapods. In a specific Cocoapod, with Objective-C source, I wanted to override a short method, without forking the pod. Writing a Swift extension wouldn't work in my case.

For using method swizzling, I created a new Objective-C class in my main bundle with the method I wanted to replace/inject into the cocoapod. (Also added the bridging header)

Using mbazaliy 's solution on stackflow, I put my code similar to this into the didFinishLaunchingWithOptions in my Appdelegate:

    let mySelector: Selector = "nameOfMethodToReplace"
    let method: Method = class_getInstanceMethod(SomeClassInAPod.self, mySelector)
    let swizzledMethod: Method = class_getInstanceMethod(SomeOtherClass.self, mySelector)
    method_exchangeImplementations(method, swizzledMethod)

This worked perfectly. The difference between @mbazaliy 's code is that I didn't need to create an instance of the SomeClassInAPod class first, which in my case would have been impossible.

Note: I put the code in the Appdelegate because every other time the code runs, it exchanges the method for the original - it should only run one time.

I also needed to copy some assets that were referenced in the Pod's bundle to the main bundle.




回答5:


I wouldn't do it that way, I think closures provide the answers (as they give you a chance to intercept, evaluate, and forward the invocation of the function, additionally it will be easy to extend when and if we have reflection.

http://www.swift-studies.com/blog/2014/7/13/method-swizzling-in-swift




回答6:


I would like to extend the great answer provided by mbazaliy.

Another way of doing swizzling in Swift is by providing an implementation using an Objective-C block.

e.g. to replace descriptionmethod on class NSString we can write:

let originalMethod = class_getInstanceMethod(NSString.self, "description")

let impBlock : @objc_block () -> NSString =
        { () in return "Bit of a hack job!" }

let newMethodImp = imp_implementationWithBlock(unsafeBitCast(impBlock, AnyObject.self))

method_setImplementation(originalMethod, newMethodImp)

This works as of Swift 1.1.




回答7:


After spending some time on it... Wake up this morning.... beta 6 is out and Problem Fixed in beta6! From release notes "Dynamic dispatch can now call overrides of methods and properties introduced in class extensions, fixing a regression introduced in Xcode 6 beta 5. (17985819)!"



来源:https://stackoverflow.com/questions/24019683/swift-function-swizzling-runtime

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!