美文网首页iOS开发笔记
Runtime访问私有变量和方法及KVO访问

Runtime访问私有变量和方法及KVO访问

作者: 祥子_HelloWorld | 来源:发表于2019-08-08 18:06 被阅读0次
    一、Class

    在之前的文章中我们提到,所有的对象都有个isa指针指向它对应的类Class,而Class是一个objc_class结构体,结构体中:

    • 实例变量列表objc_ivar_list
    • 方法列表objc_method_list
      对应的runtime获取方法:
    Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
    Method *class_copyMethodList(Class cls, unsigned int *outCount)  
    

    首先我们先创建一个Test类:

    //
    //  Test.h
    //  RuntimeKVC
    //
    //  Created by z on 2019/5/24.
    //  Copyright © 2019年 com.jzsec. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface Test : NSObject
    {
        @public
        NSString *name;
        
        /* 头文件中定义私有变量,默认为@protected */
        @private
        NSString *major;
    }
    
    @property (nonatomic, strong) Test *childTest;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    //
    //  Test.m
    //  RuntimeKVC
    //
    //  Created by z on 2019/5/24.
    //  Copyright © 2019年 com.jzsec. All rights reserved.
    //
    
    #import "Test.h"
    
    @interface Test ()
    {
        /* 类扩展区域定义私有变量,默认j就是@private */
        int age;
    }
    
    /* 类扩展区域定义私有属性 */
    @property (nonatomic, copy) NSString *job;
    
    /* 类扩展区域定义私有变量,默认j就是@private */
    - (void)test;
    
    @end
    
    @implementation Test
    
    -(void)test
    {
        NSLog(@"这是个私有实例方法");
    }
    
    @end
    
    二、Runtime访问

    runtime暴力访问私有属性、私有变量和私有方法

    //获取实例变量列表
            unsigned int count = 0;
            Ivar *members = class_copyIvarList([Test class], &count);
            //打印所有的变量名及其类型 访问私有属性和变量
            for (int i=0; i<count; i++)
            {
                Ivar var = members[i];
                const char *memberName = ivar_getName(var);
                const char *memberType = ivar_getTypeEncoding(var);
                NSLog(@"name:%s type:%s", memberName, memberType);
                NSString *varName = [NSString stringWithUTF8String:memberName];
                if ([varName isEqualToString:@"name"])
                {
                    object_setIvar(test, var, @"老王");
                    NSString *name = object_getIvar(test, var);
                    NSLog(@"name=%@", name);
                }
                else if ([varName isEqualToString:@"major"])
                {
                    object_setIvar(test, var, @"计算机");
                    NSString *major = object_getIvar(test, var);
                    NSLog(@"major=%@", major);
                }
                else if ([varName isEqualToString:@"age"])
                {
                    object_setIvar(test, var, @(30));
                    int age = [object_getIvar(test, var) intValue];
                    NSLog(@"age=%d", age);
                }
                else if ([varName isEqualToString:@"_job"])
                {
                    object_setIvar(test, var, @"工程师");
                    NSString *job = object_getIvar(test, var);
                    NSLog(@"job=%@", job);
                }
            }
    
    /* 暴力访问私有方法 */
            //获取类对象方法列表
            unsigned int countM = 0;
            Method *methods = class_copyMethodList([Test class], &countM);
            for (int i=0; i<countM; i++)
            {
                //获取方法名
                SEL sel = method_getName(methods[i]);
                NSString *selName = NSStringFromSelector(sel);
                NSLog(@"method:%@",selName);
                if (selName && [selName isEqualToString:@"test"])
                {
                    //执行该方法
                    [test performSelector:sel];
                }
            }
    

    打印结果:

    2019-08-08 18:01:44.593507+0800 RuntimeKVC[23807:412395] name:name type:@"NSString"
    2019-08-08 18:01:44.593616+0800 RuntimeKVC[23807:412395] name=老王
    2019-08-08 18:01:44.593635+0800 RuntimeKVC[23807:412395] name:major type:@"NSString"
    2019-08-08 18:01:44.593676+0800 RuntimeKVC[23807:412395] major=计算机
    2019-08-08 18:01:44.593691+0800 RuntimeKVC[23807:412395] name:age type:i
    2019-08-08 18:01:44.593745+0800 RuntimeKVC[23807:412395] age=30
    2019-08-08 18:01:44.593761+0800 RuntimeKVC[23807:412395] name:_childTest type:@"Test"
    2019-08-08 18:01:44.593784+0800 RuntimeKVC[23807:412395] name:_job type:@"NSString"
    2019-08-08 18:01:44.593803+0800 RuntimeKVC[23807:412395] job=工程师
    2019-08-08 18:01:44.593884+0800 RuntimeKVC[23807:412395] method:newProperty
    2019-08-08 18:01:44.593961+0800 RuntimeKVC[23807:412395] method:setNewProperty:
    2019-08-08 18:01:44.593990+0800 RuntimeKVC[23807:412395] method:setChildTest:
    2019-08-08 18:01:44.594044+0800 RuntimeKVC[23807:412395] method:childTest
    2019-08-08 18:01:44.594087+0800 RuntimeKVC[23807:412395] method:test
    2019-08-08 18:01:44.605142+0800 RuntimeKVC[23807:412395] 这是个私有实例方法
    2019-08-08 18:01:44.605186+0800 RuntimeKVC[23807:412395] method:.cxx_destruct
    2019-08-08 18:01:44.605276+0800 RuntimeKVC[23807:412395] method:job
    2019-08-08 18:01:44.605300+0800 RuntimeKVC[23807:412395] method:setJob:
    
    三、KVO访问

    在系统的类目中NSObject(NSKeyValueCoding),通过这两个方法,就可以用key来读取和设置属性的值:

    - (void)setValue:(nullable id)value forKey:(NSString *)key;
    - (nullable id)valueForKey:(NSString *)key;
    //nullable作用:表示可以为空;nonnull作用:不能为空
    

    在通过key查找对应的属性或变量时:

    1. 先查找对象的类中是否存在与key对应的访问器方法;
    2. 查找与key名称相同并且带“_”前缀的成员变量;
    3. 与key名称相同的属性;
    4. 以上都没有,则调用setValue:forUndefinedKey:方法。
    /* KVO暴力访问私有属性和私有变量 */
            [test setValue:@"testName" forKey:@"name"];
            NSString *tname = [test valueForKey:@"name"];
            [test setValue:@"18" forKey:@"age"];
            NSString *tage = [test valueForKey:@"age"];
            [test setValue:@"mathmetics" forKey:@"major"];
            NSString *tmajor = [test valueForKey:@"major"];
            NSLog(@"name:%@ age:%@ major:%@", tname, tage, tmajor);
    
            Test *child = [Test new];
            test.childTest = child;
            child->name = @"晨光";
            NSString *cname = [test valueForKeyPath:@"childTest.name"];
            NSLog(@"test.childTest.name:%@", cname);
            [test setValue:@"傻×" forKeyPath:@"childTest.name"];
            NSString *cname1 = [test valueForKeyPath:@"childTest.name"];
            NSLog(@"test.childTest.name:%@", cname1);
    

    打印结果:

    2019-08-08 18:01:44.605938+0800 RuntimeKVC[23807:412395] name:testName age:18 major:mathmetics
    2019-08-08 18:01:44.606054+0800 RuntimeKVC[23807:412395] test.childTest.name:晨光
    2019-08-08 18:01:44.606084+0800 RuntimeKVC[23807:412395] test.childTest.name:傻×
    

    相关文章

      网友评论

        本文标题:Runtime访问私有变量和方法及KVO访问

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