博客> iOS - runtime相关
iOS - runtime相关
2017-10-18 11:31 评论:0 阅读:223 OS_JJ
ios runtime相关(待更新)
**1.什么是 runtime ?**

    rumtime是运行时库,基于c语言的api接口,
    作用是动态的创建一个类 动态的添加属性和方法 遍历属性和方法名 动态修改属性和方法等等 
    1.能动态产生一个类,一个成员变量,一个方法
    2.能动态修改一个类,一个成员变量,一个方法
    3.能动态删除一个类,一个成员变量,一个方法

    //类在runtime中的表示
    struct objc_class {
        Class isa;//指针,顾名思义,表示是一个什么,
        //实例的isa指向类对象,类对象的isa指向元类
    #if !__OBJC2__
        Class super_class;  //指向父类
        const char *name;  //类名
        long version;
        long info;
        long instance_size
        struct objc_ivar_list *ivars //成员变量列表
        struct objc_method_list **methodLists; //方法列表
        struct objc_cache *cache;//缓存
        //一种优化,调用过的方法存入缓存列表,下次调用先找缓存
        struct objc_protocol_list *protocols //协议列表
        #endif
    } OBJC2_UNAVAILABLE;
    /* Use `Class` instead of `struct objc_class *` */

**2.runtime的头文件**

    #import <objc> 包含对类、成员变量、属性、方法的操作
     #import <objc> 包含消息机制

**3.常用方法**

    //动态拦截调用
    + (BOOL)resolveClassMethod:(SEL)sel;
    + (BOOL)resolveInstanceMethod:(SEL)sel;

    //遍历相关
    class_copyMethodList(返回一个指向类的方法数组的指针) 
    class_copyIvarList (返回一个指向类的成员变量数组的指针)
    class_copyPropertyList(返回一个指向类的属性数组的指针)

    //修改属性
        objc_getAssociatedObject 
        objc_setAssocaitedObject

    //交换
        class_getInstanceMethod
        method_exchangeImplementation(systemMethod, swizzMethod)

4.应用

        1)动态的遍历一个类的所有成员变量,用于字典转模型,归档解档操作

           1.导入头文件<objc>     */    

               - (void)viewDidLoad {    
                      [super viewDidLoad];    

                     unsigned int count = 0;   
                     Ivar *ivars = class_copyIvarList([BDPerson class], &count);//获得一个指向该类成员变量的指针   
                     for (int i =0; i &lt; count;i++){
                            Ivar ivar = ivars[i];        //根据ivar获得其成员变量的名称---&gt;C语言的字符串      
                          const char *name = ivar_getName(ivar);       
                           NSString *key = [NSString stringWithUTF8String:name];      
                          NSLog(@"%d----%@",i,key);
                    }

     2)可以利用遍历类的属性,来快速的进行归档操作;将从网络上下载的json数据进行字典转模型。遵守 NSCoding协议,实现以下两个方法
            - (void)encodeWithCoder:(NSCoder *)encoder{    
                //归档存储自定义对象    
                unsigned int count = 0;  
                //获得指向该类所有属性的指针   
                objc_property_t *properties =     class_copyPropertyList([BDPerson class], &count);   
                for (int i =0; i &lt; count; i ++) {        
                //获得        
                objc_property_t property = properties[i];        //根据objc_property_t获得其属性的名称---&gt;C语言的字符串       
               const char *name = property_getName(property);   
               NSString *key = [NSString   stringWithUTF8String:name];       
               //      编码每个属性,利用kVC取出每个属性对应的数值            
               [encoder encodeObject:[self valueForKeyPath:key] forKey:key]; 
             }}

            - (instancetype)initWithCoder:(NSCoder *)decoder{    
                  //归档存储自定义对象    
                    unsigned int count = 0;   
                 //获得指向该类所有属性的指针   
                   objc_property_t *properties = class_copyPropertyList([BDPerson class], &count);   
                   for (int i =0; i &lt; count; i ++) {       
                   objc_property_t property = properties[i];        //根据objc_property_t获得其属性的名称---&gt;C语言的字符串       
                   const char *name = property_getName(property); 
                     NSString *key = [NSString stringWithUTF8String:name];        //解码每个属性,利用kVC取出每个属性对应的数值      
                   [self setValue:[decoder decodeObjectForKey:key] forKeyPath:key];  
            }   
             return self;
            }

    3)交换方法

      一.例如数组越界问题,防止系统崩溃(NSMutableArray 添加空值会出现崩溃)

         ① 新建一个分类,分类中引入头文件,实现下列方法

            + (void)load{
                Method orginalMethod = class_getInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(addObject:));
                Method newMethod = class_getInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(Mn_addObject:));

                method_exchangeImplementations(orginalMethod, newMethod);
            }

            - (void)Mn_addObject:(id)object{
                if (object) {
                    [self Mn_addObject:object];
                }
            }

    ②在项目文件中,正常使用,若添加空值,不会崩溃只会出现报警信息

         NSMutableArray *arr = [NSMutableArray array];
        [arr addObject:nil];

    二.生命周期

        ①创建分类

            //load方法会在类第一次加载的时候被调用
            //调用的时间比较靠前,适合在这个方法里做方法交换
            + (void)load{
                //方法交换应该被保证,在程序中只会执行一次
                static dispatch_once_t onceToken;
                dispatch_once(&onceToken, ^{
                    //获得viewController的生命周期方法的selector
                    SEL systemSel = @selector(viewWillAppear:);
                    //自己实现的将要被交换的方法的selector
                    SEL swizzSel = @selector(swiz_viewWillAppear:);
                    //两个方法的Method
                    Method systemMethod = class_getInstanceMethod([self class], systemSel);
                    Method swizzMethod = class_getInstanceMethod([self class], swizzSel);
                    //首先动态添加方法,实现是被交换的方法,返回值表示添加成功还是失败
                    BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
                    if (isAdd) {
                        //如果成功,说明类中不存在这个方法的实现
                        //将被交换方法的实现替换到这个并不存在的实现
                        class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
                    }else{
                        //否则,交换两个方法的实现
                        method_exchangeImplementations(systemMethod, swizzMethod);
                    }
                });
            }
            - (void)swiz_viewWillAppear:(BOOL)animated{
                //这时候调用自己,看起来像是死循环
                //但是其实自己的实现已经被替换了
                [self swiz_viewWillAppear:animated];
                NSLog(@"swizzle");
            }

        ②在一个自己定义的viewController中重写viewWillAppear,Run起来看看输出吧!

            - (void)viewWillAppear:(BOOL)animated{
                [super viewWillAppear:animated];
                NSLog(@"viewWillAppear");
            }

 4)关联属性

  typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
      OBJC_ASSOCIATION_ASSIGN = 0,  //相当于属性中的assign         
      OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,    //retain,monatomic
      OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  //copy,nonatomic
      OBJC_ASSOCIATION_RETAIN = 01401,    //retain 
      OBJC_ASSOCIATION_COPY = 01403     //copy    
  };

         //添加关联对象
        - (void)addAssociatedObject:(id)object{
            objc_setAssociatedObject(self, @selector(getAssociatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        }
        //获取关联对象
        - (id)getAssociatedObject{
            return objc_getAssociatedObject(self, _cmd);
        }

        //样例
        - (void)viewDidLoad {
            [super viewDidLoad];
            // Do any additional setup after loading the view, typically from a nib.
            UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
            btn.backgroundColor = [UIColor blackColor];
            btn.frame = CGRectMake(100, 100, 60, 30);
            [self.view addSubview:btn];
             objc_setAssociatedObject(btn, myBtnKey, @"mybtn", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
            [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];            
        }

        - (void)btnClick:(id)sender {
            NSString *str = objc_getAssociatedObject(sender, myBtnKey);
            /**
             *  CODE
             */
        }

5)方法拦截

       + (BOOL)resolveClassMethod:(SEL)sel;
        + (BOOL)resolveInstanceMethod:(SEL)sel;

图片.png

收藏
0
sina weixin mail 回到顶部