runtime

runtime的所有知识基本都围绕两个中心(1)类的各个方面的动态配置(2)消息传递

要动态配置类就需要知道类的本质是什么,我们可以从里面看到类的定义:

structobjc_class{Class isa  OBJC_ISA_AVAILABILITY;#if!__OBJC2__Class super_class;//父类constchar*name;//类名longversion;//类的版本信息,默认为0longinfo;//类信息,供运行期使用的一些位标识longinstance_size;//类的实例变量大小structobjc_ivar_list*ivars;// 类的成员变量链表structobjc_method_list**methodLists;// 方法链表structobjc_cache*cache;//方法缓存structobjc_protocol_list*protocols;//协议链表#endif} OBJC2_UNAVAILABLE;

isa和super_class

不同的类中可以有相同的方法,同一个类中不可以有相同的方法,判断是不是同一个方法只和方法名有关系,和参数没关系。

比如说:

-(void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath{}

的方法名为

@selector(tableView:canEditRowAtIndexPath:)

可见方法名里面并没有体现参数的位置,所以是否是同一个方法取决于方法名是否相同,和参数没关系。

要找到方法首先要先确定是那个类。isa和super_class是找到实现函数的关键映射,决定找到存放在哪个类的方法实现。(isa用于自省确定所属类,super_class确定继承关系)。

实例对象的isa指针指向类,类的isa指针指向其元类(metaClass)。对象就是一个含isa指针的结构体。类存储实例对象的方法列表,元类存储类的方法列表,元类也是类对象。

当创建实例对象时,分配的内存包含一个objc_object数据结构,然后是类到父类直到根类NSObject的实例变量的数据。NSObject类的alloc和allocWithZone:方法使用函数class_createInstance来创建objc_object数据结构。

向一个Objective-C对象发送消息时,运行时库会根据实例对象的isa指针找到这个实例对象所属的类。Runtime库会在类的方法列表由super_class指针找到父类的方法列表直至根类NSObject中去寻找与消息对应的selector指向的方法。找到后即运行这个方法。

isa和super_class指针的图解.png

isa:实例对象->类->元类->(不经过父元类)直接到根元类(NSObject的元类),根元类的isa指向自己;

superclass:类->父类->...->根类NSObject,元类->父元类->...->根元类->根类,NSObject的superclass指向nil。

在正式学习runtime之前你需要知道的

属性概念

SEL:类成员方法的指针,但不同于C语言中的函数指针,函数指针直接保存了方法的地址,但SEL只是方法编号。

IMP:一个函数指针,保存了方法的地址

Method:方法的结构体,其中保存了方法的名字,实现和类型描述字符串

代码

//在runtime.h里面我们可以看到定义//Method 是一个方法结构体的指针typedefstructobjc_method *Method;//方法的结构体包含了方法需要的信息structobjc_method {    SEL method_name;char*method_types;//方法返回值,和各个参数类型等的字符串描述IMP method_imp;}//根据函数获取函数的SEL@selector()//获取函数指针SEL的函数名字符串NSString*NSStringFromSelector(SEL aSelector);//根据函数名获取函数指针SELNSSelectorFromString(NSString*aSelectorName);//获取类Class的字符串描述NSString*NSStringFromClass(Class aClass);//根据类的字符串描述获取类ClassClass _NullableNSClassFromString(NSString*aClassName);//获取协议的字符串描述(协议名字)NSString*NSStringFromProtocol(Protocol *proto)//根据协议名字获取协议对象Protocol * _NullableNSProtocolFromString(NSString*namestr)

上面的方法我平时用到的频率还不算太低,在这里就暂时不实际跑一下了。知道了上面的那些平常还有些频率用到的函数或概念,那么接下来学习runtime就有些轻松了。

下面会用到的编码值

//下面对应的编码值可以在官方文档里面找到//编码值  含意//c    代表char类型//i    代表int类型//s    代表short类型//l    代表long类型,在64位处理器上也是按照32位处理//q    代表long long类型//C    代表unsigned char类型//I    代表unsigned int类型//S    代表unsigned short类型//L    代表unsigned long类型//Q    代表unsigned long long类型//f    代表float类型//d    代表double类型//B    代表C++中的bool或者C99中的_Bool//v    代表void类型//*    代表char *类型//@    代表对象类型//#    代表类对象 (Class)//:    代表方法selector (SEL)//[array type]  代表array//{name=type…}  代表结构体//(name=type…)  代表union//bnum  A bit field of num bits//^type    A pointer to type//?    An unknown type (among other things, this code is used for function pointers)

(语法&API)class-get

//获取类名//入参:类Class//返回:类名char数组constchar*class_getName(Class cls)//获取父类//入参:类Class//返回:类ClassClassclass_getSuperclass(Class cls)//获取实例大小(返回size_t)//入参:实例的类Class//返回:大小size_t//深究请看这篇文章http://www.jianshu.com/p/df6b252fbaaesize_tclass_getInstanceSize(Class cls)//获取类中指定名称实例成员变量的信息//入参:类Class,变量名//返回:变量信息Ivar//* 1.实例变量是指变量不是属性.例如某类有个属性为:username 那么它对应的实例变量为_username//* 2.这个方法可以获取属性的变量,也可以获取私有变量(这点很重要)//* 3.如果获取的变量为空,那么 ivar_getName和 ivar_getTypeEncoding 获取的值为空,那么[NSString stringWithUTF8String:ivar1Name] 执行崩溃Ivarclass_getInstanceVariable(Class cls,constchar*name)//类成员变量的信息//入参:类Class,变量名char数组//返回:Ivar//* 1.目前没有找到关于Objective-C中类变量的信息,一般认为Objective-C不支持类变量。注意,返回的列表不包含父类的成员变量和属性。Ivarclass_getClassVariable(Class cls,constchar*name)//获取指定的属性//入参:类Class,属性名char数组//返回:属性objc_property_t// *  1.属性不是变量,此方法只能获取属性// *  2.如果属性不存在那么返回的结构体为0(可以参考下面的判断)// *  3.属性不存在获取property_getName 和 property_getAttributes 会崩溃objc_property_tclass_getProperty(Class cls,constchar*name)//获取方法实现//入参:类Class,方法名SEL//返回:方法实现IMPIMPclass_getMethodImplementation(Class cls, SEL name)//获取方法实现//入参:类Class,方法名SEL//返回:方法实现IMPIMPclass_getMethodImplementation_stret(Class cls, SEL name)//获取类方法//入参:类Class,方法名SEL//返回:方法MethodMethodclass_getClassMethod(Class cls, SEL name)

(以上API的)运行测试代码地址在这里:TFRumtimeAll:demo-runtime-part0

#import@interfaceViewController()@property(nonatomic,strong)NSArray*property0;@end@implementationViewController- (void)viewDidLoad {    [superviewDidLoad];//获取类名//入参:类Class//返回:类名char数组constchar*result0 = class_getName([ViewControllerclass]);NSLog(@">>>>>>>>0:%@",[NSStringstringWithUTF8String:result0]);//获取父类//入参:类Class//返回:类ClassClass result1 = class_getSuperclass([ViewControllerclass]);NSLog(@">>>>>>>>1:%@",result1);//获取实例大小(返回size_t)//入参:实例的类Class//返回:大小size_t//深究请看这篇文章http://www.jianshu.com/p/df6b252fbaaesize_t result2 = class_getInstanceSize([ViewControllerclass]);NSLog(@">>>>>>>>2:%zu",result2);//获取类中指定名称实例成员变量的信息//入参:类Class,变量名//返回:变量信息Ivar//* 1.实例变量是指变量不是属性.例如某类有个属性为:username 那么它对应的实例变量为_username//* 2.这个方法可以获取属性的变量,也可以获取私有变量(这点很重要)//* 3.如果获取的变量为空,那么 ivar_getName和 ivar_getTypeEncoding 获取的值为空,那么[NSString stringWithUTF8String:ivar1Name] 执行崩溃constchar*result3 = [@"property0"UTF8String];    Ivar result4 = class_getInstanceVariable([ViewControllerclass], result3);NSLog(@">>>>>>>>3:%@",result4);//获取指定的属性//入参:类Class,属性名char数组//返回:属性objc_property_t// *  1.属性不是变量,此方法只能获取属性// *  2.如果属性不存在那么返回的结构体为0(可以参考下面的判断)// *  3.属性不存在获取property_getName 和 property_getAttributes 会崩溃constchar*result5 = [@"property0"UTF8String];    objc_property_t result6 = class_getProperty([ViewControllerclass], result5);NSLog(@">>>>>>>>4:%@",[NSStringstringWithUTF8String:property_getName(result6)]);//获取方法实现//入参:类Class,方法名SEL//返回:方法实现IMPIMP result7 = class_getMethodImplementation([ViewControllerclass],@selector(method0));    result7();//获取方法实现//入参:类Class,方法名SEL//返回:方法实现IMPIMP result8 = class_getMethodImplementation_stret([ViewControllerclass],@selector(method1));    result8();//获取类方法//入参:类Class,方法名SEL//返回:方法MethodMethod result9 = class_getClassMethod([ViewControllerclass],@selector(viewDidLoad));NSLog(@">>>>>>>>7:%@",result9);}-(void)method0{NSLog(@">>>>>>>>5");}-(void)method1{NSLog(@">>>>>>>>6");}@end

(以上运行测试的)打印结果

demo-runtime-part0[984:85403]>>>>>>>>0:ViewControllerdemo-runtime-part0[984:85403]>>>>>>>>1:UIViewControllerdemo-runtime-part0[984:85403]>>>>>>>>2:768demo-runtime-part0[984:85403]>>>>>>>>3:(null)demo-runtime-part0[984:85403]>>>>>>>>4:property0demo-runtime-part0[984:85403]>>>>>>>>5demo-runtime-part0[984:85403]>>>>>>>>6demo-runtime-part0[984:85403]>>>>>>>>7:(null)

(以上打印结果的)解析

#从上面的测试我们可以发现各个函数的作用:###class_getName:获取类的字符串描述,由此我们可以想到上面提到的一个OC方法:NSStringFromClass(),这个方法和此函数的作用是一样的。那么我们可以猜想NSStringFromClass()的上层实现是runtime的class_getName函数(有兴趣的朋友可以反编译验证一下)。###class_getSuperclass:获取某个类的父类。同理我们可以想到NSObject里面有个属性:@property(readonly) Class superclass;那么这个属性的get方法也可能是调用的此函数。###class_getInstanceSize:获取实例大小,单位是字节。这个函数暂时不着重说了。有时间可以和其他几个不常用的函数单独细说一下。###class_getInstanceVariable:获取实例的变量。在上面打印3我们看到获取到的是nil。从代码中我们命名定义了:@property(nonatomic,strong)NSArray*property0;但是为什么找不到?是因为此函数获取的是变量,上面的属性对应的变量是_property0所以以property0变量名找是找不到的。需要强调的是,此函数可以找到任何属于这个类的变量,包括分类添加的变量,程序动态添加的变量等。###class_getProperty:获取类属性。注意这里是属性,不是变量。此函数只能获取类的属性。该函数返回是是objc_property_t结构体。上面demo代码里面用到了函数:property_getName,这个函数的是获取属性名,下面会说到###class_getMethodImplementation:###class_getMethodImplementation_stret:获取函数的实现。返回函数的实现指针IMP.从上面代码看到我们获取了method0和method1的函数实现并result7();result8();执行了两个函数,并且函数得到了执行。###class_getClassMethod:获取类方法Method结构体。从runtime.h源码里可以找到源码:typedefstructobjc_method *Method;structobjc_method {    SEL method_name;char*method_types;    IMP method_imp;}

总结:

从上面部分runtime API可以看出,iOS中所谓的runtime.h只是一组c语音的api,它用的c语言的语法,可以做一些OC做不到的功能.

它的语法有一定的规律,这点很重要。比如你可以使用xcode的智能提示,如下图:

runtime0.png

知道了上面的规律,那接下来学就方便多了。继续往下看:

class-copy

//获取变量列表//入参:类Class,int变量指针//返回:变量信息Ivar列表//*  1.获取所有私有变量和属性对应的变量//*  2.获取的私有变量的名和定义的名一样//*  3.获取的属性的名前面都添加了下划线//*  4.不能获取Category添加的变量(动态绑定的变量)Ivar *class_copyIvarList(Class cls,unsignedint*outCount)//获取属性列表(只获取属性不获取变量)//入参:类Class,int变量指针//返回:属性信息objc_property_t列表//*  1.获取所有属性//*  2.获取的属性名和你代码写的一样,获取出来的属性名不自动添加下划线//*  3.不能获取Category添加的属性。objc_property_t *class_copyPropertyList(Class cls,unsignedint*outCount)//获取方法列表//入参:类Class,int变量指针//返回:方法信息Method列表//*  1.获取所有实例方法,不包含静态方法//*  2.不获取父类的方法//*  3.隐式的get set 方法也能获取到//*  4.可以获取分类和动态添加的方法。Method *class_copyMethodList(Class cls,unsignedint*outCount)//获取协议列表//入参:类Class,int变量指针//返回:方法协议Protocol列表//* 1.不能获取分类实现的协议Protocol * __unsafe_unretained*class_copyProtocolList(Class cls,unsignedint*outCount)

(以上API的)运行测试代码地址在这里:TFRumtimeAll:demo-runtime-part1

#import"ViewController.h"#import@interfaceViewController(){NSArray*_property0;NSArray*property1;}@property(nonatomic,strong)UIColor*property2;@end@implementationViewController- (void)viewDidLoad {    [superviewDidLoad];//class获取--获取整个成员变量列表/**

    *  1.获取所有私有变量和属性

    *  2.获取的私有变量的名和定义的名一模一样

    *  3.获取的属性的名前面都添加了下划线

    */unsignedintcopyIvarListCount =0;    Ivar *ivars = class_copyIvarList([selfclass], ©IvarListCount);for(NSIntegeri =0; i< copyIvarListCount; i ++) {                Ivar ivar = ivars[i];constchar*name = ivar_getName(ivar);NSLog(@">>>>>>>>0:class_copyIvarList:%s",name);    }    free(ivars);//释放NSLog(@"\n");//class获取--获取整个属性列表(只获取属性不获取变量)/**

    *  1.获取所有属性

    *  2.获取的属性名和你代码写的一样,获取出来的属性名不自动添加下划线

    */unsignedintcopyPropertyListCount =0;    objc_property_t *propertys = class_copyPropertyList([selfclass], ©PropertyListCount);for(NSIntegeri =0; i < copyPropertyListCount; i++) {        objc_property_t property = propertys[i];constchar*name = property_getName(property);NSLog(@">>>>>>>>1:copyPropertyList:%s",name);    }    free(propertys);//释放NSLog(@"\n");//class获取--获取整个类的实例方法的方法列表/**

    *  1.获取所有实例方法,不包含静态方法

    *  2.不获取父类的方法

    *  3.隐式的get set 方法也能获取到

    */unsignedintcopycopyMethodListCount =0;    Method *methods = class_copyMethodList([selfclass], ©copyMethodListCount);for(NSIntegeri =0; i < copycopyMethodListCount; i++) {        Method method = methods[i];        SEL name = method_getName(method);NSLog(@">>>>>>>>2:copyMethodList:%@",NSStringFromSelector(name));    }    free(methods);//释放NSLog(@"\n");//添加--协议/**

    * 1.class_addProtocol  参数含义:第一个:要添加协议的类,第二个:协议对象

    * 2.获取协议列表具体细节参照Class1里的内容

    */unsignedintcopyProtocolListCount =0;    Protocol * __unsafe_unretained*protocals = class_copyProtocolList([selfclass], ©ProtocolListCount);for(NSIntegeri =0; i < copyProtocolListCount; i++) {        Protocol * protocal = protocals[i];constchar*name = protocol_getName(protocal);NSLog(@">>>>>>>>3:copyProtocolList:%s",name);    }    free(protocals);//释放NSLog(@"\n");}- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section{return0;}// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath{returnnil;}- (void)didReceiveMemoryWarning {    [superdidReceiveMemoryWarning];// Dispose of any resources that can be recreated.}@end

(以上运行测试的)打印结果

demo-runtime-part1[3085:698841]>>>>>>>>0:class_copyIvarList:_property0demo-runtime-part1[3085:698841]>>>>>>>>0:class_copyIvarList:property1demo-runtime-part1[3085:698841]>>>>>>>>0:class_copyIvarList:_property2demo-runtime-part1[3085:698841] demo-runtime-part1[3085:698841]>>>>>>>>1:copyPropertyList:property2demo-runtime-part1[3085:698841]>>>>>>>>1:copyPropertyList:hashdemo-runtime-part1[3085:698841]>>>>>>>>1:copyPropertyList:superclassdemo-runtime-part1[3085:698841]>>>>>>>>1:copyPropertyList:descriptiondemo-runtime-part1[3085:698841]>>>>>>>>1:copyPropertyList:debugDescriptiondemo-runtime-part1[3085:698841] demo-runtime-part1[3085:698841]>>>>>>>>2:copyMethodList:property2demo-runtime-part1[3085:698841]>>>>>>>>2:copyMethodList:setProperty2:demo-runtime-part1[3085:698841] >>>>>>>>2:copyMethodList:.cxx_destructdemo-runtime-part1[3085:698841]>>>>>>>>2:copyMethodList:tableView:numberOfRowsInSection:demo-runtime-part1[3085:698841] >>>>>>>>2:copyMethodList:tableView:cellForRowAtIndexPath:demo-runtime-part1[3085:698841] >>>>>>>>2:copyMethodList:didReceiveMemoryWarningdemo-runtime-part1[3085:698841]>>>>>>>>2:copyMethodList:viewDidLoaddemo-runtime-part1[3085:698841] demo-runtime-part1[3085:698841]>>>>>>>>3:copyProtocolList:UITableViewDelegatedemo-runtime-part1[3085:698841]>>>>>>>>3:copyProtocolList:UITableViewDataSourcedemo-runtime-part1[3085:698841]

(以上打印结果的)解析

#从上面的测试我们可以发现各个函数的作用:###class_copyIvarList:拷贝变量列表。返回的一个Ivar列表的指针。获取Ivar需要遍历这个列表。注意:调用copy的函数需要释放资源free();###class_copyPropertyList:拷贝属性列表。返回的一个objc_property_t列表的指针。获取objc_property_t需要遍历这个列表。注意:调用copy的函数需要释放资源free();###class_copyMethodList:拷贝方法列表。返回的一个Method列表的指针。获取Method需要遍历这个列表。注意:调用copy的函数需要释放资源free();此函数可以获取分类方法。###class_copyProtocolList:拷贝协议列表。返回的一个Ivar列表的指针。获取Ivar需要遍历这个列表。注意:调用copy的函数需要释放资源free();此函数不能获取分类中添加的协议。此函数可以获取动态添加的协议。

class-add

//动态添加变量//入参:类Class,变量名char数组,变量类型大小size_t,变量在内存中的对齐方式,变量的type类型//返回:添加结果,是否成功。//* 1.只能给动态创建的类添加变量也就是用 objc_allocateClassPair 创建的类//* 2.添加变量只能在函数 objc_allocateClassPair 和 class_getInstanceVariable 之间添加才有效BOOLclass_addIvar(Class cls,constchar*name, size_t size, uint8_t alignment,constchar*types)//动态添加方法//入参:类Class,方法名SEL,方法实现IMP,方法返回值各个参数类型等配置字符串//返回:添加结果,是否成功。//* 1.添加属性不用再objc_registerClassPair之前,因为添加属性其实就是添加变量的set 和 get方法而已//* 2.添加的属性和变量不能用kvc设置值和取值BOOLclass_addMethod(Class cls, SEL name, IMP imp,constchar*types)//动态添加协议//入参:类Class,协议结构体Protocol//返回:添加结果,是否成功。BOOLclass_addProtocol(Class cls, Protocol *protocol)//动态添加属性//入参:类Class,属性名char数组,属性的配置属性,objc_property_attribute_t,属性的属性数量。//返回:添加结果,是否成功。BOOLclass_addProperty(Class cls,constchar*name,constobjc_property_attribute_t *attributes,unsignedintattributeCount)

(以上API的)运行测试代码地址在这里:TFRumtimeAll:demo-runtime-part2

#import"ViewController.h"#import@interfaceViewController(){NSArray*_property0;NSArray*property1;}@property(nonatomic,strong)UIColor*property2;@end@implementationViewController//get方法NSString*attribute0Getter(idclassInstance, SEL _cmd) {    Ivar ivar = class_getInstanceVariable([classInstanceclass],"_attribute0");//获取变量,如果没获取到说明不存在returnobject_getIvar(classInstance, ivar);}//set方法voidattribute0Setter(idclassInstance, SEL _cmd,NSString*newName) {    Ivar ivar = class_getInstanceVariable([classInstanceclass],"_attribute0");//获取变量,如果没获取到说明不存在idoldName = object_getIvar(classInstance, ivar);if(oldName != newName) object_setIvar(classInstance, ivar, [newNamecopy]);}- (void)viewDidLoad {    [superviewDidLoad];//添加--为动态创建类添加变量/**

    * 1.只能给动态创建的类添加变量也就是用 objc_allocateClassPair 创建的类

    * 2.添加变量只能在函数 objc_allocateClassPair 和 class_getInstanceVariable 之间添加才有效

    */Class CreatClass0 = objc_allocateClassPair([NSObjectclass],"CreatClass0",0);    class_addIvar(CreatClass0,"_attribute0",sizeof(NSString*), log(sizeof(NSString*)),"i");    Ivar ivar = class_getInstanceVariable(CreatClass0,"_attribute0");//获取变量,如果没获取到说明不存在NSLog(@">>>>>>>>0:%@",[NSStringstringWithUTF8String:ivar_getName(ivar)]);    objc_registerClassPair(CreatClass0);NSLog(@"\n");//添加--为动态创建的类添加变量然后添加属性,类和变量和属性都是动态创建的/**

    * 1.各个属性:暂时不知道

    * 2.下面这个反驳了上面的第二标,这个证明id不是不会报错,规律是如果id调用的是系统的类的方法,那么就不会报错,

    详细介绍:上面的@selector(name) 和 @selector(setName:) name是好多系统类都有方法,所以id会认为本身代表的是那个类

    所以不会报错,但是如果你硬写一个完全没有的方法,它就会报错

    * 3.添加属性不用再objc_registerClassPair之前,因为添加属性其实就是添加变量的set 和 get方法而已

    * 4.添加的属性和变量不能用kvc设置值和取值

    */objc_property_attribute_t type2 = {"T","@\"NSString\""};    objc_property_attribute_t ownership2 = {"C",""};// C = copyobjc_property_attribute_t backingivar2  = {"V","_attribute0"};    objc_property_attribute_t attrs2[] = { type2, ownership2, backingivar2 };    class_addProperty(CreatClass0,"_attribute0", attrs2,3);        SELgetter=NSSelectorFromString(@"attribute0");    SELsetter=NSSelectorFromString(@"setAttribute0:");BOOLsuc0 = class_addMethod(CreatClass0,getter, (IMP)attribute0Getter,"@@:");BOOLsuc1 = class_addMethod(CreatClass0,setter, (IMP)attribute0Setter,"v@:@");NSLog(@">>>>>>>>3:%@:%@",@(suc0),@(suc1));ididclass = [[CreatClass0 alloc]init];NSLog(@">>>>>>>>1:%@",[idclass performSelector:getterwithObject:nil]);    [idclass performSelector:setterwithObject:@"为动态创建类先添加变量再添加属性"];NSLog(@">>>>>>>>2:%@",[idclass performSelector:getterwithObject:nil]);//class获取--获取整个类的实例方法的方法列表/**

    *  1.获取所有实例方法,不包含静态方法

    *  2.不获取父类的方法

    *  3.隐式的get set 方法也能获取到

    *  4.关于Method的更多用法参考Class2类

    */unsignedintcopycopyMethodListCount =0;    Method *methods = class_copyMethodList([selfclass], ©copyMethodListCount);for(NSIntegeri =0; i < copycopyMethodListCount; i++) {        Method method = methods[i];        SEL name = method_getName(method);NSLog(@">>>>>>>>2:copyMethodList:%@",NSStringFromSelector(name));    }    free(methods);//释放NSLog(@"\n");BOOLresult0 = class_addProtocol([selfclass],NSProtocolFromString(@"UITableViewDelegate"));NSLog(@">>>>>>>>3:添加协议成功");//添加--协议/**

    * 1.class_addProtocol  参数含义:第一个:要添加协议的类,第二个:协议对象

    * 2.获取协议列表具体细节参照Class1里的内容

    */unsignedintcopyProtocolListCount =0;    Protocol * __unsafe_unretained*protocals = class_copyProtocolList([selfclass], ©ProtocolListCount);for(NSIntegeri =0; i < copyProtocolListCount; i++) {        Protocol * protocal = protocals[i];constchar*name = protocol_getName(protocal);NSLog(@">>>>>>>>4:copyProtocolList:%s",name);    }    free(protocals);//释放NSLog(@"\n");    }- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section{return0;}// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath{returnnil;}- (void)didReceiveMemoryWarning {    [superdidReceiveMemoryWarning];// Dispose of any resources that can be recreated.}@end

(以上运行测试的)打印结果

demo-runtime-part2[3439:758345]>>>>>>>>0:_attribute0demo-runtime-part2[3439:758345] demo-runtime-part2[3439:758345]>>>>>>>>3:1:1demo-runtime-part2[3439:758345]>>>>>>>>1:(null)demo-runtime-part2[3439:758345]>>>>>>>>2:为动态创建类先添加变量再添加属性demo-runtime-part2[3439:758345]>>>>>>>>2:copyMethodList:property2demo-runtime-part2[3439:758345]>>>>>>>>2:copyMethodList:setProperty2:demo-runtime-part2[3439:758345] >>>>>>>>2:copyMethodList:.cxx_destructdemo-runtime-part2[3439:758345]>>>>>>>>2:copyMethodList:tableView:numberOfRowsInSection:demo-runtime-part2[3439:758345] >>>>>>>>2:copyMethodList:tableView:cellForRowAtIndexPath:demo-runtime-part2[3439:758345] >>>>>>>>2:copyMethodList:didReceiveMemoryWarningdemo-runtime-part2[3439:758345]>>>>>>>>2:copyMethodList:viewDidLoaddemo-runtime-part2[3439:758345] demo-runtime-part2[3439:758345]>>>>>>>>3:添加协议成功demo-runtime-part2[3439:758345]>>>>>>>>4:copyProtocolList:UITableViewDelegatedemo-runtime-part2[3439:758345]>>>>>>>>4:copyProtocolList:UITableViewDataSourcedemo-runtime-part2[3439:758345]

(以上打印结果的)解析

#从上面的测试我们可以发现各个函数的作用:###class_addIvar:添加变量。添加属性的具体解释和调用注意点上面备注已经写的很清楚。从打印结果可以看出我们添加属性已经成功。上面代码中涉及到两个函数objc_allocateClassPair和objc_registerClassPair这两个函数我们后面会继续说。###class_addProperty:添加属性。这个函数比较复杂,用到的频率也比较低。有兴趣的朋友可以参考官方文档。###class_addMethod:添加方法。这个函数还是比较重要的,后面说几个runtime的几个应用实例会用的到,更多用法也可以去看一下JSPatch源码。该函数一共需要四个参数,前三个分别是:要添加方法的类,方法名,方法实现。第四个函数的字符串:get方法:第一个个@代表返回的类型为非基本数据类型,如果返回的数据是int那么第一个字符应该为iset方法:第一个个v代表返回的类型为void,如果返回的数据是int那么第一个字符应该为i,最后一个@代表函数的第一个试用参数类型为非基本数据类型set和get方法的共同部分是@:分别代表方法的两个默认函数target和SEL。###class_addProtocol:添加协议。这个函数比较简单,具体上面代码注释已经详细说过了。

class-replace

//属性替换//入参://返回:char数组//低平率函数,有兴趣的可以看官方文档https://developer.apple.com/reference/objectivec/objective_c_runtimevoidclass_replaceProperty(Class cls,constchar*name,constobjc_property_attribute_t*attributes,unsignedintattributeCount)//方法替换//入参:要替换方法所在的类Class,  要替换的方法名SEL,方法的实现,方法的描述字符串//返回:char数组IMPclass_replaceMethod(Class cls, SEL name, IMP imp,constchar*types)

(以上API的)运行测试代码地址在这里:TFRumtimeAll:demo-runtime-part3

#import"ViewController.h"#import@interfaceViewController()@end@implementationViewController- (void)viewDidLoad {    [superviewDidLoad];// Do any additional setup after loading the view, typically from a nib.[selfmethod0];BOOLresult0 = class_replaceMethod([selfclass],@selector(method0), (IMP)method1,NULL);NSLog(@">>>>>>>>2:%@",@(result0));    [selfmethod0];    }-(void)method0{NSLog(@">>>>>>>>0");}voidmethod1(){NSLog(@">>>>>>>>1");}- (void)didReceiveMemoryWarning {    [superdidReceiveMemoryWarning];// Dispose of any resources that can be recreated.}@end

(以上运行测试的)打印结果

demo-runtime-part3[4256:976005]>>>>>>>>0demo-runtime-part3[4256:976005]>>>>>>>>2:1demo-runtime-part3[4256:976005]>>>>>>>>1

(以上打印结果的)解析

#从上面的测试我们可以发现各个函数的作用:###class_replaceMethod:从上面的代码可以看出。我们第一次调用了[selfmethod0];然后把method0替换成了method1,当我再次调用method0的时候执行的是method1方法。上面代码只是简单演示基本功能。更多的用法,后面会说几个应用实例。

链接:https://www.jianshu.com/p/0e6eb2f9ed5d

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

推荐阅读更多精彩内容