2015-12-16 5 views
0

Я следую Swift & the Objective-C Runtime, он работает для обычных методов.Как swizzle init в Swift

Мне нравится подкачать метод init, из моего понимания, init - это как метод класса. Поэтому я попробовал swizzling init как метод экземпляра и класса. Но это не похоже на работу

я могу заставить его работать, используя Objective C, просто интересно, как заставить его работать в Swift

Извлеченный из моей gist

dispatch_once(&Static.token) { 
      let originalSelector = Selector("init:source:destination:") 
      let swizzledSelector = Selector("ftg_init:source:destination:") 

      let originalMethod = class_getClassMethod(self, originalSelector) 
      let swizzledMethod = class_getClassMethod(self, swizzledSelector) 

      let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)) 

      if didAddMethod { 
       class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)) 
      } else { 
       method_exchangeImplementations(originalMethod, swizzledMethod); 
      } 
     } 
+0

Что ваш класс наследует от? Я считаю, что swizzling работает только с классами, которые наследуются от 'NSObject' или определяются как объективный класс c, используя' @ objc'. –

+0

@CleverError Я связался с моим вопросом в вопросе, вы можете посмотреть – onmyway133

ответ

3

При создании селектора для метод, вы должны основывать его на сигнатуре метода Obj C, поскольку swizzling выполняется с использованием среды выполнения Obj C.

Таким образом, оригинальный селектор должен быть

initWithIdentifier:source:destination:

Теперь это становится немного странно, так как ваш метод инициализации определяется так, что требуется метка на первый аргумент (при наличии identifier дважды), то селектор вы хотите использовать на самом деле

ftg_initWithIdentifier:source:destination:

единственный documentation я мог найти о нем говорить о переводе с Оби j C в Swift, но похоже, что обратное происходит от Swift до Obj C.

Далее, init... - это метод экземпляра, поэтому вам нужно сделать два изменения. Вам необходимо изменить class_getClassMethod на class_getInstanceMethod, и вам необходимо удалить class из вашего метода ft_init....

Так что, когда все сказано и сделано, ваш код должен выглядеть следующим образом (который работал для меня)

dispatch_once(&Static.token) { 
    let originalSelector = Selector("initWithIdentifier:source:destination:") 
    let swizzledSelector = Selector("ftg_initWithIdentifier:source:destination:") 

    let originalMethod = class_getInstanceMethod(self, originalSelector) 
    let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) 

    let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)) 

    if didAddMethod { 
     class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)) 
    } else { 
     method_exchangeImplementations(originalMethod, swizzledMethod); 
    } 
} 

func ftg_init(identifier identifier: String!, 
    source: UIViewController, 
    destination: UIViewController) -> UIStoryboardSegue { 

    return ftg_init(identifier: identifier, 
     source: source, 
     destination: destination.ftg_resolve()) 
} 
+0

Я пробовал, но он не работает – onmyway133

+0

Я обновил свой ответ с изменением, которое я забыл добавить. –

0

Вы также можете Swizzle в расширениях класса. Вот пример swizzling UIViewController-х viewDidLoad:

extension UIViewController { 

    // We cannot override load like we could in Objective-C, so override initialize instead 
    public override static func initialize() { 

     // Make a static struct for our dispatch token so only one exists in memory 
     struct Static { 
      static var token: dispatch_once_t = 0 
     } 

     // Wrap this in a dispatch_once block so it is only run once 
     dispatch_once(&Static.token) { 
      // Get the original selectors and method implementations, and swap them with our new method 
      let originalSelector = #selector(UIViewController.viewDidLoad) 
      let swizzledSelector = #selector(UIViewController.myViewDidLoad) 

      let originalMethod = class_getInstanceMethod(self, originalSelector) 
      let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) 

      let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)) 

      // class_addMethod can fail if used incorrectly or with invalid pointers, so check to make sure we were able to add the method to the lookup table successfully 
      if didAddMethod { 
       class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)) 
      } else { 
       method_exchangeImplementations(originalMethod, swizzledMethod); 
      } 
     } 
    } 

    // Our new viewDidLoad function 
    // In this example, we are just logging the name of the function, but this can be used to run any custom code 
    func myViewDidLoad() { 
     // This is not recursive since we swapped the Selectors in initialize(). 
     // We cannot call super in an extension. 
     self.myViewDidLoad() 
     print(#function) // logs myViewDidLoad() 
    } 
} 
+0

не работает для init –

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