【问题标题】:Method swizzling for property in swift快速调整属性的方法
【发布时间】:2016-08-04 22:17:31
【问题描述】:

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

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

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

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

【问题讨论】:

  • 这篇文章可以帮到你nshipster.com/swift-objc-runtime
  • 如果你想追到兔子洞:stackoverflow.com/questions/25651081/…
  • @VictorSigler 那篇文章没有用属性设置器介绍这种特殊情况。
  • @BaseZen,您提到的问题和答案也是如此。我正在寻找如何处理属性设置器,特别是我想知道为什么从#selector(UIScrollView.setContentOffset) 中选择了错误的方法。
  • 答案已重做,所以我很好奇现在是否符合您的需求。

标签: ios swift selector objective-c-runtime method-swizzling


【解决方案1】:

[进一步研究后重写]

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

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

【讨论】:

  • 谢谢,但我真的需要将setContentOffset 替换为UIScrollView 及其子类。原因是 - 我正在实现自定义的下拉刷新控制。
  • 我在UIScrollView 接口中看到public var contentOffset: CGPoint - 所以它是读写的,而不是只读的。 setContentOffset(animated:) 是独立方法。拖动滚动视图时,其contentOffset 是通过setter 设置的,而不是通过setContentOffset:animated 设置的。看来您的回答与我的问题无关。
  • 你说得对,我误读了它在 Xcode 中如何显示为“V”,认为这意味着只读。重做。无论如何,看看我的新cmets。一切似乎都奏效了。
【解决方案2】:

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

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

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

更多详情here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多