Swift の前は、Objective-C で を使用してクラスのメソッドをスウィズルまたはフックしていました<objc/runtime.h>
。
Swift のランタイムの変更と、CydiaSubstrate などの関数のフックや、この分野で役立つ他のライブラリのトピックに関する情報を誰かが持っている場合は、私に知らせてください。
Objective-C クラスから継承する迅速に生成されたクラスは、常に動的メソッド ディスパッチを使用しているように見えるため、問題なくスウィズルできる可能性があります。ブリッジを渡って渡されることにより、Objective-C ランタイムに存在する、swift で定義されたクラスのメソッドを入れ替えることができる場合がありますが、Objective-C 側のメソッドは、ブリッジを渡って swift-そのため、それらをスウィズルすることが特に役立つかどうかは明らかではありません。
「純粋な」迅速なメソッド呼び出しは、次のような方法で動的にディスパッチされるようには見えず、objc_msgSend
(簡単な実験から) コンパイル時に迅速な型の安全性が実装され、実際の型情報の多くが存在しない (つまり、なくなっている)ように見えます。非クラス型の実行時 (どちらも、Swift の速度の利点とされるものに寄与する可能性があります)。
これらの理由から、Swift のみのメソッドを意味のあるスウィズリングすることは、Objective-C メソッドのスウィズリングよりもかなり難しく、Objective mach_override
-C メソッドのスウィズリングよりもはるかに似ていると思います。
あらゆる種類のクラスのメソッド スウィズリングの決定的な要件セットを提供する回答は他にないため、1 年以上後にこの質問に回答します。
他の人が説明しているものは、Foundation/uikit クラス (NSDictionary など) への拡張機能では問題なく機能しますが、独自の Swift クラスでは決して機能しません。
hereで説明されているように、カスタム クラスで NSObject を拡張する以外に、メソッドの入れ替えには追加の要件があります。
スウィズルしたい迅速なメソッドは、マークされている必要があります dynamic
。
マークしないと、メソッド ポインターが正しくスワップされているように見えても、ランタイムは単純に入れ替わったメソッドではなく元のメソッドを呼び出し続けます。
この回答をブログ投稿で展開しました。
Cocoapods を使用して、Swift 2 で記述された Xcode 7 iOS プロジェクトがありました。特定の Cocoapod で、Objective-C ソースを使用して、ポッドをフォークせずに短いメソッドをオーバーライドしたいと考えました。私の場合、Swift 拡張機能を作成してもうまくいきません。
メソッド スウィズリングを使用するために、メイン バンドルに新しい Objective-C クラスを作成し、cocoapod に置き換え/挿入したいメソッドを含めました。(ブリッジヘッダーも追加)
stackflow でmbazaliy のソリューションを使用して、これに似たコードを Appdelegate に入れましたdidFinishLaunchingWithOptions
。
let mySelector: Selector = "nameOfMethodToReplace"
let method: Method = class_getInstanceMethod(SomeClassInAPod.self, mySelector)
let swizzledMethod: Method = class_getInstanceMethod(SomeOtherClass.self, mySelector)
method_exchangeImplementations(method, swizzledMethod)
これは完璧に機能しました。@mbazaliy のコードとの違いは、SomeClassInAPod
最初にクラスのインスタンスを作成する必要がなかったことです。これは、私の場合は不可能でした。
注:コードを Appdelegate に入れました。これは、コードが実行されるたびに、元のメソッドと交換されるためです。一度だけ実行する必要があります。
また、Pod のバンドルで参照されていたいくつかのアセットをメイン バンドルにコピーする必要がありました。
mbazaliyが提供する素晴らしい回答を拡張したいと思います。
Swift でスウィズリングを行う別の方法は、Objective-C ブロックを使用して実装を提供することです。
description
たとえば、クラスのメソッドを置き換えるには、次のNSString
ように記述できます。
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)
これは Swift 1.1 以降で機能します。
それにしばらく時間を費やした後... 今朝目を覚ます.... ベータ6が出て、ベータ6で問題が修正されました! リリースノートから「動的ディスパッチは、クラス拡張で導入されたメソッドとプロパティのオーバーライドを呼び出すことができるようになり、Xcode 6 ベータ 5 で導入された回帰を修正しました。(17985819)!」