当前位置: 首页 > 科技观察

关于runtime的那些事儿(消息机制)

时间:2023-03-19 21:35:53 科技观察

1.关于runtime之前我用runtime解决了项目中更改全局字体的问题,于是我又一次感受到了runtime黑魔法的威力,现在有机会了分享它。对运行时的一些了解。在对象上调用方法是Objective-C中经常使用的功能,即消息传递,而Objective-C是C的超集,所以与C不同,Objective-C使用动态绑定,也就是runtime。Objective-C的消息传递和消息机制就不多说了。今天我们主要讲动态方法,也就是函数调用。2、几个相关函数下图详细总结了各个函数调用的顺序和执行的前提。消息传递函数调用1.对象收到无法破译的消息后,会先调用所属类的+(.BOOL)resolveInstanceMethod:(SEL)sel当找不到SEL的IML时会执行该方法在运行时。这个函数是类使用class_addMethod添加函数的机会。根据文档,如果添加的功能代码被实现,则返回YES,如果没有实现,则返回NO。例如,创建了一个新项目。首先,我在ViewController类中执行doSomething1方法。代码如下////ViewController.m//RuntimeTest1////CreatedbyHenryChengon15/12/24.//版权所有?(版权符号)2015www.igancao.comAllrightsreserved.//#import"ViewController.h"@interfaceViewController()@end@implementationViewController-(void)viewDidLoad{[superviewDidLoad];[selfperformSelector:@selector(doSomething)];}-(void)didReceiveMemoryWarning{[superdidReceiveMemoryWarning];//Disposeofanyresourcesthatcanberecreated.}@endrunningresult**2015-12-2410:35:37.726RuntimeTest1[1877:337842]-[ViewControllerdoSomething]:1-3016450*2***37:37.729RuntimeTest1[1877:337842]***Terminatingappduetouncaughtexception'NSInvalidArgumentException',reason:'-[ViewControllerdoSomething]:unrecognizedselectorsenttoinstance0x7fe9f3736680'******Firstthrowcallstack:**不出意外,程序崩溃是因为找不到doSomething方法,让我们执行其中的+(BOOL)resolveInstanceMethod:(SEL)sel方法,判断SEL是否为doSomething,则输出add方法here////ViewController.m//RuntimeTest1////CreatedbyHenryChengon15/12/24.//版权所有?(版权符号)2015www.igancao.comAllrightsreserved.//#import"ViewController.h"@interfaceViewController()@end@implementationViewController-(void)viewDidLoad{[superviewDidLoad];[selfperformSelector:@selector(doSomething)];}+(BOOL)resolveInstanceMethod:(SEL)sel{if(sel==@selector(doSomething)){NSLog(@"addmethodhere");returnYES;}返回[superresolveInstanceMethod:sel];}-(void)didReceiveMemoryWarning{[superdidReceiveMemoryWarning];//Disposeofanyresourcesthatcanberrecreated.}@end继续运行,然后看日志**2015-12-2410:47:24.687RuntimeTest1[2007:382077]在这里添加方法****2015-12-2410:47:24.687RuntimeTest1[2007:382077]-[ViewControllerdoSomething]:unrecognizedselectorsenttoinstance0x7f9568c331f0****2015-1671time0:7102[Runtime2:24]:382077]***终止应用程序duetouncaughtexception'NSInvalidArgumentException',原因:'-[ViewControllerdoSomething]:unrecognizedselectorsente0x7f9568c331f0'*****Firstthrowcallstack:**可以看到程序还是崩溃了,但是我们可以看到这里输出了add方法,也就是说我们+(BOOL)resolveInstanceMethod:(SEL)sel这个方法被执行了,并且进入了判断,所以这里,我们可以做一些操作让这个方法得到对应,以免去***-(void)doesNotRecognizeSelector:(SEL)aSelector方法而崩溃,接下来,我们继续操作如下////ViewController.m//RuntimeTest1////CreatedbyHenryChengon15/12/24.//版权所有?(版权符号)2015www.igancao.comAllrightsreserved.//#import"ViewController.h"#import[objc/runtime.h](由于识别问题将尖括号改为方括号)@interfaceViewController()@end@implementationViewController-(void)viewDidLoad{[superviewDidLoad];[selfperformSelector:@selector(doSomething)];}+(BOOL)resolveInstanceMethod:(SEL)sel{if(sel==@selector(doSomething)){NSLog(@"addmethodhere");class_addMethod([selfclass],sel,(IMP)dynamicMethodIMP,"v@:");returnYES;}return[superresolveInstanceMethod:sel];}voiddynamicMethodIMP(idself,SEL_cmd){NSLog(@"doSomethingSEL");}-(void)didReceiveMemoryWarning{[superdidReceiveMemoryWarning];//迪poseofanyresourcesthatcanberecreated.}@end导入并执行了+(BOOL)resolveInstanceMethod:(SEL)sel中的方法class_addMethod,然后定义了一个voiddynamicMethodIMP(idself,SEL_cmd)函数,运行项目,见日志**2015-12-2411:45:11.934RuntimeTest1[2284:478571]addmethodhere****2015-12-2411:45:11.934RuntimeTest1[2284:478571]doSomethingSEL**这时候我们发现程序并没有崩溃,还输出了doSomethingSEL,表示我们通过runtime成功的给我们的类添加了一个方法关于方法class_addMethod,定义如下:cls在这个类中添加一个方法,即方法添加的类名和方法名,这个imp可以随便用,实现这个方法的函数类型定义返回数字String的值类型和参数类型,这里比如"v@:",其中v为void,带table的返回类型为空,@代表参数,这里指id(self),这里:指的是methodSEL(_cmd),比如我定义了一个函数intnewMethod(idself,SEL_cmd,NSString*str){return100;}那么添加这个函数的方法应该是ass_addMethod([selfclass],@selector(newMethod),(IMP)newMethod,"i@:@");2.如果在+(BOOL)resolveInstanceMethod:(SEL)sel中没有找到方法或者添加的方法消息向下传递给-(id)forwardingTargetForSelector:(SEL)aSelector看是否有对象可以执行这个方法,我们重新建一个工程,然后新建一个类叫SecondViewController,里面有一个-(void)secondVCMethod方法,如下////SecondViewController.m//RuntimeTest2////CreatedbyHenryChengon15/12/24.//Copyright?(版权符号)2015www.igancao.com版权所有。//#import"SecondViewController.h"@interfaceSecondViewController()@end@implementationSecondViewController-(void)viewDidLoad{[superviewDidLoad];//Doanyadditionalsetupafterloadingtheview.}-(void)secondVCMethod{NSLog(@"ThisissecondVCmethod!");}-(void)didReceiveMemoryWarning{[superdidReceiveMemoryWarning];//处理任何可以创建的资源//Getthenewviewcontrollerusing[seguedestinationViewController].//Passtheselectedobjecttothenewviewcontroller.}*/@end工程结构应该是这样的,现在我要调用ViewController中的方法-(void)secondVCMethod,我们知道ViewController和SecondViewController没有继承关系,按照正常步骤做程序肯定会崩溃,因为在ViewController中找不到方法-(void)secondVCMethod////ViewController.m//RuntimeTest2////CreatedbyHenryChengon15/12/24.//Copyright?(Copyrightsymbol)2015www.igancao.comAllrightsreserved.//#import"ViewController.h"#import@interfaceViewController()@end@implementationViewController-(void)viewDidLoad{[superviewDidLoad];[selfperformSelector:@selector(secondVCMethod)];}-(void)didReceiveMemoryWarning{[superdidReceiveMemoryWarning];//Disposeofanyresourcesthatcanberrecreated.}@endRunningresult**2015-12-2413:54:44.314RuntimeTest2[3164:835814]-[ViewControllersecondVCMethod]:unrecognizedselectorsenttoinstance0x7fc3a8535c10****2015-14-untime124:3:3R24[3:3R24]24[ViewControllersecondVCMethod]3164:835814]***Terminatingappduetouncaughtexception'NSInvalidArgumentException',reason:'-[ViewControllersecondVCMethod]:unrecognizedselectorsenttoinstance0x7fc3a8535c10'******Firstthrowcallstack:**现在我们来处理这个消息,如下////ViewController.m//RuntimeTest2////CreatedbyHenryChengon15/12/24.//版权所有?(版权标志)2015www.igancao.comAllrightsreserved.//#import"ViewController.h"#import@interfaceViewController()@end@implementationViewController-(void)viewDidLoad{[superviewDidLoad];[selfperformSelector:@selector(secondVCMethod)];}-(id)forwardingTargetForSelector:(SEL)aSelector{Classclass=NSClassFromString(@"SecondViewController");UIViewController*vc=class.new;if(aSelector==NSSelectorFromString(@"secondVCMeth)od")){NSLog(@"secondVCdothis!");returnvc;}returnil;}+(BOOL)resolveInstanceMethod:(SEL)sel{return[superresolveInstanceMethod:sel];}-(void)didReceiveMemoryWarning{[superdidReceiveMemoryWarning];//Disposeofanyresourcesthatcanberrecreated.}@endOperationresult**2015-12-2414:00:34.168RuntimeTest2[3284:870957]secondVCdothis!****2015-12-2414:00:34.169RuntimeTest2[3284:870957]ThisissecondVCmethod!**我们会发现-(void)secondVCMethod被执行了,程序并没有崩溃,原因就在这一步-(id)forwardingTargetForSelector:(SEL)aSelector{Classclass=NSClassFromString(@"SecondViewController");UIViewController*vc=class.new;if(aSelector==NSSelectorFromString(@"secondVCMethod")){NSLog(@"secondVCdothis!");returnvc;}returnnil;}当没有找到方法-(void)secondVCMethod时,继续传递消息直到-(id)forwardingTargetForSelector:(SEL)aSelector,然后我在里面创建了一个SecondViewController对象,判断如果有这个方法,就返回nSecondViewController对象。这个功能就是消息的转发。这里我们成功的将消息传递给了SecondViewController,然后让它去执行,这样那个方法就被执行了。同时相当于完成了一次多重继承!三、最后一点当然还有几个功能,在上图中已经表达的很清楚了。有兴趣的可以自己试试,看看消息传递的顺序是怎样的。上面说的runtime知识的冰山一角,runtime的黑魔法远比这个厉害,比如方法的部署(MethodSwizzling)等,在实际项目实战中还是很有用的,会稍后介绍。参考Objective-CRuntimeReferenceObjective-CRuntimeProgrammingGuide

最新推荐
猜你喜欢