运行时(runtime)-方法交换

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 方法的实现的确被交换了。

4. 交换系统自带的方法
在实际开发中,交换方法最大的应用场景是`替换系统自带的方法。
4.1 举例分析
举个例子:从iOS6到iOS7,苹果的界面风格由 ‘拟物化’ 转变为 ‘扁平化’。
‘怎么快速适配?’
1> 设置图片是通过系统的 imageNamed 方法来实现的。
2> 如果要进行iOS7之后版本的适配,首先需要平面设计师做出一套扁平化的图片,然后对系统版本(systemVersion)进行判断: 大于7.0就是用扁平化图片。
3> 如果项目中很多地方都有设置图片的代码,就要在每一处都添加判断系统版本的代码,工作量十分庞大,很不现实。这个时候,就可以考虑使用运行时交换方法来解决问题。
‘解决思路’
1> 假设旧图片的图片名为 ‘xxx.png’,可以让平面设计师将新图片的图片名命名为’xxx_ios7.png’
2> 自定义一个 ‘xfq_imageNamed’ 方法,在该方法中进行系统版本判断,如果大于7.0就使用’xxx_ios7.png’的图片。
3> 交换自定义的 ‘xfq_imageNamed’ 方法 和 系统的 ‘imageNamed’ 方法的实现

4.2 代码实现

1> 自定义 UIImage+Extension 分类

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(xfq_imageNamed:));
    method_exchangeImplementations(m1, m2);
}

+ (UIImage *)xfq_imageNamed:(NSString *)name {
    // 获取当前设备的系统版本号 
    float version = [[UIDevice currentDevice].systemVersion floatValue];
    // 判断版本号,如果 >= 7.0,在图片名后面拼接 "_ios7"
    if (version >= 7.0) {
        name = [name stringByAppendingString:@"_ios7"];
    }
    // 这里需要特别注意: return的时候,使用的是自定义的方法,这个时候由于已经交换了方法,实际上调用的是系统的imageNamed方法'
    return [UIImage xfq_imageNamed:name];
}

// 注意: 在分类中重写系统方法,不能满足要求
// 这种方式会导致系统原来的方法无法使用
//+ (UIImage *)imageNamed:(NSString *)name {
//    
//    float version = [[UIDevice currentDevice].systemVersion floatValue];
//    if (version >= 7.0) {
//        name = [name stringByAppendingString:@"_ios7"];
//    }
//    // 会死循环
//    return [self imageNamed:name];
//}

@end

2> 控制器中调用

#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:@"open"];
}
@end
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值