交换方法
1. 简单实现
1> 创建一个 Person 类,并定义两个方法 study 和 run,分别实现:
#import "Person.h"
@implementation Person
- (void)study {
NSLog(@"study");
}
- (void)run {
NSLog(@"run");
}
@end
2> 正常调用方法
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
[p study];
[p run];
}
执行程序,结果如下:
3> 交换方法实现
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
[p study];
[p run];
// 交换方法实现
Method m1 = class_getInstanceMethod(objc_getClass("Person"), @selector(study));
Method m2 = class_getInstanceMethod(objc_getClass("Person"), @selector(run));
method_exchangeImplementations(m1, m2);
[p study];
[p run];
}
return 0;
}
执行程序,结果如下:
由运行结果可知:study 方法和 run 方法的实现的确被交换了。
2. 交换系统自带的方法
在实际开发中,交换方法最大的应用场景是`替换系统自带的方法。
2.1 举例分析
举个例子:从iOS6到iOS7,苹果的界面风格由 '拟物化' 转变为 '扁平化'。
'怎么快速适配?'
1> 设置图片是通过系统的 imageNamed 方法来实现的。
2> 如果要进行iOS7之后版本的适配,首先需要平面设计师做出一套扁平化的图片,然后对系统版本(systemVersion)进行判断: 大于7.0就是用扁平化图片。
3> 如果项目中很多地方都有设置图片的代码,就要在每一处都添加判断系统版本的代码,工作量十分庞大,很不现实。这个时候,就可以考虑使用运行时交换方法来解决问题。
'解决思路'
1> 假设旧图片的图片名为 'xxx.png',可以让平面设计师将新图片的图片名命名为'xxx_os7.png'
2> 自定义一个 'xx_imageNamed' 方法,在该方法中进行系统版本判断,如果大于7.0就使用'xxx_os7.png'的图片。
3> 交换自定义的 'xx_imageNamed' 方法 和 系统的 'imageNamed' 方法的实现
2.2 代码实现
1> 自定义 UIImage+Extension 分类
UIImage+Extension.h
#import <UIKit/UIKit.h>
@interface UIImage (Extension)
@end
UIImage+Extension.m
#import "UIImage+Extension.h"
#import <objc/runtime.h>
@implementation UIImage (Extension)
/**
当某个类或者分类加载到内存的时候,会调用1次
*/
+ (void)load {
Method m1 = class_getClassMethod(objc_getClass("UIImage"), @selector(imageNamed:));
Method m2 = class_getClassMethod(objc_getClass("UIImage"), @selector(xd_imageNamed:));
method_exchangeImplementations(m1, m2);
}
/**
自定义方法
*/
+ (UIImage *)xd_imageNamed:(NSString *)name {
// 获取当前设备的系统版本号
float version = [[UIDevice currentDevice].systemVersion floatValue];
// 判断版本号,如果 >= 7.0,在图片名后面拼接 "_os7"
if (version >= 7.0) {
name = [name stringByAppendingString:@"_os7"];
}
// 这里需要特别注意: return的时候,使用的是自定义的方法,这个时候由于已经交换了方法,实际上调用的是系统的imageNamed方法'
return [UIImage xd_imageNamed:name];
}
// 注意: 在分类中重写系统方法,不能满足要求
// 这种方式会导致系统原来的方法无法使用
//+ (UIImage *)imageNamed:(NSString *)name {
//
// float version = [[UIDevice currentDevice].systemVersion floatValue];
// if (version >= 7.0) {
// name = [name stringByAppendingString:@"_os7"];
// }
// // 会死循环
// return [self imageNamed:name];
//}
@end
2> vc 中调用
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
/**
iOS6 -> iOS7
拟物化 -> 扁平化
*/
- (void)viewDidLoad {
[super viewDidLoad];
// 正是因为交换了方法的实现,所以这里的代码不用做任何修改,就可以实现想要的效果
self.imageView.image = [UIImage imageNamed:@"close"];
}
@end