Swift から Method Swizzling

MacDev

昔はよく使ってた Method Swizzling だけど、Objective-C ランタイムの機能なので Swift 時代になってからはなんとなく手を出さずにいた。でも実際に書いてみると何も import せずに関連関数を呼び出せたし、普通にすっきりしたコードで実現できるのね。

extension NSObject
{
    public class func swizzle(_ originalSelector: Selector, to replacedSelector: Selector)
    {
        method_exchangeImplementations(
            class_getInstanceMethod(self, originalSelector)!,
            class_getInstanceMethod(self, replacedSelector)!
        )
    }
}
少し便利な定義
extension NSCursor
{
    @objc public func swizzle_set()
    {
        //call original method
        swizzle_set()

        if (self == .arrow), NSEvent.modifierFlags.contains(.shift) {
            NSLog("\(self) set! \(Thread.callStackSymbols) \(NSApp.currentEvent)")
        }
    }
}

NSCursor.swizzle(#selector(NSCursor.set), to: #selector(NSCursor.swizzle_set))
使用例

例えばカーソルの変更みたいに頻繁に呼ばれる処理を調べたい場合、Xcode のデバッグ機能でシンボル -[NSCursor set] をトリガーに指定して止めるやり方では必要なタイミングに限定しにくいけど、Method Swizzling を使用すると「対象が黒矢印カーソルのとき」「Shift キーが押されているとき」のように好きな条件で発動させることができて簡単だ。

多用していたあの頃は SIMBL プラグインを作るという目的があったから特定の接頭辞のついた大量のメソッドを一括で置き換えるような処理を書いたりもしていたけど、単純にアプリケーションのバグ原因を調査する目的1であればそこまで必要ないでしょう。


  1. 実際は悩んだ末にここまでやってたどり着くのが AppKit 側のバグだったりするのが泣ける。 ↩︎

スペースバーを押したときだけ手のひらツールにする

MacDev

「スペースバーが押されているあいだだけ手のひらツールに変化1させるのが大変だった」という話を耳にしたので自分も挑戦してみた。もっときれいな方法や間違いがあったら教えてください。

→ 続きを読む


  1. 広く見かける UI だけど、現在の Apple 純正ソフトウェアで使えるのは Keynote ぐらい? ↩︎

© 2005-2018 zumuya