iOS-虚心若愚,求知若饥iOS Developer - Runtime运行时runtime

iOS黑魔法Method Swizzling

2015-12-27  本文已影响619人  船长_
IMP.png

- 我们可以利用 method_exchangeImplementations 来交换2个方法中的IMP,
- 我们可以利用 class_replaceMethod 来修改类,
- 我们可以利用 method_setImplementation 来直接设置某个方法的IMP,
……
- 归根结底,都是偷换了selector的IMP,如下图所示:

IMP2.png

示例需求:修改系统UIImage的imageNamed方法,当我们传图片为空时候,就打印,或者报错;

1.导入头文件

#import <objc/message.h>

2.配置支持运行时如图


配置.png

3.给UIImage扩充一个分类,添加一个自定义的方法用于替换系统的方法

@interface UIImage (Image)
+ (__kindof UIImage *)dx_imageNamed:(NSString *)imageName;
@end

4.实现这个自定义的方法

+ (UIImage *)dx_imageNamed:(NSString *)imageName
{
    // 1.恢复系统方法加载图片功能
    // 注意:这里不会死循环,因为此时已经交换了方法,调用这个方法,其实是调用系统的方法
    // 注意:这里调用系统的方法不能用super,因为在分类里面不能调用super,分类没有父类
    UIImage *image = [UIImage dx_imageNamed:imageName];
    
    // 2.判断图片是否为空
    if (image == nil) {
        NSLog(@"加载image为空");
    }
    return image;
}

5.在加载这个分类的时候交换方法

// load方法是应用程序把这个类加载到内存的时候调用,而且只会调用一次,所以在这个方法中实现方法的交换最合适
+ (void)load
{
    // 交换方法实现,方法都是定义在类里面
    // class_getMethodImplementation:获取方法实现
    // class_getInstanceMethod:获取对象
    // class_getClassMethod:获取类方法
    // IMP:方法实现
    
    // Class:获取哪个类方法
    // SEL:获取方法编号,根据SEL就能去对应的类找方法
    // 获取系统的方法
    Method imageNameMethod = class_getClassMethod([self class], @selector(imageNamed:));
    
    // 获取自定义方法dx_imageNamed
    Method dx_imageNamedMethod = class_getClassMethod([UIImage class], @selector(dx_imageNamed:));
    
    // 交换方法实现
    method_exchangeImplementations(imageNameMethod, dx_imageNamedMethod);
}

6.在外界调用,测试

- (void)viewDidLoad {
[super viewDidLoad];
// 图片名字是乱编写的
[UIImage imageNamed:@"123"];
}

7.控制台打印输出

加载image为空

这样以后调用imageNamed的时候,就知道图片是否加载成功;
注意:通常你替换一个方法的实现,是希望它在整个程序的生命周期里有效的。也就是说,你会把 method swizzling 修改方法实现的操作放在+(void)load方法里,并在应用程序的一开始就调用执行

上一篇下一篇

猜你喜欢

热点阅读