ios – 方法Swizzling不起作用

前端之家收集整理的这篇文章主要介绍了ios – 方法Swizzling不起作用前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我想利用方法调配,但我甚至无法得到简单的例子来为我工作.我可能误解了这个概念是什么,但据我所知,它允许交换方法实现.

给定两个方法A和B,我想交换它们的实现,这样调用A就会执行B.我偶然发现了一些混合的例子(example1example2).我创建了一个带有类的新项目来测试它.

  1. class Swizzle: NSObject
  2. {
  3. func method()
  4. {
  5. print("A");
  6. }
  7. }
  8.  
  9. extension Swizzle
  10. {
  11. override class func initialize()
  12. {
  13. struct Static
  14. {
  15. static var token: dispatch_once_t = 0;
  16. }
  17.  
  18. // make sure this isn't a subclass
  19. if (self !== Swizzle.self)
  20. {
  21. return;
  22. }
  23.  
  24. dispatch_once(&Static.token)
  25. {
  26. let originalSelector = Selector("method");
  27. let swizzledSelector = Selector("methodExt");
  28.  
  29. let originalMethod = class_getInstanceMethod(self,originalSelector);
  30. let swizzledMethod = class_getInstanceMethod(self,swizzledSelector);
  31.  
  32. print(method_getImplementation(originalMethod));
  33. print(method_getImplementation(swizzledMethod));
  34.  
  35. let didAddMethod = class_addMethod(self,originalSelector,method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod));
  36.  
  37. if didAddMethod
  38. {
  39. class_replaceMethod(self,swizzledSelector,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod));
  40. }
  41. else
  42. {
  43. method_exchangeImplementations(originalMethod,swizzledMethod);
  44. }
  45.  
  46. print(method_getImplementation(originalMethod));
  47. print(method_getImplementation(swizzledMethod));
  48. }
  49. }
  50.  
  51. func methodExt()
  52. {
  53. print("B");
  54. }
  55. }

然后我尝试用它来执行它

  1. var s = Swizzle();
  2. s.method();

预期输出为“B”,但仍然打印“A”.从我的代码中可以看出,我在swizzle操作之前和之后都包含了每个IMP的打印件.这些打印显示交换确实发生,但输出保持不变.

输出

  1. 0x000000010251a920
  2. 0x000000010251ad40
  3. 0x000000010251ad40
  4. 0x000000010251a920
  5. A

在让这些更改生效时,我有什么遗漏吗?

PS.目前正在使用XCode 7.0.1

解决方法

问题是你的method()缺少动态指令:
  1. class Swizzle: NSObject
  2. {
  3. dynamic func method()
  4. {
  5. print("A")
  6. }
  7. }

修改声明,它应该工作.

在Swift中使用方法调配时,您的类/方法必须符合以下两个要求:

>您的课程必须扩展NSObject
>您想要调配的函数必须具有动态属性

有关为何需要此操作的完整说明,请查看Using Swift with Cocoa and Objective-C

Requiring Dynamic Dispatch

While the @objc attribute exposes your Swift API to the Objective-C
runtime,it does not guarantee dynamic dispatch of a property,method,
subscript,or initializer. The Swift compiler may still devirtualize
or inline member access to optimize the performance of your code,
bypassing the Objective-C runtime
. When you mark a member declaration
with the dynamic modifier,access to that member is always dynamically
dispatched. Because declarations marked with the dynamic modifier are
dispatched using the Objective-C runtime,they’re implicitly marked
with the @objc attribute.

Requiring dynamic dispatch is rarely necessary. However,you must use
the dynamic modifier when you know that the implementation of an API
is replaced at runtime
. For example,you can use the
method_exchangeImplementations function in the Objective-C runtime to
swap out the implementation of a method while an app is running. If
the Swift compiler inlined the implementation of the method or
devirtualized access to it,the new implementation would not be used.

Swift 3更新:

关于GCD已经有一些变化,并且dispatch_once不再可用.为了执行相同的一次操作,我们可以将代码包含在全局静态类常量的初始化块中.

Swift语言保证此代码在应用程序的生命周期内仅执行一次.

  1. class TestSwizzling : NSObject {
  2. dynamic func methodOne()->Int{
  3. return 1
  4. }
  5. }
  6.  
  7. extension TestSwizzling {
  8.  
  9. //In Objective-C you'd perform the swizzling in load(),//but this method is not permitted in Swift
  10. override class func initialize()
  11. {
  12.  
  13. struct Inner {
  14. static let i: () = {
  15.  
  16. let originalSelector = #selector(TestSwizzling.methodOne)
  17. let swizzledSelector = #selector(TestSwizzling.methodTwo)
  18. let originalMethod = class_getInstanceMethod(TestSwizzling.self,originalSelector);
  19. let swizzledMethod = class_getInstanceMethod(TestSwizzling.self,swizzledSelector)
  20. method_exchangeImplementations(originalMethod,swizzledMethod)
  21. }
  22. }
  23. let _ = Inner.i
  24. }
  25.  
  26. func methodTwo()->Int{
  27. // It will not be a recursive call anymore after the swizzling
  28. return methodTwo()+1
  29. }
  30. }
  31.  
  32. var c = TestSwizzling()
  33. print(c.methodOne())
  34. print(c.methodTwo())

Swift 2.2更新:

我已经为新的#selector属性更新了原始示例:

  1. class TestSwizzling : NSObject {
  2. dynamic func methodOne()->Int{
  3. return 1
  4. }
  5. }
  6.  
  7. extension TestSwizzling {
  8.  
  9. //In Objective-C you'd perform the swizzling in load(),//but this method is not permitted in Swift
  10. override class func initialize()
  11. {
  12. struct Static
  13. {
  14. static var token: dispatch_once_t = 0
  15. }
  16.  
  17. // Perform this one time only
  18. dispatch_once(&Static.token)
  19. {
  20. let originalSelector = #selector(TestSwizzling.methodOne)
  21. let swizzledSelector = #selector(TestSwizzling.methodTwo)
  22. let originalMethod = class_getInstanceMethod(self,originalSelector);
  23. let swizzledMethod = class_getInstanceMethod(self,swizzledMethod)
  24. }
  25. }
  26.  
  27. func methodTwo()->Int{
  28. // It will not be a recursive call anymore after the swizzling
  29. return methodTwo()+1
  30. }
  31. }
  32.  
  33. var c = TestSwizzling()
  34. print(c.methodOne())
  35. print(c.methodTwo())

如果您需要一个示例,请查看此示例项目on github.

猜你在找的iOS相关文章