iOS-runtime通篇详解-下

作者: 抱紧我的小鲤鱼 | 来源:发表于2018-03-12 11:07 被阅读694次
    上接上篇iOS-runtime通篇详解-上
    原创内容,转载请注明出处:

    http://www.jianshu.com/p/4c276f5c338c

    class-response
    class-conforms
    class-is
    //判断是否响应方法
    //入参:类Class,方法名SEL
    //返回:是否响应BOOL值
    BOOL class_respondsToSelector(Class cls, SEL sel)
    
    //判断是否遵循某个协议
    //入参:类Class,协议
    //返回:是否响应BOOL值
    BOOL class_conformsToProtocol(Class cls, Protocol *protocol)
    
    //判断是否是元类
    //入参:类Class
    //返回:返回结果
    BOOL class_isMetaClass(Class cls)
    
    (以上API的)运行测试代码地址在这里:iOS-Rumtime-All:demo-runtime-part3
    
    #import "ViewController.h"
    #import <objc/runtime.h>
    @interface ViewController ()<UITableViewDelegate>
    
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        [self method0];
        BOOL result0 = class_replaceMethod([self class], @selector(method0), (IMP)method1, NULL);
        NSLog(@">>>>>>>>2:%@",@(result0));
        [self method0];
        NSLog(@"\n");
        
        BOOL result1 = class_conformsToProtocol([self class], NSProtocolFromString(@"UITableViewDelegate"));
        BOOL result2 = class_conformsToProtocol([self class], NSProtocolFromString(@"UITableViewDataSource"));
        BOOL result3 = class_addProtocol([self class], NSProtocolFromString(@"UITableViewDataSource"));
        NSLog(@">>>>>>>>3:%@",@(result1));
        NSLog(@">>>>>>>>4:%@",@(result2));
        NSLog(@">>>>>>>>5:%@",@(result3));
        
        BOOL result4 = class_conformsToProtocol([self class], NSProtocolFromString(@"UITableViewDelegate"));
        BOOL result5 = class_conformsToProtocol([self class], NSProtocolFromString(@"UITableViewDataSource"));
        
        NSLog(@">>>>>>>>6:%@",@(result4));
        NSLog(@">>>>>>>>7:%@",@(result5));
        NSLog(@"\n");
        
        BOOL result6 = class_isMetaClass([self class]);
        NSLog(@">>>>>>>>8:%@",@(result6));
        
        BOOL result7 = class_respondsToSelector([self class], @selector(method0));
        NSLog(@">>>>>>>>9:%@",@(result7));
        BOOL result8 = class_respondsToSelector([self class], @selector(method1));
        NSLog(@">>>>>>>>10:%@",@(result8));
    }
    
    -(void)method0{
        NSLog(@">>>>>>>>0");
    
    }
    void method1(){
        NSLog(@">>>>>>>>1");
    }
    +(void)method1{
        NSLog(@">>>>>>>>0");
        
    }
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    @end
    
    (以上运行测试的)打印结果
    demo-runtime-part3[4514:1037966] >>>>>>>>0
    demo-runtime-part3[4514:1037966] >>>>>>>>2:1
    demo-runtime-part3[4514:1037966] >>>>>>>>1
    demo-runtime-part3[4514:1037966] 
    demo-runtime-part3[4514:1037966] >>>>>>>>3:1
    demo-runtime-part3[4514:1037966] >>>>>>>>4:0
    demo-runtime-part3[4514:1037966] >>>>>>>>5:1
    demo-runtime-part3[4514:1037966] >>>>>>>>6:1
    demo-runtime-part3[4514:1037966] >>>>>>>>7:1
    demo-runtime-part3[4514:1037966] 
    demo-runtime-part3[4514:1037966] >>>>>>>>8:0
    demo-runtime-part3[4568:1062173] >>>>>>>>9:1
    demo-runtime-part3[4568:1062173] >>>>>>>>10:0
    
    (以上打印结果的)解析
    #从上面的测试我们可以发现各个函数的作用:
    
    ###class_conformsToProtocol:
    判断某类是否遵循某协议。从打印可以看出代码已经实现的协议UITableViewDelegate已经可以判断出来,后来代码通过class_addProtocol添加的协议也能判断出来。
    由此判断此函数可以判断出来代码添加的协议,动态添加的协议可以获取到。
    ###class_isMetaClass:
    判断是否是元类。不细说了,有兴趣可以看下这篇博客http://www.jianshu.com/p/79b06fabb459
    ###class_respondsToSelector:
    某类是否响应某个方法。从打印结果可以看到,此函数可以判断是否响应某个实例方法,并不能判断静态方法。
    OC里面也有一个类似的函数:
    - (BOOL)respondsToSelector:(SEL)aSelector;
    虽然我们没有源码,但是我们可以猜想上面这个方法是调用或者说在编译的时候被转换成class_respondsToSelector函数。
    
    
    Ivar-get
    //获取变量名
    //入参:变量Ivar
    //返回:char数组
    const char *ivar_getName(Ivar v)
    
    //获取变量类型描述()
    //入参:变量Ivar
    //返回:char数组
    const char *ivar_getTypeEncoding(Ivar v)
    
    //获取变量基地址偏移量
    //入参:变量Ivar
    //返回:偏移量ptrdiff_t,有兴趣的可以参考这篇文章http://www.jianshu.com/p/be00d998a4ed
    ptrdiff_t ivar_getOffset(Ivar v)
    
    (以上API的)运行测试代码地址在这里:iOS-Rumtime-All:demo-runtime-part4
    
    #import "ViewController.h"
    #import <objc/runtime.h>
    @interface ViewController ()
    {
        NSObject *_property0;
        UIView *_property1;
        UIViewController *_property2;
        float _property3;
        int _property4;
    }
    @property (nonatomic,strong)NSObject *property5;
    @property (nonatomic,strong)UIView   *property6;
    @property (nonatomic,strong)UIViewController *property7;
    @property (nonatomic,assign)float property8;
    @property (nonatomic,assign)int property9;
    
    @end
    
    @implementation ViewController
    
    //编码值   含意
    //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)
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        
        //class获取--获取整个成员变量列表
        /**
         *  1.获取所有私有变量和属性
         *  2.获取的私有变量的名和定义的名一模一样
         *  3.获取的属性的名前面都添加了下划线
         */
        unsigned int copyIvarListCount = 0;
        Ivar *ivars = class_copyIvarList([self class], &copyIvarListCount);
        for (NSInteger i = 0; i< copyIvarListCount; i ++) {
            
            Ivar ivar = ivars[i];
            const char *name = ivar_getName(ivar);
            const char *encoding = ivar_getTypeEncoding(ivar);
            ptrdiff_t   ofset    = ivar_getOffset(ivar);
            
            NSLog(@">>>>>>>>0:%@:%@:%td",
                  [NSString stringWithUTF8String:name],
                  [NSString stringWithUTF8String:encoding],
                  ofset);
            
        }
        free(ivars);
        
    }
    @end
    
    
    (以上运行测试的)打印结果
    demo-runtime-part4[4611:1075970] >>>>>>>>0:_property0:@"NSObject":760
    demo-runtime-part4[4611:1075970] >>>>>>>>0:_property1:@"UIView":768
    demo-runtime-part4[4611:1075970] >>>>>>>>0:_property2:@"UIViewController":776
    demo-runtime-part4[4611:1075970] >>>>>>>>0:_property3:f:784
    demo-runtime-part4[4611:1075970] >>>>>>>>0:_property4:i:788
    demo-runtime-part4[4611:1075970] >>>>>>>>0:_property8:f:792
    demo-runtime-part4[4611:1075970] >>>>>>>>0:_property9:i:796
    demo-runtime-part4[4611:1075970] >>>>>>>>0:_property5:@"NSObject":800
    demo-runtime-part4[4611:1075970] >>>>>>>>0:_property6:@"UIView":808
    demo-runtime-part4[4611:1075970] >>>>>>>>0:_property7:@"UIViewController":816
    
    
    (以上打印结果的)解析
    #从上面的测试我们可以发现各个函数的作用:
    ###ivar_getName:
    获取变量名。定义的变量直接获取变量的名字,定义的属性获取的属性会在原来属性名的基础上加一条下划线。
    ###ivar_getTypeEncoding:
    获取变量的类型编码。编码对应的含义上面代码已经写了。
    ###ivar_getOffset:
    获取变量基地址偏移量。有兴趣的可以参考这篇文章http://www.jianshu.com/p/be00d998a4ed
    
    property-get
    //获取属性名
    //入参:属性objc_property_t
    //返回:属性名char字符串
    const char *property_getName(objc_property_t property)
    
    //获取属性的属性
    //入参: objc_property_t
    //返回:属性的属性char数组描述
    const char *property_getAttributes(objc_property_t property)
    
    (以上API的)运行测试代码地址在这里:iOS-Rumtime-All:demo-runtime-part5
    #import "ViewController.h"
    #import <objc/runtime.h>
    @interface ViewController ()
    {
        NSObject *_property0;
        UIView *_property1;
        UIViewController *_property2;
        float _property3;
        int _property4;
    }
    @property (nonatomic,strong)NSObject *_property5;
    @property (nonatomic,  weak)UIView   *property6;
    @property (nonatomic,strong)UIViewController *property7;
    
    @property (nonatomic,assign)float property8;
    @property (nonatomic,assign)int property9;
    @property (nonatomic,strong,readonly)UIViewController *property10;
    @property (nonatomic,readwrite)UIViewController *property11;
    @property (nonatomic,  copy)NSString *property12;
    @end
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //class获取--获取整个属性列表(只获取属性不获取变量)
        /**
         *  1.获取所有属性
         *  2.获取的属性名和你代码写的一样,获取出来的属性名不自动添加下划线
         */
        unsigned int copyPropertyListCount = 0;
        objc_property_t *propertys = class_copyPropertyList([self class], &copyPropertyListCount);
        for (NSInteger i = 0; i < copyPropertyListCount; i++) {
            objc_property_t property = propertys[i];
            const char *name = property_getName(property);
            const char *attributes = property_getAttributes(property);
            
            NSLog(@">>>>>>>>0:%@:%@",
                  [NSString stringWithUTF8String:name],
                  [NSString stringWithUTF8String:attributes]);
        }
        free(propertys);
    }
    @end
    
    (以上运行测试的)打印结果
    demo-runtime-part5[4916:1190177] >>>>>>>>0:_property5:T@"NSObject",&,N,V__property5
    demo-runtime-part5[4916:1190177] >>>>>>>>0:property6:T@"UIView",W,N,V_property6
    demo-runtime-part5[4916:1190177] >>>>>>>>0:property7:T@"UIViewController",&,N,V_property7
    demo-runtime-part5[4916:1190177] >>>>>>>>0:property8:Tf,N,V_property8
    demo-runtime-part5[4916:1190177] >>>>>>>>0:property9:Ti,N,V_property9
    demo-runtime-part5[4916:1190177] >>>>>>>>0:property10:T@"UIViewController",R,N,V_property10
    demo-runtime-part5[4916:1190177] >>>>>>>>0:property11:T@"UIViewController",&,N,V_property11
    demo-runtime-part5[4916:1190177] >>>>>>>>0:property12:T@"NSString",C,N,V_property12
    
    
    (以上打印结果的)解析
    #从上面的测试我们可以发现各个函数的作用:
    ###property_getName:
    这个没什么好说的,获取属性的名字。
    
    ###property_getAttributes:
    获取属性的属性。从上面打印可以看出,属性定义的时候写法不一样,打印出来的属性的属性也不一样,下面简单说明一下property_getAttributes结果的每个符号的意义:
    T:后面跟的是属性的运行时类型。
    &:表示strong,W表示weak
    N:noautomic
    V:后面跟的是属性所对应的变量
    有兴趣了解更多请参考这篇博客:http://www.jianshu.com/p/cefa1da5e775
    
    property-copy
    //获取属性的属性列表
    //入参: objc_property_t,int变量指针
    //返回: objc_property_attribute_t属性的属性结构体包含键和值
    //typedef struct {
    //        const char *name;
    //        const char *value;
    //} objc_property_attribute_t;
    //使用方法暂时不做demo演示了,请参考上面代码演示
    objc_property_attribute_t *property_copyAttributeList(objc_property_t property, unsigned int *outCount)
    //获取属性的属性值
    //入参: objc_property_t,attributeName属性的属性键
    //返回: 属性的属性值char数组
    //使用方法暂时不做demo演示了,请参考上面代码演示
    char *property_copyAttributeValue(objc_property_t property, const char *attributeName)
    
    method-get
    //获取方法名
    //入参: 方法描述结构体Method
    //返回: 方法名SEL
    SEL method_getName(Method m)
    
    //获取方法实现
    //入参: objc_property_t,attributeName属性的属性键
    //返回: 属性的属性值char数组
    IMP method_getImplementation(Method m)
    
    //获取方法的属性信息(包括返回值类型,参数类型等等信息)
    //入参: 方法描述结构体Method
    //返回: 属性值char数组
    const char *method_getTypeEncoding(Method m)
    
    //获取方法的参数个数
    //入参: 方法描述结构体Method
    //返回: 属性的个数
    unsigned int method_getNumberOfArguments(Method m)
    
    //获取返回值的类型
    //入参: 方法描述结构体Method,返回值的容器char数组,容器大小。返回值的类型字符串会被copy到第二个参数里。
    //返回: void
    void method_getReturnType(Method m, char *dst, size_t dst_len)
    
    //获取参数的属性参数
    //入参: 方法描述结构体Method,返回值的容器char数组,容器大小。返回值的类型字符串会被copy到第二个参数里。
    //返回: void
    void method_getArgumentType(Method m, unsigned int index, char *dst, size_t dst_len) 
    
    //获取方法的描述
    //入参: 方法描述结构体Method
    //返回: 方法描述结构体objc_method_description
    //struct objc_method_description {
    //  SEL name;     
    //  char *types;  
    //};
    struct objc_method_description *method_getDescription(Method m)
    
    (以上API的)运行测试代码地址在这里:iOS-Rumtime-All:demo-runtime-part6
    
    #import "ViewController.h"
    #import <objc/runtime.h>
    @interface ViewController ()
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        Method result0 = class_getInstanceMethod([self class], @selector(method0:agu1:agu2:agu3:agu4:agu5:));
        //获取方法名
        //入参: 方法描述结构体Method
        //返回: 方法名SEL
        SEL result1 = method_getName(result0);
        NSLog(@">>>>>>>>0:%@",NSStringFromSelector(result1));
        
        //获取方法实现
        //入参: objc_property_t,attributeName属性的属性键
        //返回: 属性的属性值char数组
        IMP result2 =  method_getImplementation(result0);
        
        //获取方法的属性信息(包括返回值类型,参数类型等等信息)
        //入参: 方法描述结构体Method
        //返回: 属性值char数组
        const char *result3 = method_getTypeEncoding(result0);
        NSLog(@">>>>>>>>2:%@",[NSString stringWithUTF8String:result3]);
    
        //获取方法的参数个数
        //入参: 方法描述结构体Method
        //返回: 属性的个数
        unsigned int result4 = method_getNumberOfArguments(result0);
        NSLog(@">>>>>>>>3:%ud",result4);
        for (unsigned int i =0 ; i < result4; i++) {
            char result5[1024] = {};
            //获取参数的属性参数
            //入参: 方法描述结构体Method,返回值的容器char数组,容器大小。返回值的类型字符串会被copy到第二个参数里。
            //返回: void
            method_getArgumentType(result0, i, result5, 1024);
            NSLog(@">>>>>>>>4:%ud",result4);
        }
    
        //获取返回值的类型
        //入参: 方法描述结构体Method,返回值的容器char数组,容器大小。返回值的类型字符串会被copy到第二个参数里。
        //返回: void
        char result6[1024] = {};
        method_getReturnType(result0, result6, 1024);
        NSLog(@">>>>>>>>5:%s",result6);
    
        //获取方法的描述
        //入参: 方法描述结构体Method
        //返回: 方法描述结构体objc_method_description
        //struct objc_method_description {
        //    SEL name;     
        //    char *types;  
        //};
        struct objc_method_description result7 = *method_getDescription(result0);
        NSLog(@">>>>>>>>5:%@:%@",NSStringFromSelector(result7.name),[NSString stringWithUTF8String:result7.types]);
    }
    -(NSArray *)method0:(NSArray *)agu0
                   agu1:(CGFloat)agu1
                   agu2:(NSObject *)agu2
                   agu3:(NSString *)agu3
                   agu4:(CGRect)agu4
                   agu5:(UIView *)agu5{
        NSLog(@">>>>>>>>1");
        return @[];
    }
    @end
    
    
    (以上运行测试的)打印结果
    demo-runtime-part6[5528:1411303] >>>>>>>>0:method0:agu1:agu2:agu3:agu4:agu5:
    demo-runtime-part6[5528:1411303] >>>>>>>>2:@88@0:8@16d24@32@40{CGRect={CGPoint=dd}{CGSize=dd}}48@80
    demo-runtime-part6[5528:1411303] >>>>>>>>3:8d
    demo-runtime-part6[5528:1411303] >>>>>>>>4:@
    demo-runtime-part6[5528:1411303] >>>>>>>>4::
    demo-runtime-part6[5528:1411303] >>>>>>>>4:@
    demo-runtime-part6[5528:1411303] >>>>>>>>4:d
    demo-runtime-part6[5528:1411303] >>>>>>>>4:@
    demo-runtime-part6[5528:1411303] >>>>>>>>4:@
    demo-runtime-part6[5528:1411303] >>>>>>>>4:{CGRect={CGPoint=dd}{CGSize=dd}}
    demo-runtime-part6[5528:1411303] >>>>>>>>4:@
    demo-runtime-part6[5528:1411303] >>>>>>>>5:@
    demo-runtime-part6[5528:1411303] >>>>>>>>6:method0:agu1:agu2:agu3:agu4:agu5::@88@0:8@16d24@32@40{CGRect={CGPoint=dd}{CGSize=dd}}48@80
    
    
    (以上打印结果的)解析
    
    #从上面的测试我们可以发现各个函数的作用:
    ###method_getName:
    获取方法名。这个函数和NSStringFromSelector(result1)的结果是一样的。
    可以猜想这两个函数可能存在一定的关系。
    ###method_getImplementation:
    获取方法的实现。
    ###method_getTypeEncoding:
    获取方法的编码:
    @88@0:8@16d24@32@40{CGRect={CGPoint=dd}{CGSize=dd}}48@80。
    详细解释请参考这篇文章http://blog.csdn.net/zhenganzhong_csdn/article/details/47094407
    ###method_getNumberOfArguments:
    获取方法参数的个数。
    ###method_getArgumentType:
    获取参数的类型。这里返回的类型编码请参考上面的码表。
    ###method_getReturnType:
    获取返回值的类型。这里返回的类型编码请参考上面的码表。
    ###method_getDescription:
    获取方法的描述。包括方法的名字和方法的编码值。
    这两个值我们上面已经介绍了。请才考上面的代码。
    
    method-copy
    //同上method_getReturnType
    char *method_copyReturnType(Method m)
    //同上method_getArgumentType
    char *method_copyArgumentType(Method m, unsigned int index)
    
    method-set
    //设置方法的实现
    //入参:方法Method,要设置的方法的实现IMP
    //返回:设置方法的实现
    IMP method_setImplementation(Method m, IMP imp)
    
    method-exchange(method swing)
    //交换方法的实现
    //入参:要交换实现的两个方法Method
    //返回:void
    void method_exchangeImplementations(Method m1, Method m2)
    
    (以上API的)运行测试代码地址在这里:iOS-Rumtime-All:demo-runtime-part7
    
    #import "ViewController.h"
    #import <objc/runtime.h>
    @interface ViewController ()
    
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
    //    方法      方法名     方法实现
    //    method0  method0   method0
    //    method1  method1   method1
    //    method2  method2   method2
        [self method0];
        Method result0 = class_getInstanceMethod([self class], @selector(method0));
        Method result1 = class_getInstanceMethod([self class], @selector(method1));
        Method result2 = class_getInstanceMethod([self class], @selector(method2));
        method_setImplementation(result0, method_getImplementation(result1));
        
    //    方法      方法名     方法实现
    //    method0  method0   method1
    //    method1  method1   空
    //    method2  method2   method2
        [self method0];
        method_exchangeImplementations(result1, result2);
        
    //    方法      方法名     方法实现
    //    method0  method0   method1
    //    method1  method1   method2
    //    method2  method2   空
        [self method1];
    }
    
    -(void)method0{
        NSLog(@">>>>>>>>0:%s",__func__);
    }
    -(void)method1{
        NSLog(@">>>>>>>>1:%s",__func__);
    }
    -(void)method2{
        NSLog(@">>>>>>>>2:%s",__func__);
    }
    @end
    
    (以上运行测试的)打印结果
    demo-runtime-part7[6157:1606828] >>>>>>>>0:-[ViewController method0]
    demo-runtime-part7[6157:1606828] >>>>>>>>1:-[ViewController method1]
    demo-runtime-part7[6157:1606828] >>>>>>>>2:-[ViewController method2]
    
    (以上打印结果的)解析
    #从上面的测试我们可以发现各个函数的作用:
    ###class_getInstanceMethod:
    获取类的实例方法。
    ###method_setImplementation:
    设置方法的实现。如上代码,在没有设置实现前,每个方法的方法名和实现都是一一对应的。
    后来我们将method0的实现设置为method1后,我们再调用method0的时候,执行的是method1.
    这时候method0的实现是method1,method1的实现为method1。
    ###method_exchangeImplementations:
    交换两个方法的实现。如上代码我们再上一步的基础上交换了method1和method2的实现。在交换前method1的实现是method1,method2的实现是method2。
    交换完成后method1的实现是method2,method2的实现是method1,所以交换完成后调用method1执行的是method2.
    
    objc
    //创建一个类对(类和元类)
    //入参:父类Class,要创建的类名,类大小。
    //返回:创建的新类
    Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes) 
    
    //注册类
    //入参:类Class
    //返回:void
    void objc_registerClassPair(Class cls)
    
    //获取所有已注册类的列表
    //入参:int型变量指针
    //返回:类的指针列表
    Class *objc_copyClassList(unsigned int *outCount)
    
    //销毁一个类及其相关联的类
    //入参:类Class
    //返回:void
    //注意销毁的是类不是对象
    void objc_disposeClassPair(Class cls)
    
    (以上API的)运行测试代码地址在这里:iOS-Rumtime-All:demo-runtime-part8
    #import "ViewController.h"
    #import <objc/runtime.h>
    @interface ViewController ()
    
    @end
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        //创建--一个新类和元类
        /**
         * 1.添加动态添加变量只能在 objc_allocateClassPair 和 objc_registerClassPair之间才可以
         */
        Class result0 = objc_allocateClassPair([NSObject class], "ObjectSubClass", 0);
        NSLog(@">>>>>>>>0:%@",NSStringFromClass(result0));
        id    result1 = [[result0 alloc]init];
        NSLog(@">>>>>>>>2:%@",result1);
        class_addIvar(result0, "_attribute1", sizeof(NSString *), log(sizeof(NSString *)), "i");
        Ivar ivar = class_getInstanceVariable(result0, "_attribute1");//获取变量,如果没获取到说明不存在
        NSLog(@">>>>>>>>3:%@",[NSString stringWithUTF8String:ivar_getName(ivar)]);
        
        //注册--在应用中注册由objc_allocateClassPair创建的类
        objc_registerClassPair(result0);
        //销毁--一个类及其相关联的类,注意不是对象
        objc_disposeClassPair(result0);
    }
    @end
    
    
    (以上运行测试的)打印结果
    demo-runtime-part8[6341:1657279] >>>>>>>>0:ObjectSubClass
    demo-runtime-part8[6341:1657279] >>>>>>>>1:<ObjectSubClass: 0x608000013bc0>
    demo-runtime-part8[6341:1657279] >>>>>>>>2:_attribute1
    
    (以上打印结果的)解析
    #从上面的测试我们可以发现各个函数的作用:
    ###objc_allocateClassPair:
    创建一个类对,包括新类和元类。
    如上代码,我们创建了一个类ObjectSubClass并且给这个类创建了实例和动态添加了属性。并且操作都已成功。
    添加动态添加变量只能在 objc_allocateClassPair 和 objc_registerClassPair之间才可以。
    ###objc_registerClassPair:
    注册新创建的类对。新创建的类对需要注册才能生效。
    ###objc_disposeClassPair:
    销毁刚才创建的类对。
    
    
    sel
    //获取方法名
    //入参:方法名SEL
    //返回:方法名char
    const char *sel_getName(SEL sel)
    
    //获取方法id
    //入参:属性方法名char
    //返回:SEL
    SEL sel_getUid(const char *str)
    
    //注册方法
    //入参:要注册的方法名
    //返回:返回方法名选择器
    //在系统中注册一个方法,将方法名映射到一个选择器,并返回这个选择器
    SEL sel_registerName(const char *str)
    
    //对比方法选择器
    //入参:要对比的两个选择器SEL
    //返回:是否相等
    BOOL sel_isEqual(SEL lhs, SEL rhs)
    
    (以上API的)运行测试代码地址在这里:iOS-Rumtime-All:demo-runtime-part9
    #import "ViewController.h"
    #import <objc/runtime.h>
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //获取方法名
        //入参:方法名SEL
        //返回:方法名char
        const char *result0 = sel_getName(@selector(method0));
        NSLog(@">>>>>>>>0:%s",result0);
        //获取方法id
        //入参:属性方法名char
        //返回:SEL
        SEL result1 =  sel_getUid(result0);
        NSLog(@">>>>>>>>1:%@",NSStringFromSelector(result1));
        //注册方法
        //入参:要注册的方法名
        //返回:返回方法名选择器
        //在系统中注册一个方法,将方法名映射到一个选择器,并返回这个选择器
        SEL result2 = sel_registerName("method1");
        NSLog(@">>>>>>>>2:%@",NSStringFromSelector(result2));
        
        //对比方法选择器
        //入参:要对比的两个选择器SEL
        //返回:是否相等
        BOOL result3 = sel_isEqual(result1, result2);
        NSLog(@">>>>>>>>2:%d",result3);
    }
    -(void)method0{}
    @end
    
    
    (以上运行测试的)打印结果
    demo-runtime-part9[7824:1762586] >>>>>>>>0:method0
    demo-runtime-part9[7824:1762586] >>>>>>>>1:method0
    demo-runtime-part9[7824:1762586] >>>>>>>>2:method1
    demo-runtime-part9[7824:1762586] >>>>>>>>2:0
    
    (以上打印结果的)解析
    #从上面的测试我们可以发现各个函数的作用:
    ###sel_getName:
    获取方法名。如上打印结果method0的方法名为method0。
    ###sel_getUid:
    获取选择器。如上打印结果method0的选择器为method0。
    ###sel_registerName:
    注册方法名。
    在系统中注册一个方法,将方法名映射到一个选择器,并返回这个选择器。
    如上打印,系统注册并且返回了
    ###sel_isEqual:
    如上打印。method0和method1是不相等的,所以打印0;
    
    object
    //获取变量实例
    //入参:变量所属实例,变量结构体Ivar
    //返回:变量实例
    id object_getIvar(id obj, Ivar ivar)
    
    //给实例设置变量实例
    //入参:要设置变量的实例,变量结构体Ivar,变量值value
    //返回:void
    void object_setIvar(id obj, Ivar ivar, id value)
    
    (以上API的)运行测试代码地址在这里:iOS-Rumtime-All:demo-runtime-part10
    #import "ViewController.h"
    #import <objc/runtime.h>
    @interface ViewController ()
    {
        NSArray *_property0;
    }
    @property (nonatomic,strong)NSObject *property1;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        
        unsigned int result0 = 0;
        Ivar *result1 = class_copyIvarList([self class], &result0);
        for (unsigned int i = 0; i < result0; i++) {
            Ivar result2 = result1[i];
            //获取变量实例
            //入参:变量所属实例,变量结构体Ivar
            //返回:变量实例
            id result3 = object_getIvar(self, result2);
            NSLog(@">>>>>>>>0:%@",result3);
            NSString *result4 = [NSString stringWithUTF8String:ivar_getName(result2)];
            if ([result4 isEqualToString:@"_property0"]) {
                //给实例设置变量实例
                //入参:要设置变量的实例,变量结构体Ivar,变量值value
                //返回:void
                object_setIvar(self, result2, @[@"0",@"1",@"2",@"3",@"4",@"5"]);
            }else if([result4 isEqualToString:@"_property1"]){
                //给实例设置变量实例
                //入参:要设置变量的实例,变量结构体Ivar,变量值value
                //返回:void
                object_setIvar(self, result2, [[NSObject alloc]init]);
            }
            id result5 = object_getIvar(self, result2);
            NSLog(@">>>>>>>>1:%@",result5);
        }
    }
    @end
    
    (以上运行测试的)打印结果
    demo-runtime-part10[7907:1785561] >>>>>>>>0:(null)
    demo-runtime-part10[7907:1785561] >>>>>>>>1:(
        0,
        1,
        2,
        3,
        4,
        5
    )
    demo-runtime-part10[7907:1785561] >>>>>>>>0:(null)
    demo-runtime-part10[7907:1785561] >>>>>>>>1:<NSObject: 0x610000006a10>
    
    (以上打印结果的)解析
    #从上面的测试我们可以发现各个函数的作用:
    ###object_getIvar:
    获取实例变量值。总共有两个参数,第一个是实例,第二个是变量的结构体,前面已经说过了。这个函数的作用获取某个实例的属性的值。
    ###object_setIvar:
    设置变量值。三个参数。第一个是要设置变量的实例,第二个变量的结构体。第三个是要设置属性值。
    这里要着重说明一下的是,变量设置需要类型对的上,如果类型对不上会产生很多问题。
    
    

    在篇尾

    由于篇幅太长,简书都不能编辑了,只好拆成几篇。
    程序员不需要打赏,只希望自己的项目能帮助更多人,请支持我的git开源框架:TFEasyCoder
    下篇:iOS-runtime通篇详解-拓展

    相关文章

      网友评论

      本文标题:iOS-runtime通篇详解-下

      本文链接:https://www.haomeiwen.com/subject/onvqattx.html