Runtime 交换方法

作者: 留个念想给昨天 | 来源:发表于2018-04-03 17:01 被阅读21次

    他喜欢创作,时常有充沛的表达诉求,
    他喜欢听王菲的歌,也向往远方和自由。
    他希望有人懂他偶尔的烦躁和头脑短路,
    懂他有时写文案也是带着镣铐在跳舞。
    然而,他洞察过那么多消费者的心,
    却没有人还给他一个“懂得”。

    于是他,转行了,做了iOS开发人!

    写在前头

    工作中,是不是经常这样用到NSURL
    NSURL * url = [NSURL URLWithString:@"www.baidu.com"];
    很简单,但是!!! 有个致命的隐藏问题 !!!!
    当字符串中有中文,这个url就创建不成功,那么我们发送的请求就会出错。
    是不是有这样的经历:发现一个问题,找bug找了半天,最后发现是url中有中文。。。(此处是不是想摔电脑)
    那么有什么办法在不改变原有代码的情况下给URLWithString 添加一个检测是否为空的功能?

    这里就要用到runtime的方法交互了。

    image.png

    1.创建NSURL的分类

    #import <Foundation/Foundation.h>
    
    @interface NSURL (url)
    
    +(instancetype)My_URLWithString:(NSString *)URLString;
    
    @end
    
    #import "NSURL+url.h"
    @implementation NSURL (url)
    
    +(instancetype)My_URLWithString:(NSString *)URLString
    {
        NSURL * url = [NSURL URLWithString:URLString];
        if (url == nil) {
            NSLog(@"哥么是一个空的URL");
        }
        return url;
        
    }
    
    @end
    

    这个My_URLWithString 就是检测url为空的方法
    那么应该怎么交换方法呢,什么时候交换呢

    2.寻找方法交换的时机

    每一个类,只要参与了编译,就会加载这个类,只要加载这个类就会调用这个类的load方法。
    试一下

    #import "NSURL+url.h"
    
    @implementation NSURL (url)
    
    +(void)load
    {
        NSLog(@"%s",__func__);
        
    }
    
    
    +(instancetype) My_URLWithString:(NSString *)URLString
    {
        NSURL * url = [NSURL URLWithString:URLString];
        if (url == nil) {
            NSLog(@"哥么这个URL为空");
        }
        return url;
        
    }
    @end
    

    然后在ViewController中调用URLWithString:这个方法

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
     
        NSURL * url = [NSURL URLWithString:@"www.baidu.com/中文"];
    }
    @end
    

    运行,会发现,没有引用NSURL+url 这个类,也没有调用My_URLWithString这个方法,这个load也走了。

    image.png

    那么,我们可以在load中添加方法交互

    3.方法交换

    #import "NSURL+url.h"
    #import <objc/message.h>
    
    @implementation NSURL (url)
    
    +(void)load
    {
        //获取类方法
        //1.类类型
        //2.方法编号
        Method URLWithStr = class_getClassMethod([NSURL class], @selector(URLWithString:));
        //My_URLWithString
        Method MyURLWithStr = class_getClassMethod([NSURL class], @selector(My_URLWithString:));
        //交换方法
        method_exchangeImplementations(URLWithStr, MyURLWithStr); 
        
    }
    +(instancetype)My_URLWithString:(NSString *)URLString
    {
        NSURL * url = [NSURL URLWithString:URLString];
        if (url == nil) {
            NSLog(@"哥么这个URL为空");
        }
        return url;
    }
    @end
    

    辛辛苦苦,来一发测试
    打个断点,看有没有走My_URLWithString:方法

    image.png

    果断进来了,放开断点

    image.png

    百密一疏

    +(instancetype)My_URLWithString:(NSString *)URLString
    {
        //注意这里----一定一定要标注好
        NSURL * url = [NSURL My_URLWithString:URLString];
        if (url == nil) {
            NSLog(@"哥么这个URL为空");
        }
        return url;
        
    }
    

    这样就OK了。完美解决,没有改变原有的任何代码。
    提供一份Demo,希望对你有帮助
    想了解更多RunTime,可以参考:
    Runtime 消息传递机制
    Runtime 消息转发机制
    写在最后:

    希望这篇文章对您有帮助,最好就是实操一边,这样才能理解更深入。
    当然如果您发现有可以优化的地方,希望您能慷慨的提出来。
    最后祝您工作愉快!
    

    相关文章

      网友评论

      本文标题:Runtime 交换方法

      本文链接:https://www.haomeiwen.com/subject/stfchftx.html