美文网首页
ios-Runtime(运行时)

ios-Runtime(运行时)

作者: 瑞阳gg | 来源:发表于2017-08-09 17:20 被阅读0次
    利用runtime来实现归档解档
     //告诉系统,归档哪些属性
    - (void)encodeWithCoder:(NSCoder *)coder
    {
        //利用runtime 来归档!!
        unsigned int count = 0;
        /*
         拷贝Person类里的所有成员变量
         &count  获得count的地址 来改变它的值
         &       取地址符
         count   Person这个类的成员变量的个数
         */
        Ivar * ivars = class_copyIvarList([self class], &count);
    
        /*   
         好玩的!
         Ivar * ivars = class_copyIvarList([UIButton class], &count);
         获取button的所有属性 然后通过kvc改变button的属性
         */
        
        for (int i = 0; i < count; i++) {
            //拿出每一个Ivar
            Ivar ivar = ivars[i];
            /*
             ivar_getName   获取变量名称
             */
            const char * name = ivar_getName(ivar);
            /*
             C语言的char转换成oc对象的字符串
             */
            NSString * KEY = [NSString stringWithUTF8String:name];
            //归档
            /*
             通过KVC拿到值         [self valueForKey:KEY]
             */
            [coder encodeObject: [self valueForKey:KEY] forKey:KEY];
        }
        
        //C语言里面!! 一旦遇到了copy creat new 需要释放
        free(ivars);
        
    }
    //解档
    - (instancetype)initWithCoder:(NSCoder *)coder
    {
        self = [super init];
        if (self) {
            unsigned int count = 0;
            Ivar * ivars = class_copyIvarList([self class], &count);
            for (int i = 0; i < count; i++) {
                Ivar ivar = ivars[i];
                const char * name = ivar_getName(ivar);
                NSString * KEY = [NSString stringWithUTF8String:name];
                //解档
                id value = [coder decodeObjectForKey:KEY];
                //通过KVC 设置
                [self setValue:value forKey:KEY];
            }
            
            free(ivars);
        }
        return self;
    }
    
    
    方法交换 俗称 OC的方法欺骗
    #import "NSURL+url.h"
    #import <objc/message.h>
    
    @implementation NSURL (url)
    
    //重写!! 将系统所有的行为都改变了!!
    //+(instancetype)URLWithString:(NSString *)URLString{
    ////    原来系统的处理方式被我们改变了!!
    //    //创建url
    //    NSURL * url = [[NSURL alloc]initWithString:URLString];
    //    if (!url) {
    //        NSLog(@"哥么为nil!!");
    //    }
    //    return url;
    //}
    
    //
    
    //load 方法当类被加载的时候调用
    +(void)load
    {
        
    //    IMP URLWithStr = class_getMethodImplementation([self class], @selector(URLWithString:));
    //    IMP HKURLSTR = class_getMethodImplementation([self class], @selector(HK_URLWSTR:));
    //    
    //    Method URLWithStrM = class_getClassMethod([self class], @selector(URLWithString:));
    //    
    //    method_setImplementation(URLWithStrM, HKURLSTR);
        
        
        
        /*
         class_getClassMethod      获取类方法
         class_getInstanceMethod   获取对象方法
         */
        Method URLWithStr = class_getClassMethod([self class], @selector(URLWithString:));
        Method HKURLSTR = class_getClassMethod([self class], @selector(HK_URLWSTR:));
        //交换
        method_exchangeImplementations(URLWithStr, HKURLSTR);
        
        
    }
    
    
    //看起来就是死循环的代码!!其实不是
    +(instancetype)HK_URLWSTR:(NSString *)str{
        //创建URL
        /*
         这时候因为方法已经交换过了
         [NSURL HK_URLWSTR:str];     走到了系统的方法实现!!
         [NSURL URLWithString:str];  走到了自定义的 HK_URLWSTR 方法
         */
        NSURL * url = [NSURL HK_URLWSTR:str];
        if (!url) {
            NSLog(@"哥么nil");
        }
        return nil;
    }
    
    KVO的实现原理
    /**原理
     Person里有一个属性age
     KVO监听age属性变化
     首先系统会动态的创建一个类继承自Person类
     然后重写age的set方法(这些都是通过runtime来做的)
      - (void)setAge:(int)age
      {
          [self willChangeValueForKey:@"age"];
          [super setAge:age];
          [self didChangeValueForKey:@"age"];
      }
     然后改变p的指针,指向子类;
     这时候再改变age这个属性的值得时候,他就会走子类的set方法;
     willChangeValueForKey和didChangeValueForKey这两个方法会调observeValueForKeyPath;
     这样就能监听到属性的变化;
     */
    
    Person * p = [[Person alloc] init];
    [p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
    
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
    {
    }
    
    
    用runtime来实现KVO

    objc_allocateClassPair创建一个类
    class_addMethod添加一个方法

    //监听某个对象的属性
    //谁调用监听谁
    -(void)HKaddObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context{
        
        //1.动态创建一个类
        //1.1 动态类名
        NSString * oldName = NSStringFromClass([self class]);
        NSString * newStr = [@"HankKVO_" stringByAppendingString:oldName];
        const char * newName = [newStr UTF8String];
        //1.2 继承的类  2.类名
        Class Myclass = objc_allocateClassPair([self class], newName, 0);
        //添加set方法
        class_addMethod(Myclass, @selector(setName:), (IMP)setName, "v@:@");
        
        
        //注册该类
        objc_registerClassPair(Myclass);
        
        //修改观察者的isa指针
        object_setClass(self, Myclass);
        //将观察者保存到当前对象中
        objc_setAssociatedObject(self, @"hank666", observer, OBJC_ASSOCIATION_ASSIGN);
        
    }
    
    
    //函数搞定!!
    void setName(id self,SEL _cmd,NSString * newName){
        //调用super
        //1.保存当前类型
        Class class = [self class];
        //改变
        object_setClass(self, class_getSuperclass(class));
        objc_msgSend(self, @selector(setName:),newName);//相当于调用 super setName
        object_setClass(self, class);
        
        
        //拿到观察者
        id obsever = objc_getAssociatedObject(self, @"hank666");
        
        objc_msgSend(obsever, @selector(observeValueForKeyPath:ofObject:change:context:),@"name",self,nil,nil);
        
    }
    

    相关文章

      网友评论

          本文标题:ios-Runtime(运行时)

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