Swift以前は、Objective-Cでは、<objc/runtime.h>
を使用してクラスのメソッドをスウィズルまたはフックしていました。
Swiftのランタイムの変更と、CydiaSubstrateやこの分野で役立つ他のライブラリなどのフック関数のトピックに関する情報を誰かが持っている場合は、お知らせください。
Swiftでのメソッドのスウィズリングで成功しました。この例は、NSDictionaryで記述メソッドをフックする方法を示しています
私の実装:
extension NSDictionary {
func myDescription() -> String!{
println("Description hooked")
return "Hooooked " + myDescription();
}
}
スウィズリングコード:
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
}
編集済み:このコードは、任意のカスタムSwiftから継承するクラス)で機能しますNSObject(ただし、機能しないクラスでは機能しません。)その他の例- https://github.com/mbazaliy/MBSwizzler
Objective-Cクラスから継承するSwift生成クラスは、動的メソッドディスパッチを常に使用しているように見えるため、問題なくスウィズルできる可能性があります。ブリッジを介して渡されるため、Objective-Cランタイムに存在するSwiftで定義されたクラスのメソッドをスウィズルできる場合がありますが、Objective-Cのサイドメソッドは、ブリッジを介してSwift-サイドランタイムなので、それらをスウィズルすることが特に役立つかどうかは明確ではありません。
"Pure" Swiftメソッド呼び出しはobjc_msgSend
などを介して動的にディスパッチされているようには見えず、(短い実験から)Swiftはコンパイル時に実装され、実際の型情報の多くは非クラス型の実行時に存在しません(つまり、失われます)(どちらもSwiftの速度の利点とされていると考えられます)。
これらの理由から、Swiftのみのメソッドを有意にスウィズルすることは、Objective-Cメソッドをスウィズリングするよりもかなり難しく、おそらくObjective-Cメソッドのスウィズリングよりもmach_override
のように見えると思います。
他のどの回答も、あらゆる種類のクラスのメソッドのスウィズリングの明確な要件のセットを提供していないため、この質問に1年以上回答しています。
他の人が説明していることは、(NSDictionaryのような)ファウンデーション/ uikitクラスの拡張に対しては問題なく機能しますが、あなた自身の場合にはneverは機能しませんSwiftクラス。
here で説明したように、カスタムクラスでNSObjectを拡張する以外に、メソッドのスウィズリングには追加の要件があります。
スウィズルするSwiftメソッドはマークする必要がありますdynamic
。
マークを付けないと、メソッドポインターが正しくスワップされているように見えても、ランタイムはスウィズルされたメソッドではなく、元のメソッドを呼び出し続けます。
私はこの答えを拡張しました ブログ投稿で 。
Swift 2、Cocoapodsを使用して記述されたXcode 7 iOSプロジェクトがありました。Objective-Cソースを使用する特定のCocoapodでは、ポッドをフォークせずに短いメソッドをオーバーライドしたいと思いました。書き込みSwift私の場合、拡張機能は機能しません。
メソッドスウィズリングを使用するために、ココアポッドに置き換え/注入したいメソッドを使用して、メインバンドルに新しいObjective-Cクラスを作成しました。 (ブリッジヘッダーも追加)
Mbazaliyの stackflowのソリューション を使用して、次のようなコードを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にコードを配置しました。コードは1回だけ実行する必要があります。
また、ポッドのバンドルで参照されているいくつかのアセットをメインバンドルにコピーする必要がありました。
mbazaliyが提供するすばらしい答えを拡張したいと思います。
Swiftでスウィズリングを行う別の方法は、Objective-Cブロックを使用して実装を提供することです。
例えばクラスdescription
のNSString
methodを置き換えるには、次のように記述します。
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以降で機能します。
私はそのようにはしませんが、クロージャーが答えを提供すると思います(関数の呼び出しをインターセプト、評価、および転送する機会を与えるため、さらに、リフレクションがある場合とそうである場合に、拡張が容易になります。
http://www.Swift-studies.com/blog/2014/7/13/method-swizzling-in-Swift