2016-08-04 2 views
3

Хотя в obj-c можно заменить метод setMyProperty:, мне интересно, как это сделать быстро?Метод swizzling для свойства в swift

Например, я хочу, чтобы заменить UIScrollView::setContentOffset::

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

... но после выполнения originalSelector содержит setContentOffset:animaed. Итак, как передать метод setter свойства на selector?

+0

Эта статья может помочь вам http://nshipster.com/swift-objc-runtime/ –

+1

И если вы хотите, чтобы преследовать вниз кролика отверстие : http://stackoverflow.com/questions/25651081/method-swizzling-in-swift?rq=1 – BaseZen

+0

@VictorSigler в этой статье не рассматривается этот специальный случай с установщиком свойств. – brigadir

ответ

2

Начиная от Swift 2.3 (XCode 8) можно назначить сеттер и геттер селектор переменные:

Объективных-C селекторы для геттера или сеттеров свойства может теперь будет ссылаться с #selector. Например:

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

Более подробную информацию here

3

[переписано после проведения дальнейших исследований]

Вот сложный обходной путь на основе ниже

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

Спасибо, но мне действительно нужно заменить 'setContentOffset' для' UIScrollView' и его подклассов. Причина в том, что я внедряю пользовательский контроль над потоком-обновлением. – brigadir

+0

OK, сделано. Дайте мне знать, как это работает. – BaseZen

+0

Я вижу 'public var contentOffset: CGPoint' в интерфейсе' UIScrollView', поэтому он читает-записывает, а не только на чтение. 'setContentOffset (анимированный:)' является автономным методом. При перетаскивании прокрутки, его 'contentOffset' устанавливается через setter, а не через' setContentOffset: анимированный'. Похоже, ваш ответ не касается моей проблемы. – brigadir

Смежные вопросы