Alfred Woo
Alfred Woo

Reputation: 716

Method Swizzling and Inheritance

As I know, method swizzling with method_exchangeImplementations(A, B) function exchanges A method and B method in runtime.

If I get method with:

let A = class_getInstanceMethod(UIViewController.self, #selector(UIViewController.viewDidAppear))

it looks like Swift runtime replaces viewDidAppear method of UIViewController class -- superclass of SomeViewController inherits from.

But as I know, inheritance works on compile time, not in a runtime. In this case, how do they know, on runtime, that which superclass method (swizzled method or original method) should be called?

Here is simple example

AppDelegate.swift

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    let originalMethod = class_getInstanceMethod(UIViewController.self, #selector(UIViewController.viewDidAppear))
    let swizzledMethod = class_getInstanceMethod(UIViewController.self, #selector(UIViewController.s_viewDidAppear))
    if let originalMethod, let swizzledMethod {
        method_exchangeImplementations(originalMethod, swizzledMethod)
    }
    return true
}

UIViewController+S.swift

extension UIViewController {
    func s_viewDidAppear(_ animated: Bool) {
        print("s_viewDidAppear")
    }
}

SViewController.swift

class SViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("SViewController.viewDidAppear")
    }
}

and this prints

s_viewDidAppear
SViewController.viewDidAppear

I expected only SViewController.viewDidAppear prints since inheritance works on compile time, and viewDidAppear method is swapped. And now I think runtime only swaps viewDidAppear of UIViewController because s_viewDidAppear is being printed. And this became conflict to me.

Upvotes: 0

Views: 115

Answers (1)

Cy-4AH
Cy-4AH

Reputation: 4615

It's Objective-c's runtime, despite that it's implemented in Swift. Better to implement it with Objective-C to avoid confusions.

It's sending messages, instead of calling functions. In compile time is determined, that super class is UIViewController, and you will sending message with selector @selector(viewDidAppear). Then when application executing, runtime will just receive message: call implementation of [UIViewController class] with viewDidAppear selectior.

You have exchanged implementations at app start, so new one will be called instead. Btw you should have such implementation to call original code:

extension UIViewController {
    func s_viewDidAppear(_ animated: Bool) {
        s_viewDidAppear(animated)
        print("s_viewDidAppear")
    }
}

Missing super's viewDidAppear can break something.

Upvotes: 1

Related Questions