设为首页 加入收藏

TOP

OC方法交换swizzle详细介绍——不再有盲点(二)
2019-08-15 00:11:50 】 浏览:300
Tags:方法 交换 swizzle 详细 介绍 再有 盲点

其实,方法交换后,任何时候都不要尝试手动调用,特别是交换的系统方法。实际开发中,也没人会手动调用,这里我们只讨论这种场景的技术及后果,帮助理解

  • 奇数次调用

奇数次之后一切正常。但是,奇数次之前,它会先经历偶数次。

比如,第一次交换,正常,第二次交换,那么相当于没有交换,如果你手动调用了swizzle_viewDidLoad,很明显死循环了,然后你又在其它线程进行第三次交换,又不死循环了。哈哈,好玩,但你要保重,别玩失火了玩到线上了!!!

这种情况还是有可能发生的,比如交换没有放在load方法,又没有dispatch_once,而是自己写了个类似start的开始方法,被自己或者他人误调用。

最后:为了防止多次交换始终加上dispatch_once,除非你清楚你自己在干啥。

再次扩展:常见的多次交换

这里说的多次交换,和上面说的不一样,交换方法不一样,比如我们开发中经常遇到的。

我们自己交换了viewDidLoad,然后第三方库也交换了viewDidLoad,那么交换前(箭头代表映射关系):

sysSel -> sysImp
ourSel -> ourImp
thirdSel -> thirdImp

第一步,我们与系统交换:

sysSel -> ourImp
ourSel -> sysImp
thirdSel -> thirdImp

第二步,第三方与系统交换:

sysSel -> thirdImp
ourSel -> sysImp
thirdSel -> ourImp

假设,push了一个VC,首先是系统的sysSel,那么调用顺序:

thirdImp、ourImp、sysImp

没毛病!

多次交换这种场景是真实存在的,比如我们监控viewDidload/viewWillappear,在程序退到后台时,想停止监控,则再进行一次(偶数)交换也是一种取消监控的方式。当再次进入前台时,则再次(奇数)交换,实现监控。(通过标志位实现用的更多,更简单)

问题二、被交换的类没有实现该方法

我们还是在分类里面添加方法来交换

情况一:父类实现了被交换方法

我们本意交换的是子类方法,但是子类没有实现,父类实现了class_getInstanceMethod(target, swizzledSelector);执行的结果返回父类的Method,那么后续交换就相当于和父类的方法实现了交换。

一般情况下也不会出问题,可是埋下了一系列隐患。如果其它程序员也继承了这个父类。举例代码如下

/// 父类
@interface SuperClassTest : NSObject
- (void)printObj;
@end
@implementation SuperClassTest
- (void)printObj {
    NSLog(@"SuperClassTest");
}
@end

/// 子类1
@interface SubclassTest1 : SuperClassTest
@end
@implementation SubclassTest1
- (void)printObj {
    NSLog(@"printObj");
}
@end

/// 子类2
@interface SubclassTest2 : SuperClassTest
@end
@implementation SubclassTest2
/// 有没有重写此方法,会呈现不同的结果
- (void)printObj {
    // 有没有调用super  也是不同的结果
    [super printObj];
    NSLog(@"printObj");
}
@end

/// 子类1 分类实现交换

+ (void)swizzleInstanceMethod:(Class)target original:(SEL)originalSelector swizzled:(SEL)swizzledSelector {
    Method originMethod = class_getInstanceMethod(target, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(target, swizzledSelector);
    method_exchangeImplementations(originMethod, swizzledMethod);
}
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleInstanceMethod:[SubclassTest1 class] original:@selector(printObj) swizzled:@selector(swiprintObj)];
    });
}

- (void)swiprintObj {
    NSLog(@"swi1:%@",self);
    [self swiprintObj];
}

示例代码,实现了printObjswiprintObj的交换。

  • 问题1:父类的实例对象正常调用printObj,也会造成swiprintObj优先调用,然后再调用printObj,这不是我们想要的,如果你想监控父类,那么完全可以直接交换父类的方法;
  • 问题2:假设sub2(子类2)没有实现printObj,但它的实例对象也调用了printObj,正常应该是能够调用父类的printObj方法,但是由于被交换,会调用sub1的swiprintObj,swiprintObj的实现里面有[self swiprintObj],这里的self是sub2,sub2是没有实现swiprintObj的,直接崩溃。
  • 问题3:sub2子类重写了printObj,一切正常,sub2实例对象调用正常,但是如果在printObj里面调用super方法就。。。

那么如何避免这种情况呢?

使用class_addMethod方法来避免。再次优化后的结果:

+ (void)swizzleInstanceMethod:(Class)target original:(SEL)originalSelector swizzled:(SEL)swizzledSelector {
    Method originMethod = class_getInstanceMethod(target, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(target, swizzledSelector);
    if (class_addMethod(target, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
        class_replaceMethod(target, swizzledSelector, method_getImplementation(originMethod), metho
首页 上一页 1 2 3 4 下一页 尾页 2/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Swift 面试题 下一篇iOS技术栈-Swift版

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目