runtime应用场景-归档/KVO内部实现原理

作者: 我的梦想之路 | 来源:发表于2016-08-08 23:56 被阅读189次

    新建一个OSX命令行项目,是一个然后看代码加命令行,显示结果

    main.m文件
    
    #import <Foundation/Foundation.h>
    #import "TTSleep.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // OC的代码
            TTSleep *tt  = [[TTSleep alloc] init];
            
            /** 底层c的代码
             TTSleep *tt = ((TTSleep *(*)(id, SEL))(void *)objc_msgSend)((id)((TTSleep *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("TTSleep"), sel_registerName("alloc")), sel_registerName("init"));
             */
        }
        return 0;
    }
    
    命令行编译OC语言

    将OC编译成c语言的东西:cd到此目录下,然后:clang -rewrite-objc main.m
    你就可以看到


    文件名

    runtime的两个必要常识:

    1.Method :成员方法
    2.Ivar:成员变量
    快捷键:cmd+shift+0 :打开官方文档

    runtime的函数:

    1.class_copyIvarList 拷贝出成员变量列表
    2.class_copyMethodList 成员方法

    message的函数:

    1.objc_msgSend:给某一个对象发送消息
    2.objc_msgSendSuper:给对象父类发送消息

    两者开头不同

    Ivar的函数

    ivar_getName:(<#Ivar v#>) :给我一个ivar给你一个名称

    runtime的应用场景:

    1.归档

    在Controllers中写一些方法含义和变量含义

    #import "ViewController.h"
    #import <objc/runtime.h>
    #import <objc/message.h>
    #import "TTPerson.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        unsigned int count = 0;
        /**
         在controller中runtime实现
         runtime是底层C的库,所以C用的最多的就是指针指向首地址
         第一个:class 第二个:conut的指针地址(可以在内部改变值)
         */
        Ivar *ivars = class_copyIvarList([TTPerson class], &count);
        NSLog(@"%d",count); /// 此时的count就是你这个类里面的属性列表
        for (int i =0; i<count; i++) {
            // 取出属性
            Ivar ivar = ivars[i]; ///C语言的指针就像OC中的数组,根据角标获取值
            // 查看变量名称
          const char *name = ivar_getName(ivar);
            NSLog(@"%s",name); // 这里%s就是c里面的字符占位符
        }
    }
    
    

    在自定义类中实现归档

    //  Copyright © 2016年 糖糖. All rights reserved.
    /// 归档
    
    #import "TTPerson.h"
    #import <objc/runtime.h>
    #import <objc/message.h>
    
    @interface TTPerson ()<NSCoding> // 实现归档协议
    
    @end
    
    @implementation TTPerson
    /// 归档
    -(void)encodeWithCoder:(NSCoder *)aCoder{
        
        [aCoder encodeObject:self.name forKey:@"name"];
        /* 所有属性写完:如果有N多个属性,归档会写很多冗余的代码*/
        
        
        /** runtime实现归档
         count:装载成员属性个数,可以在runtime内部改变
         
         */
        unsigned int count = 0;
        //取出成员属性列表
        Ivar *ivars = class_copyIvarList([TTPerson class], &count);
      
        for (int i =0; i<count; i++) {
            // 取出属性
            Ivar ivar = ivars[i]; ///C语言的指针就像OC中的数组,根据角标获取值
            
            // 查看变量名称
            const char *name = ivar_getName(ivar);
            // 根据key-value获取属性
            NSString *key = [NSString stringWithUTF8String:name];
            // 归档
            [aCoder encodeObject:[self valueForKey:key] forKey:key];
        }
        // 在C语言中  只要用到了 copy/new/creat 就一定要释放(不然会造成内存泄露)
        free(ivars);
        
    }
    /// 解档
    - (instancetype)initWithCoder:(NSCoder *)coder
    {
        if (self = [super init]) {
            unsigned int count = 0;
            //取出成员属性列表
            Ivar *ivars = class_copyIvarList([TTPerson class], &count);
            
            for (int i =0; i<count; i++) {
                // 取出属性
                Ivar ivar = ivars[i]; ///C语言的指针就像OC中的数组,根据角标获取值
                
                // 查看变量名称
                const char *name = ivar_getName(ivar);
                // 根据key-value获取属性
                NSString *key = [NSString stringWithUTF8String:name];
                // 解档
                id value = [coder decodeObjectForKey:key];
                //设置到成员变量上面(KVC设置)
                [self setValue:value forKey:key];
            }
            // 在C语言中  只要用到了 copy/new/creat 就一定要释放(不然会造成内存泄露)
            free(ivars);
        }
        return self;
    }
    
    @end
    

    2.KVO内部实现原理:
    东西有点多涉及到了三个类

    KVO原理

    控制器

    ##控制器中
    //  Copyright © 2016年 糖糖. All rights reserved.
    //
    
    #import "ViewController.h"
    #import <objc/runtime.h>
    #import <objc/message.h>
    #import "TTPerson.h"
    #import "TTDog.h"
    
    @interface ViewController ()
    // 强引用
    @property (nonatomic,strong)TTPerson *person;
    @property (nonatomic,strong)TTDog *dog;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.person = [[TTPerson alloc] init];
        self.dog = [[TTDog alloc] init];
        /**注册监听
         NSKeyValueObservingOptionNew:传递新值
         KVO内部实现原理:通过runtime动态的传递了一个对象。
         在运行的时候给TTDog动态的创建并重写了一个 [NSKVONotifying_TTDog setAge]
         */
        [self.dog addObserver:self.person forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
    }
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        self.dog.age = 10;
    }
    

    Person类:只贴写了代码的地方

    //  Copyright © 2016年 糖糖. All rights reserved.
    
    #import "TTPerson.h"
    
    @interface TTPerson ()
    
    @end
    
    @implementation TTPerson
    /// 监听到object的keyPath属性变化为change
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
        NSLog(@"监听到%@的%@属性变化为%@",object,keyPath,change);
    }
    @end
    

    TTDog类

    //  Copyright © 2016年 糖糖. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    @interface TTDog : NSObject
    @property (nonatomic,assign)NSInteger age;
    @end
    

    创建一个dog的子类解释KVO原理

    //  Copyright © 2016年 糖糖. All rights reserved.
    //
    
    #import "NSKVONotifying_TTDog.h"
    
    @implementation NSKVONotifying_TTDog
    -(void)setAge:(NSInteger)age{
        [super setAge:age];
        // 在子类中调用两个方法:内部都会调用TTPerson的observeValueForKeyPath 方法
        [self willChangeValueForKey:@"age"]; //即将改变的时候获取旧值
        [self didChangeValueForKey:@"age"]; // 完成改变时获取新值
    }
    
    输出的日志信息

    今天就先到这里吧,要七夕了,我和代码有个约会,晚安了,各位男神女神们。

    相关文章

      网友评论

      • 阿兹尔: [super setAge:age]; 这个从哪里来的?

      本文标题:runtime应用场景-归档/KVO内部实现原理

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