swift 中 属性 的方法调配

Method swizzling for property in swift

虽然可以替换 obj-c 中的 setMyProperty: 方法,但我想知道如何在 swift 中替换它?

例如我想替换UIScrollView::setContentOffset::

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

...但执行后 originalSelector 包含 setContentOffset:animaed。那么,如何将属性的setter方法传递给selector呢?

[进一步研究后重写]

这是一个基于以下内容的详细解决方法

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

[作者警告]

In closing, remember that tinkering with the Objective-C runtime should be much more of a last resort than a place to start. Modifying the frameworks that your code is based upon, as well as any third-party code you run, is a quick way to destabilize the whole stack. Tread softly!

就是这样,必须涵盖所有访问器和修改器,所以很多。 另外,由于您需要调解这些值但必须重新使用原始存储的 属性 因为您不能在此处引入任何新存储,所以您有一些看起来很奇怪的函数似乎是递归的但不是因为运行时调配。这是编译器第一次为我的代码生成警告,我知道在运行时会 错误

哦,好吧,这是一个有趣的学术练习。

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
        }
    }
}

从 Swift 2.3 (XCode 8) 开始,可以将 setter 和 getter 分配给选择器变量:

The Objective-C selectors for the getter or setter of a property can now be referenced with #selector. For example:

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

更多详情here