快速处理属性的方法

bri*_*dir 6 objective-c-runtime selector ios swift method-swizzling

虽然可以setMyProperty:在obj-c中替换方法,但我想知道如何快速进行?

例如我要替换UIScrollView::setContentOffset:

let originalSelector: Selector = #selector(UIScrollView.setContentOffset)
let replaceSelector: Selector = #selector(UIScrollView.setContentOffsetHacked)
...
Run Code Online (Sandbox Code Playgroud)

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

Bas*_*Zen 6

[经过进一步研究后改写]

这是基于以下内容的精心设计的解决方法

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

[作者警告]

最后,请记住,修补Objective-C运行时应该是最后的选择,而不是开始的地方。修改代码所基于的框架以及运行的任何第三方代码,都是破坏整个堆栈稳定的快速方法。轻踩一下!

就是这样,所有访问器和mutator都必须包含在内,因此很多。另外,由于您需要求取值,但由于无法在此处引入任何新的存储而必须重新使用原始的存储属性,因此您会看到一些看起来很奇怪的函数,这些函数似乎是递归的,但并非由于运行时混乱而引起。这是编译器第一次为我的代码生成警告,我知道在运行时会出错

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

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


bri*_*dir 5

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

现在可以使用#selector引用属性的getter或setter的Objective-C选择器。例如:

let sel: Selector = #selector(setter: UIScrollView.contentOffset)
Run Code Online (Sandbox Code Playgroud)

在这里更多细节