仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1632|回复: 18
打印 上一主题 下一主题

[学习教程] IOS编程:Objective-C的hook计划(一): Method Swizzling仓酷云

[复制链接]
若天明 该用户已被删除
跳转到指定楼层
楼主
发表于 2015-1-18 11:37:10 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
Model的改变最好通过Notification来传播之前吃过这样的亏最好不要用delegate模式)UIViewController在没有一个类的完成源码的情形下,想改动个中一个办法的完成,除承继它重写、和借助种别重名办法暴力争先以外,另有加倍天真的办法吗?在Objective-C编程中,怎样完成hook呢?题目有点年夜,企图分几篇来总结。
本文次要先容针对selector的hook,配角被题目剧透了————MethodSwizzling。
MethodSwizzling道理

在Objective-C中挪用一个办法,实际上是向一个对象发送动静,查找动静的独一根据是selector的名字。使用Objective-C的静态特征,能够完成在运转时掉包selector对应的办法完成,到达给办法挂钩的目标。
每一个类都有一个办法列表,寄存着selector的名字和办法完成的映照干系。IMP有点相似函数指针,指向详细的Method完成。

<br>
我们能够使用method_exchangeImplementations来互换2个办法中的IMP,
我们能够使用class_replaceMethod来修正类,
我们能够使用method_setImplementation来间接设置某个办法的IMP,
……
归根结柢,都是掉包了selector的IMP,以下图所示:

<br>

MethodSwizzling理论

举个例子好了,我想钩一下NSArray的lastObject办法,只需两个步骤。
第一步:给NSArray加一个我本人的lastObject

  • #import"NSArray+Swizzle.h"


  • @implementationNSArray(Swizzle)


  • -(id)myLastObject
  • {
  • idret=[selfmyLastObject];
  • NSLog(@"**********myLastObject***********");
  • returnret;
  • }
  • @end
乍一看,这不递回了么?别健忘这是我们筹办互换IMP的selector,[selfmyLastObject]将会实行真的[selflastObject]。



第二步:互换IMP

  • #import<objc/runtime.h>
  • #import"NSArray+Swizzle.h"


  • intmain(intargc,char*argv[])
  • {
  • @autoreleasepool{

  • Methodori_Method=class_getInstanceMethod([NSArrayclass],@selector(lastObject));
  • Methodmy_Method=class_getInstanceMethod([NSArrayclass],@selector(myLastObject));
  • method_exchangeImplementations(ori_Method,my_Method);

  • NSArray*array=@[@"0",@"1",@"2",@"3"];
  • NSString*string=[arraylastObject];
  • NSLog(@"TESTRESULT:%@",string);

  • return0;
  • }
  • }
把持台输入Log:

  • 2013-07-1816:26:12.585Hook[1740:c07]**********myLastObject***********
  • 2013-07-1816:26:12.589Hook[1740:c07]TESTRESULT:3
了局很让人欣喜,是否是不由得想给UIWebView的loadRequest:加TODO了呢?
MethodSwizzling的封装

之前在github上找到的RNSwizzle,保举给人人,能够搜一下。

  • //
  • //RNSwizzle.m
  • //MethodSwizzle


  • #import"RNSwizzle.h"
  • #import<objc/runtime.h>
  • @implementationNSObject(RNSwizzle)


  • +(IMP)swizzleSelector:(SEL)origSelector
  • withIMP:(IMP)newIMP{
  • Classclass=[selfclass];
  • MethodorigMethod=class_getInstanceMethod(class,
  • origSelector);
  • IMPorigIMP=method_getImplementation(origMethod);

  • if(!class_addMethod(self,origSelector,newIMP,
  • method_getTypeEncoding(origMethod)))
  • {
  • method_setImplementation(origMethod,newIMP);
  • }

  • returnorigIMP;
  • }
  • @end
MethodSwizzling伤害不伤害

针对这个成绩,我在stackoverflow上看到了中意的谜底,这里翻译一下,总结纪录在本文中,以示分享:


利用MethodSwizzling编程就比如切菜时利用厉害的刀,一些人由于忧虑切到本人以是惧怕厉害的刀具,但是现实上,利用钝刀常常更简单失事,而利刀更加平安。
Methodswizzling能够匡助我们写出更好的,更高效的,易保护的代码。可是假如滥用它,也将会招致难以排查的bug。
背景

比如计划形式,假如我们摸清了一个形式的门道,利用该形式与否我们本人冷暖自知。单例形式就是一个很好的例子,它饱受争议可是很多人仍旧利用它。MethodSwizzling也是一样,一旦你真正了解它的上风和坏处,利用它与否你应当就有你本人的概念。
会商

这里是一些MethodSwizzling的圈套:


  • Methodswizzlingisnotatomic
  • Changesbehaviorofun-ownedcode
  • Possiblenamingconflicts
  • Swizzlingchangesthemethodsarguments
  • Theorderofswizzlesmatters
  • Difficulttounderstand(looksrecursive)
  • Difficulttodebug
我将一一剖析这些点,促进对MethodSwizzling的了解的同时,并弄懂怎样应对。
Methodswizzlingisnotatomic

我所见过的利用methodswizzling完成的办法在并发利用时基础都是平安的。95%的情形里这都不会是个成绩。一般你交换一个办法的完成,是但愿它在全部程序的性命周期里无效的。也就是说,你会把methodswizzling修正办法完成的操纵放在一个加号办法+(void)load里,并在使用程序的一入手下手就挪用实行。你将不会碰着并提问题。假设你在+(void)initialize初始化办法中举行swizzle,那末……rumtime大概逝世于一个诡异的形态。
Changesbehaviorofun-ownedcode

这是swizzling的一个成绩。我们的方针是改动某些代码。swizzling办法是一件灰常灰常主要的事,当你不但是对一个NSButton类的实例举行了修正,而是程序中一切的NSButton实例。因而在swizzling时应当多加当心,但也不必老是往决心制止。

设想一下,假如你重写了一个类的办法,并且没有挪用父类的这个办法,这大概会引发成绩。年夜多半情形下,父类办法希冀会被挪用(最少文档是如许说的)。假如你在swizzling完成中也如许做了,这会制止年夜部分成绩。仍是挪用原始完成吧,如若否则,你会费很鼎力气往思索代码的平安成绩。
Possiblenamingconflicts

定名抵触贯串全部Cocoa的成绩.我们经常在类名和种别办法名前加上前缀。不幸的是,定名抵触还是个熬煎。可是swizzling实在也不用过量思索这个成绩。我们只必要在原始办法定名前做小小的修改来定名就好,好比一般我们如许定名:

  • @interfaceNSView:NSObject
  • -(void)setFrame:(NSRect)frame;
  • @end


  • @implementationNSView(MyViewAdditions)


  • -(void)my_setFrame:(NSRect)frame{
  • //docustomwork
  • [selfmy_setFrame:frame];
  • }


  • +(void)load{
  • [selfswizzle:@selector(setFrame:)with:@selector(my_setFrame:)];
  • }


  • @end
这段代码运转准确,可是假如my_setFrame:在别处被界说了会产生甚么呢?
这个成绩不单单存在于swizzling,这里有一个替换的变通办法:

  • @implementationNSView(MyViewAdditions)


  • staticvoidMySetFrame(idself,SEL_cmd,NSRectframe);
  • staticvoid(*SetFrameIMP)(idself,SEL_cmd,NSRectframe);


  • staticvoidMySetFrame(idself,SEL_cmd,NSRectframe){
  • //docustomwork
  • SetFrameIMP(self,_cmd,frame);
  • }


  • +(void)load{
  • [selfswizzle:@selector(setFrame:)with:(IMP)MySetFramestore:(IMP*)&SetFrameIMP];
  • }


  • @end
看起来不那末Objectice-C了(用了函数指针),如许制止了selector的定名抵触。


最初给出一个较完善的swizzle办法的界说:

  • typedefIMP*IMPPointer;


  • BOOLclass_swizzleMethodAndStore(Classclass,SELoriginal,IMPreplacement,IMPPointerstore){
  • IMPimp=NULL;
  • Methodmethod=class_getInstanceMethod(class,original);
  • if(method){
  • constchar*type=method_getTypeEncoding(method);
  • imp=class_replaceMethod(class,original,replacement,type);
  • if(!imp){
  • imp=method_getImplementation(method);
  • }
  • }
  • if(imp&&store){*store=imp;}
  • return(imp!=NULL);
  • }


  • @implementationNSObject(FRRuntimeAdditions)
  • +(BOOL)swizzle:(SEL)originalwith:(IMP)replacementstore:(IMPPointer)store{
  • returnclass_swizzleMethodAndStore(self,original,replacement,store);
  • }
  • @end
Swizzlingchangesthemethodsarguments

我以为这是最年夜的成绩。想一般挪用methodswizzling将会是个成绩。

  • [selfmy_setFrame:frame];
间接挪用my_setFrame:,runtime做的是

  • objc_msgSend(self,@selector(my_setFrame:),frame);
runtime往寻觅my_setFrame:的办法完成,_cmd参数为my_setFrame:,可是现实上runtime找到的办法完成是原始的setFrame:的。

一个复杂的办理举措:利用下面先容的swizzling界说。
Theorderofswizzlesmatters

多个swizzle办法的实行按次也必要注重。假定setFrame:只界说在NSView中,想像一下依照上面的按次实行:

  • [NSButtonswizzle:@selector(setFrame:)with:@selector(my_buttonSetFrame:)];
  • [NSControlswizzle:@selector(setFrame:)with:@selector(my_controlSetFrame:)];
  • [NSViewswizzle:@selector(setFrame:)with:@selector(my_viewSetFrame:)];
WhathappenswhenthemethodonNSButtonisswizzled?WellmostswizzlingwillensurethatitsnotreplacingtheimplementationofsetFrame:forallviews,soitwillpulluptheinstancemethod.Thiswillusetheexistingimplementationtore-definesetFrame:intheNSButtonclasssothatexchangingimplementationsdoesntaffectallviews.TheexistingimplementationistheonedefinedonNSView.ThesamethingwillhappenwhenswizzlingonNSControl(againusingtheNSViewimplementation).

WhenyoucallsetFrame:onabutton,itwillthereforecallyourswizzledmethod,andthenjumpstraighttothesetFrame:methodoriginallydefinedonNSView.TheNSControlandNSViewswizzledimplementationswillnotbecalled.

Butwhatiftheorderwere:

  • [NSViewswizzle:@selector(setFrame:)with:@selector(my_viewSetFrame:)];
  • [NSControlswizzle:@selector(setFrame:)with:@selector(my_controlSetFrame:)];
  • [NSButtonswizzle:@selector(setFrame:)with:@selector(my_buttonSetFrame:)];
Sincetheviewswizzlingtakesplacefirst,thecontrolswizzlingwillbeabletopulluptherightmethod.Likewise,sincethecontrolswizzlingwasbeforethebuttonswizzling,thebuttonwillpullupthecontrolsswizzledimplementationofsetFrame:.Thisisabitconfusing,butthisisthecorrectorder.Howcanweensurethisorderofthings?

Again,justuseloadtoswizzlethings.Ifyouswizzleinloadandyouonlymakechangestotheclassbeingloaded,youllbesafe.Theloadmethodguaranteesthatthesuperclassloadmethodwillbecalledbeforeanysubclasses.Wellgettheexactrightorder!


这段贴了原文,硬翻译太拗口……总结一下就是:多个有承继干系的类的对象swizzle时,从子类对象入手下手。假如先swizzle父类对象,那末前面子类对象swizzle时就没法拿到真实的原始办法完成了。

(感激批评中qq373127202的提示,在此改正一下,非常感激)
多个有承继干系的类的对象swizzle时,先从父对象入手下手。如许才干包管子类办法拿到父类中的被swizzle的完成。在+(void)load中swizzle不会堕落,就是由于load类办法会默许从父类入手下手挪用。
Difficulttounderstand(looksrecursive)

(新办法的完成)看起来像递回,可是看看下面已给出的swizzling封装办法,利用起来就很易读懂.
这个成绩是已完整办理的了!

很多框架是用C写的学习iOS开发基础可以按照下面两个方面学基础(原料钢铁塑料)
透明 该用户已被删除
沙发
发表于 2015-1-21 11:13:25 | 只看该作者
以上可以同时进行,学习过程中尽量不要纠结细节和底层,要知道ios是封闭的、OC是高级语言,我们不可能过多地去了解它的原理,至少在新手阶段没有必要。用迭代的方式更新你的知识,而不是死抠一个知识点。
变相怪杰 该用户已被删除
板凳
发表于 2015-1-30 16:45:07 | 只看该作者
在此,某不才愿将安装成功的Mac OS X系统的vmware虚拟机向有志学习iOS开发的各位学友们免费开放出来,经测试,可以在WindowsXP/Win7系统上完美运行,即便你的机器只有2GB内存。
若天明 该用户已被删除
地板
 楼主| 发表于 2015-2-1 19:14:55 | 只看该作者
看《iPhone 4与iPad开发基础教程》,跟着一步步来
乐观 该用户已被删除
5#
发表于 2015-2-3 09:00:16 | 只看该作者
以上可以同时进行,学习过程中尽量不要纠结细节和底层,要知道ios是封闭的、OC是高级语言,我们不可能过多地去了解它的原理,至少在新手阶段没有必要。用迭代的方式更新你的知识,而不是死抠一个知识点。
愤怒的大鸟 该用户已被删除
6#
发表于 2015-2-5 03:51:39 | 只看该作者
首先是基础,在汉昌的课程非常全面。从object—c到最后的毕业项目,基本上方方面面都涉及到了,我是一名非计算机专业的学生,起初学习还有点吃力,因为基础知识薄弱。经常像听天书
只想知道 该用户已被删除
7#
发表于 2015-2-5 08:14:53 | 只看该作者
自从苹果公司开放iOS SDK以来,大量的国内外的软件开发者将关注的目光聚集在苹果的iOS平台上。由于iPhone和iPad自一出现就给人带来了颠覆性的感觉
小妖女 该用户已被删除
8#
发表于 2015-2-10 17:37:53 | 只看该作者
在此,某不才愿将安装成功的Mac OS X系统的vmware虚拟机向有志学习iOS开发的各位学友们免费开放出来,经测试,可以在WindowsXP/Win7系统上完美运行,即便你的机器只有2GB内存。
冷月葬花魂 该用户已被删除
9#
发表于 2015-3-1 13:31:17 | 只看该作者
中国如今已然发展成为一个软件大国,软件人才的数量跃居全球之首。当然,在苹果平台的开发领域,也保持了相当强劲的发展势头。然而,很多初入iOS开发门槛的开发者,
若相依 该用户已被删除
10#
发表于 2015-3-10 17:42:57 | 只看该作者
自从苹果公司开放iOS SDK以来,大量的国内外的软件开发者将关注的目光聚集在苹果的iOS平台上。由于iPhone和iPad自一出现就给人带来了颠覆性的感觉
因胸联盟 该用户已被删除
11#
发表于 2015-3-10 19:06:23 | 只看该作者
首先是基础,在汉昌的课程非常全面。从object—c到最后的毕业项目,基本上方方面面都涉及到了,我是一名非计算机专业的学生,起初学习还有点吃力,因为基础知识薄弱。经常像听天书
简单生活 该用户已被删除
12#
发表于 2015-3-17 09:08:58 | 只看该作者
其实在培训的过程中,学习到最多的就是查资料的方式,当时感觉老师好坑,什么都不告诉我们,让我们自己去查,但是现在觉得还是要自己解决问题,这样才能理解的更加深入。
13#
发表于 2015-3-23 11:00:34 | 只看该作者
以上可以同时进行,学习过程中尽量不要纠结细节和底层,要知道ios是封闭的、OC是高级语言,我们不可能过多地去了解它的原理,至少在新手阶段没有必要。用迭代的方式更新你的知识,而不是死抠一个知识点。
再现理想 该用户已被删除
14#
发表于 2015-3-27 05:08:10 | 只看该作者
其实在培训的过程中,学习到最多的就是查资料的方式,当时感觉老师好坑,什么都不告诉我们,让我们自己去查,但是现在觉得还是要自己解决问题,这样才能理解的更加深入。
灵魂腐蚀 该用户已被删除
15#
发表于 2015-4-1 23:10:50 | 只看该作者
还有开发工具是用Xcode,是在Mac系统的,你多摸索一下就可以开发简单的应用了,建议你买一本iphone开发秘籍第二版看看,希望可以帮到你,谢谢。
金色的骷髅 该用户已被删除
16#
发表于 2015-4-7 15:01:51 | 只看该作者
在百度搜索你想要了解的类名(苹果的cocoa和cocoatouch框架的类名很有特点很容易搜到,前缀都是NS or UI),看别人写的博客详解
柔情似水 该用户已被删除
17#
发表于 2015-4-29 07:53:36 | 只看该作者
其实在培训的过程中,学习到最多的就是查资料的方式,当时感觉老师好坑,什么都不告诉我们,让我们自己去查,但是现在觉得还是要自己解决问题,这样才能理解的更加深入。
小魔女 该用户已被删除
18#
发表于 2015-5-1 02:12:14 | 只看该作者
自从苹果公司开放iOS SDK以来,大量的国内外的软件开发者将关注的目光聚集在苹果的iOS平台上。由于iPhone和iPad自一出现就给人带来了颠覆性的感觉
爱飞 该用户已被删除
19#
发表于 2015-5-9 17:57:45 | 只看该作者
因为我们老师也是自学的,给我们讲课说的最多的就是百度,谷歌,查文档。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-5-2 18:54

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表