10

obj-c でメソッドを置き換えることは可能setMyProperty:ですが、swift で行う方法を知りたいですか?

たとえば、私は置き換えたいUIScrollView::setContentOffset:

let originalSelector: Selector = #selector(UIScrollView.setContentOffset)
let replaceSelector: Selector = #selector(UIScrollView.setContentOffsetHacked)
...

...しかし、実行後originalSelectorに含まれていますsetContentOffset:animaed。では、プロパティの setter メソッドを に渡す方法はselector?

4

2 に答える 2

10

[さらなる研究の後に書き直された]

以下に基づいた詳細な回避策を次に示します。

http://nshipster.com/swift-objc-runtime/

【作者からの警告】

最後に、Objective-C ランタイムをいじるのは、開始する場所ではなく、最後の手段であることを忘れないでください。コードのベースとなっているフレームワークや実行するサードパーティ コードを変更すると、スタック全体が不安定になります。ふんわり踏む!

これで、すべてのアクセサーとミューテーターをカバーする必要があるため、非常に多くなります。さらに、値に介入する必要がありますが、ここでは新しいストレージを導入できないため、元の保存されたプロパティを再利用する必要があるため、再帰的に見えるが実行時のスウィズリングが原因ではない奇妙な関数がいくつかあります。実行時に間違っていることがわかっているコードに対して、コンパイラが警告を生成したのはこれが初めてです。

まあ、それは興味深い学問的な演習です。

extension UIScrollView {
    struct StaticVars {
        static var token: dispatch_once_t = 0
    }

    public override class func initialize() {
        dispatch_once(&StaticVars.token) {
            guard self == UIScrollView.self else {
                return
            }
            // Accessor
            method_exchangeImplementations(
                class_getInstanceMethod(self, Selector("swizzledContentOffset")),
                class_getInstanceMethod(self, Selector("contentOffset"))
            )
            // Two-param setter
            method_exchangeImplementations(
                class_getInstanceMethod(self, #selector(UIScrollView.setContentOffset(_:animated:))),
                class_getInstanceMethod(self, #selector(UIScrollView.swizzledSetContentOffset(_:animated:)))
            )
            // One-param setter
            method_exchangeImplementations(
                class_getInstanceMethod(self, #selector(UIScrollView.swizzledSetContentOffset(_:))),
                class_getInstanceMethod(self, Selector("setContentOffset:")))
        }
    }

    func swizzledSetContentOffset(inContentOffset: CGPoint, animated: Bool) {
        print("Some interceding code for the swizzled 2-param setter with \(inContentOffset)")
        // This is not recursive. The method implementations have been exchanged by runtime. This is the
        // original setter that will run.
        swizzledSetContentOffset(inContentOffset, animated: animated)
    }


    func swizzledSetContentOffset(inContentOffset: CGPoint) {
        print("Some interceding code for the swizzled 1-param setter with \(inContentOffset)")
        swizzledSetContentOffset(inContentOffset) // not recursive
    }


    var swizzledContentOffset: CGPoint {
        get {
            print("Some interceding code for the swizzled accessor: \(swizzledContentOffset)") // false warning
            return swizzledContentOffset // not recursive, false warning
        }
    }
}
于 2016-08-04T22:27:31.187 に答える
9

Swift 2.3 (XCode 8) 以降では、セッターとゲッターをセレクター変数に割り当てることができます。

プロパティのゲッターまたはセッターの Objective-C セレクターを #selector で参照できるようになりました。例えば:

let sel: Selector = #selector(setter: UIScrollView.contentOffset)

詳細はこちら

于 2016-09-16T12:13:56.423 に答える