Runtime 的方法交换(IMP)

前言

Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的。

强大之处

Objc 在三种层面上与 Runtime 系统进行交互:

  • 通过 Objective-C 源代码
  • 通过 Foundation 框架的 NSObject 类定义的方法
  • 通过对 Runtime 库函数的直接调用

runtime 有个东西是"方法欺骗"(IMP 方法的交换). 表面上调用的是方法A,但内部调用的是方法B

    NSString * str = @"http://baidu.com李";
  
    NSURL * url = [NSURL URLWithString:str];
    
    NSLog(@"1----%@",url);
打印:
2017-09-14 17:45:41.777 Runtime[4402:1147114] 1----(null)

复制代码

这里出现nil 是因为 字符串包含中文,导致转成URL时不识别. 可以UTF-8 转码处理

NSString* string2 = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
复制代码

但是每次出现一个URL 你都要这样处理,显然是很麻烦的. 就算你写在类别里面处理,使用的地方你要导入头文件,使用新加的类别方法.新项目还ok.

但是你要改写别人的代码你会疯掉的.


下面要介绍使用runtime来处理这个事情 不过还是要新建一个NSURL类别的

 引用#import <objc/runtime.h>
  Runtime的头文件
复制代码
#import "NSURL+url.h"
#import <objc/runtime.h>
@implementation NSURL (url)
复制代码

新增类别方法来做处理+(instancetype)strURL:(NSString *)str

+(instancetype)strURL:(NSString *)str {
   
   NSURL * url =  [NSURL URLWithString:str]; 
    if (url == nil) {
        NSLog(@"url 为空");
    }
    return url;
}
复制代码

我们希望在外面还调用下面这个系统方法,但是希望内部走+(instancetype)strURL:(NSString *)str处理

[NSURL URLWithString:str];
复制代码

ok,其实一个类被内存装载时,会调用一个方法: +(void)load 在这里做它们两个的方法交换处理

// 这个类被装载的时候调用 进入内存的时候调用
+(void)load {
    // 交换方法的IMP
    /*
     <#Method m1#>   要交换的方法1
     
     <#Method m2#>   要交换的方法2
      method_exchangeImplementations(<#Method m1#>, <#Method m2#>)
     
     得到一个类方法
     class_getClassMethod([self class], @selector(URLWithString:));
    
     得到一个实例方法
     class_getInstanceMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>)
     */
  
    Method url = class_getClassMethod([self class], @selector(URLWithString:));

    Method strUrl = class_getClassMethod([self class], @selector(strURL:) );
   
    method_exchangeImplementations(url, strUrl);

}
复制代码

这时候两个方法已经交换完了,但是你自己新加的类方法: +(instancetype)strURL:(NSString *)str 内部实现还是有 :NSURL * url = [NSURL URLWithString:str]; 这样会造成死循环.你应该这样把这个方法(NSURL * url = [NSURL URLWithString:str];)也替换掉

#import "NSURL+url.h"
#import <objc/runtime.h>
@implementation NSURL (url)

// 这个类被装载的时候调用 进入内存的时候调用
+(void)load {
    // 交换方法的IMP
    /*
     <#Method m1#>   要交换的方法1
     <#Method m2#>   要交换的方法2
      method_exchangeImplementations(<#Method m1#>, <#Method m2#>)
     
     得到一个类方法
     class_getClassMethod([self class], @selector(URLWithString:));
    
     得到一个实例方法
     class_getInstanceMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>)
     */
  
    Method url = class_getClassMethod([self class], @selector(URLWithString:));

    Method strUrl = class_getClassMethod([self class], @selector(strURL:) );
   
    method_exchangeImplementations(url, strUrl);
}

+(instancetype)strURL:(NSString *)str {
   注意:这里一定要写好注释,不然别人看到...
   NSURL * url =  [NSURL strURL:str];  // 这里用到的IMP 方法交换 这个方法已经是 URLWithString
    if (url == nil) {
        NSLog(@"url 为空");
    }
    return url;
}
@end
复制代码

这里已经ok了

外面依然是使用系统的方法:[NSURL URLWithString:str]

但内部是走的自己新加的方法:+(instancetype)strURL:(NSString *)str

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值