- 利用
Runtime
实现的方法交换,主要是为了修改系统的方法实现。 - Objective-C 提供了一下API用于动态替换类方法或者实例方法的实现:
class_replaceMethod
替换类方法的定义
method_exchangeImplementations
交换两个方法的实现
method_setImplementation
设置一个方法的实现
注:class_replaceMethod
试图替换一个不存在的方法时候,会调用class_addMethod
为该类增加一个新方法
以UIImage类目为例,实现类方法,以及对象方法的实现交换。
代码示例如下:
// UIImage+Image.h
@interface UIImage (Image)
// 对象方法
- (void)sayOne;
- (void)sayTwo;
@end
// UIImage+Image.m
#import <objc/message.h>
@implementation UIImage (Image)
+ (void)load{
// 修改类方法实现示例:
Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:));
Method xmg_imageNamedMethod = class_getClassMethod(self, @selector(xmg_imageNamed:));
// 利用runtime实现方法的交换
method_exchangeImplementations(imageNamedMethod, xmg_imageNamedMethod);
// 修改对象方法实现示例:
Method method1 = class_getInstanceMethod(self, @selector(sayOne));
Method method2 = class_getInstanceMethod(self, @selector(sayTwo));
method_exchangeImplementations(method1, method2);
}
+ (UIImage *)xmg_imageNamed:(NSString *)name{
// 由于已经实现了方法交换,故[UIImage xmg_imageNamed:name]的内部调用实际上走的是[UIImage imageNamed:name]原本的内部实现。
UIImage *image = [UIImage xmg_imageNamed:name];
if (image) {
NSLog(@"加载成功");
} else {
NSLog(@"加载失败");
}
return image;
}
- (void)sayOne{
NSLog(@"One");
}
- (void)sayTwo{
NSLog(@"Two");
}
@end
// 调用示例:
#import "UIImage+Image.h" // 导入此类目
UIImage *image = [[UIImage alloc]init];
[image sayOne]; //输出:Two
[UIImage imageNamed:@"one.png"]; //输出:加载成功
总结:
Method class_getInstanceMethod(Class cls, SEL name)
获取类方法。参数一Class cls
:获取哪个类的方法;参数二SEL name
:获取类的哪个方法;返回值类型为Method
。Method class_getInstanceMethod(Class cls, SEL name)
获取对象方法。参数一Class cls
:获取哪个类的方法;参数二SEL name
:获取类的哪个对象方法;返回值类型为Method
。void method_exchangeImplementations(Method m1, Method m2)
替换方法m1与方法m2的实现。- 在类目中,最好不要重写系统方法。一旦重写,将会把系统方法实现给干掉,因为分类不是继承父类,而是继承NSObject,因此super不是改类的方法,而是直接覆盖掉了父类的行为。
+ (void)load
与+ (void)initialize
的区别:
+ (void)load
:当类加载进内存的时候调用,而且不管有没有子类,都只会调用一次,在main函数之前调用。- 用途:(1)可以新建类在该类中实现一些配置信息。(2)runtime交换方法的时候,因为只需要交换一次方法,所有可以在该方法中实现交换方法的代码,用于只实现一次的代码 。
+ (void)initialize
:当类被初始化的时候调用,可能会被调用多次,若是没有子类,则只会调用一次,若是有子类的话,该方法会被调用多次,若是子类的继承关系,先会调用父类的+ (void)initialize
方法,然后再去调用子类的+ (void)initialize方法
(若是继承关系,调用某个方法的时候,先会去父类中查找,若是父类中没有方法的实现就去子类中查找)- 用途:(1)在设置导航栏的全局背景的时候,只需要设置一次,可以重写该方法设置,最好是在该方法判断子类,若是自己,则实现设置全局导航栏的方法,若不是自己则跳过实现。(2)在创建数据库代码的时候,可以在该方法中去创建,保证只初始化一次数据库实例,也可以用
dispatch
或是懒加载的方法中初始化数据库实例,也能保证只初始化一次数据库实例。其中也可以在+ (void)initialize
方法中用dispatch
也能保证即使有子类也只会初始化一次。