[さらなる研究の後に書き直された]
以下に基づいた詳細な回避策を次に示します。
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
}
}
}