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

一、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:傻×
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,084评论 1 32
  • runtime 和 runloop 作为一个程序员进阶是必须的,也是非常重要的, 在面试过程中是经常会被问到的, ...
    made_China阅读 1,200评论 0 7
  • 本文详细整理了 Cocoa 的 Runtime 系统的知识,它使得 Objective-C 如虎添翼,具备了灵活的...
    lylaut阅读 792评论 0 4
  • 昨天晚上接到通知说今早要去练舞,由于目的地离家里不是特别远,早上提前半个小时起床,小跑到了会场开始排舞,就当做早晨...
    annie小树洞阅读 133评论 0 1
  • 有一天,手指们在那里吵吵闹闹。只是大拇指挑起的战斗,本来他们在 为自己的小主人干活,但大拇指一直说我很粗鲁...
    33小溪流王铭锐阅读 201评论 0 0