设为首页 加入收藏

TOP

OC方法交换swizzle详细介绍——不再有盲点(三)
2019-08-15 00:11:50 】 浏览:277
Tags:方法 交换 swizzle 详细 介绍 再有 盲点
d_getTypeEncoding(originMethod)); } else { method_exchangeImplementations(originMethod, swizzledMethod); } }

分步骤详细解析如下:

  • class_addMethod 执行前

superSel -> superImp
sub1SwiSel -> sub1SwiImp

  • class_addMethod 执行后,给子类增加了sel,但是对应的imp实现还是swizzledMethod的imp即交换方法的imp

superSel -> superImp
sub1Sel -> sub1SwiImp
sub1SwiSel -> sub1SwiImp

被交换的方法sub1Sel已经指向了交换方法的imp实现,下一步将交换方法的sel 指向被交换方法的imp即可。被交换方法不是没有实现吗??? 有的,OC继承关系,父类的实现就是它的实现superImp

  • class_replaceMethod,将sub1SwiSel的实现替换为superImp

superSel -> superImp
sub1Sel -> sub1SwiImp
sub1SwiSel -> superImp

系统在给对象发送sel消息时,执行sub1SwiImp,sub1SwiImp里面发送sub1SwiSel,执行superImp,完成hook。

我们说的给子类新增method,其实并不是一个全新的,而是会共享imp,函数实现没有新增。这样的好处是superSel对应的imp没有改变,它自己的以及它的其它子类不受影响,完美解决此问题;但是继续往下看其它问题

情况2:父类也没有实现

尴尬了,都没有实现方法,那还交换个锤子???

先说结果吧,交换函数执行后,方法不会被交换,但是手动调用下面这些,同样会死循环。

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

所以我们要加判断,然后返回给方法调用者一个bool值,或者更直接一点,抛出异常。

/// 交换类方法的注意获取meta class, object_getClass。class_getClassMethod
+ (void)swizzleInstanceMethod:(Class)target original:(SEL)originalSelector swizzled:(SEL)swizzledSelector {
    Method originMethod = class_getInstanceMethod(target, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(target, swizzledSelector);
    if (originMethod && swizzledMethod) {
        if (class_addMethod(target, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
            class_replaceMethod(target, swizzledSelector, method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
        }
        else {
            method_exchangeImplementations(originMethod, swizzledMethod);
        }
    }
    else {
        @throw @"originalSelector does not exit";
    }
}

再加上 dispatch_once 上面已经算是比较完美了,但是并没有完美,主要是场景不同,情况就不同。我们只有理解原理,不同场景不同对待。

新建类来交换系统方法

上面说的都是在分类里面实现交换方法,这里新建"私有类"来交换系统方法。

在写SDK时,分类有重名覆盖问题,编译选项还要加-ObjC。出问题编译阶段还查不出来。那么我们可以用新建一个私有类实现交换,类重名则直接编译报错。交换方法和上面的分类交换稍不一样

比如hook viewDidload,代码如下:

@interface SwizzleClassTest : NSObject
@end

@implementation SwizzleClassTest
+ (void)load {
    /// 私有类,可以不用dispatch_once
    Class target = [UIViewController class];
    Method swiMethod = class_getInstanceMethod(self, @selector(swi_viewDidLoad));
    Method oriMethod = class_getInstanceMethod(target, @selector(viewDidLoad));
    if (swiMethod && oriMethod) {
        if (class_addMethod(target, @selector(swi_viewDidLoad), method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod))) {
            // 这里获取给UIViewController新增的method
            swiMethod = class_getInstanceMethod(target, @selector(swi_viewDidLoad));
            method_exchangeImplementations(oriMethod, swiMethod);
        }
    }
}

- (void)swi_viewDidLoad {
    // 不能调用,这里的self是UIViewController类或者子类的实例,调用test的话直接崩溃。或者做类型判断 [self isKindOfClass:[SwizzleClassTest class]],然后再调用
    // [self test];
    [self swi_viewDidLoad];
}

- (void)test {
    NSLog(@"Do not do this");
}

@end

这里也用到class_addMethod,给UIViewController新增了一个swi_viewDidLoad sel及其imp实现,共享了SwizzleClassTest 的imp实现。

另外系统发送viewdidload消息进而调用swi_viewDidLoad方法,里面的self是UIViewController,所以不能再[self test],否则崩溃。也不能在其它地方手动[self swi_viewDidLoad];会死循环,因为这时候self是SwizzleClassTest,而它的method是没有被交换的,好处是我们可以通过sel

首页 上一页 1 2 3 4 下一页 尾页 3/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Swift 面试题 下一篇iOS技术栈-Swift版

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目