Runtime的总结(第二篇)

利用Runtime实现方法交换

分析场景     在我们的项目开发中,会遇到这样的问题:如果字符串链接里面带有文字,那它返回的url是空的, “null” ,后面我们再进行网络请求一个空的url结果..... 。这也就说明NSURL 在创建出来后,系统不会检测是否为nil。怎么解决?

开始的想法:我可以为NSURL扩展一个方法,在方法实现里判断 返回的url是否为空,再做一些其他处理,最后再把方法名替换下就行啦。   扩展的方法如下;

+(instancetype)HZ_urlWtihString:(NSString *)str{

NSURL *url = [NSURL URLWithString:str];

if (url == nil) {

NSLog(@"url 空了");

}

return url;

}

要是方法调用次数少,简单替换下方法名 。但是如果类似于这种情况 并且替换的地方很多呢?就要一个一个找替换 很麻烦。  

这时有一个一劳永逸的方法:HOOK(面向切面编程)钩子思想!! 勾住某个方法的调用,改变方法调用的顺序。

继续说,我们可以不改变系统方法的调用,还是调用URLWithString,但是调用后让它走到我们自定义方法的实现里。 方法的调用就是发送消息 , 那么调用URLWithString方法 被编译后C++代码 就是这样:   objc_msgSend(<#id self#>, @selector(URLWithString)) ,只要他发送这消息,我就拦截到  ,接下来就需要我们做些改变 !! Runtime 提供了改变方法调用顺序的功能  ,那改变的是什么?  @selector(URLWithString)这是被编译后的我们无法改变(改变相当于改变源码),在@selector之后还有一个机会改变方法的调用 ,改变的是方法的IMP

提示方法的组成:SEL方法编号+IMP方法实现(实质:函数指针,它指向一块内存区域,内存里面的二进制指令)),SEL和IMP是一一对应的。 也可以这样理解:SEL(书的目录)--IMP(书的页码)--  代码(书里的内容);我们发送消息SEL 它就会找到IMP(IMP不是代码 它是函数指针)


发送消息就是通过目录找页码,通过页码找内容

Runtime可以这么做,它可以改变目录上面的对应关系,它可以做交换


那什么时候交换?要在一切调用该方法之前做好,main函数里一般不写,我们可以在分类的.m源文件里,源文件再加载的时候有个+(void)load方法,

//App装载进内存的时候 就会执行这个指令;(app安装在手机里都是二进制,点开App第一件事就是将硬盘里的二进制装进内存里,就会读load里面的内容,也就是先执行load里面的指令,再启动APP 执行UIApplicationmain, load比main函数靠前)

+(void)load{

//下钩子

}

我们在load里面“下钩子”。我们会用到这个方法来交换:

method_exchangeImplementations(<#Method m1#>, <#Method m2#>)//改变方法实现

我们通过函数获取m1 m2

+(void)load{

//下钩子

//    class_getInstanceMethod;//获取实例方法

//获取类方法,返回Method结构体

Method urlWithString = class_getClassMethod(self, @selector(URLWithString:));

Method HZ_UrlWithString = class_getClassMethod(self, @selector(HZ_urlWtihString:));

//交换方法的实现(相当于交换:“书的页码”)

method_exchangeImplementations(urlWithString, HZ_UrlWithString);

}

到现在,方法交换就实现啦,原来的代码不用动。但是还有一地方没改,否则会造成循环调用 ,栈溢出:URLWithString 改为HZ_urlWtihString

//HZ_urlWtihString 与 urlWithString 的imp已经交换

+(instancetype)HZ_urlWtihString:(NSString *)str{

NSURL *url = [NSURL HZ_urlWtihString:str];

if (url == nil) {

NSLog(@"url 空了");

}

return url;

}

所谓HOOK,是一种思想,是专门的一种钩子,他会在内存中找到某一个方法的调用(一块儿内存区域),直到调用这个方法时,就勾住他,对它的方法行为进行改变,所以Runtime 的方法交换 某种意义上说也是一种钩子。

动态添加方法

当一个类被调用了没有被实现的方法,那么他会走这两种方法的一种

//当类被调用没有实现的类方法时,调用该方法

+(BOOL)resolveClassMethod:(SEL)sel{

}

//当这个类被调用没有实现的对象方法时,调用该方法

+(BOOL)resolveInstanceMethod:(SEL)sel{

}

比如在控制器里,一个类Person被调用了一个没有实现的无参数方法[p performSelector:@selector(eat)];,会走到resolveInstanceMethod:里来,那么我们就可以在该方法中给Person类动态添加方法。使用Runtime添加方法,就会用到这个函数class_addMethod,代码实例

//当这个类被调用没有实现的对象方法时,调用该方法

+(BOOL)resolveInstanceMethod:(SEL)sel{

NSLog(@"%@",NSStringFromSelector(sel));

//动态添加方法(方法组成:SEL (方法编号) + IMP(函数指针) + 函数体)

/*

1.cls 目标类

2.sel 方法编号

3.imp 方法实现(函数指针)

4.返回值类型 (无返回值无参的函数 为 "v@:")//v代表viod,@代表一个id对象,:代表方法编号

*/

class_addMethod(self, sel, (IMP)haha, "v@:");

//不知道返回什么,父类返回什么 我这就返回什么

return [super resolveInstanceMethod:sel];

}

//写一个函数,haha 就是函数指针,这里需要加(IMP)来强制转化,不然会报警告

void haha(){

NSLog(@"来了!!");

}

以上时添加的无返回值无参数的实例方法,如果调用了一个没有实现的带参数的方法,

[p performSelector:@selector(eat) withObject:@"哈哈"];

这就要改变class_addMethod的第四个参数-返回值类型,和haha函数体里的参数。返回值类型改为“v@:”,更改函数体里的参数,有两个 方法调用的隐式参数 id self,SEL _cmd,

/*

1.方法的调用者

2.方法的编号

(OC 里 每个方法调用,它的函数体里有俩个隐式参数,是默认的)

*/

void eat(id self,SEL _cmd,NSString *str){

NSLog(@"%@",str);

}

为什么会有隐式参数,可能隐约感觉的到,方法调用就是消息发送:

// [p performSelector:@selector(eat) withObject:@"哈哈"];

//调用OC方法的时候,会给IMP传入两个参数:方法的调用者  id self 、方法编号SEL _cmd (行参名字可以改)

    objc_msgSend(p, @selector(eat:),@"哈哈");

这里面的p 和 @selector(eat:) 就传到 void eat(id self,SEL _cmd,NSString *str)这里来啦。

*另外swift调用方法 和oc  调用方法的底层是不一样的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • 我们常常会听说 Objective-C 是一门动态语言,那么这个「动态」表现在哪呢?我想最主要的表现就是 Obje...
    Ethan_Struggle阅读 2,170评论 0 7
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,541评论 33 466
  • Zeppelin介绍 Apache Zeppelin提供了web版的类似ipython的notebook,用于做数...
    JasonDing阅读 3,663评论 1 14
  • 遇到一个相互喜欢的人是很难的 去喜欢一个人也是很难的 不适合谈恋爱的我注定是孤独的 因为不懂得喜欢也不值得被喜欢 ...
    有一颗文艺心的理科女阅读 289评论 3 2