//

//  HKPerson.h

//  runtimeDemo1

//

//  Created by 123 on 16/5/23.

//  Copyright © 2016年 123. All rights reserved.

//

#import <Foundation/Foundation.h>

@interface HKPerson : NSObject

@property (nonatomic,strong) NSString * name;

@property (nonatomic,strong) NSString * age;

@property (nonatomic,strong) NSString * sex;

@end

//

//  HKPerson.m

//  runtimeDemo1

//

//  Created by 123 on 16/5/23.

//  Copyright © 2016年 123. All rights reserved.

//

#import "HKPerson.h"

#import <objc/runtime.h>

#import <objc/message.h>

@implementation HKPerson

void static printM(){

}

+(void)printMessage{

NSLog(@"print MEssage");

}

-(void)printProperty{

unsigned  int count = 0;

Ivar * var= class_copyIvarList(self.class, &count);

for (int i = 0; i < count; i++) {

const char * name=ivar_getName(var[i]);

NSLog(@"%s\n",name);

}

free(var);

}

@end

//

//  ViewController.m

//  runtimeDemo1

//

//  Created by 123 on 16/5/23.

//  Copyright © 2016年 123. All rights reserved.

//

#import "ViewController.h"

#import "HKPerson.h"

#import <objc/runtime.h>

#import <objc/message.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {

[super viewDidLoad];

HKPerson * p1 = [HKPerson new];

unsigned int count = 0;

// 只能获得对象的方法   不能获得类方法以及 静态的方法

Method * method = class_copyMethodList(p1.class, &count);

[p1 performSelector:@selector(printProperty)];

// 获取一个对象的所有方法  重复崩溃

//  NSLog(@"%@",[NSString stringWithUTF8String:name]);

// Do any additional setup after loading the view, typically from a nib.

}

- (void)didReceiveMemoryWarning {

[super didReceiveMemoryWarning];

// Dispose of any resources that can be recreated.

}

2.函数的定义

对对象进行操作的方法一般以object_开头

对类进行操作的方法一般以class_开头

对类或对象的方法进行操作的方法一般以method_开头

对成员变量进行操作的方法一般以ivar_开头

对属性进行操作的方法一般以property_开头

对协议进行操作的方法一般以protocol_开头

根据以上的函数的前缀 可以大致了解到层级关系。对于以objc_开头的方法,则是runtime最终的管家,可以获取内存中类的加载信息,类的列表,关联对象和关联属性等操作。

例如:使用runtime对当前的应用中加载的类进行打印,别被吓一跳。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

unsigned int count = 0;

Class *classes = objc_copyClassList(&count);

for (int i = 0; i < count; i++) {

const char *cname = class_getName(classes[i]);

printf("%s\n", cname);

}

}

@end

3_1.获取属性\成员变量列表

回到顶部

对于获取成员变量的列表可以使用class_copyIvarList函数,如果想要获取属性列表可以使用class_copyPropertyList函数,使用的示例如下:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

Class classPerson = NSClassFromString(@"Person"); // 与下面一句效果一样,可以不用导入头文件

//    Class clazz = Person.class;

unsigned int count = 0;

Ivar *ivarList = class_copyIvarList(classPerson, &count); // 获取成员变量数组

for (int i = 0; i < count; i++) {

const char *cname = ivar_getName(ivarList[i]); // 获取成员变量的名字

NSString *name = [NSString stringWithUTF8String:cname];

NSLog(@"%@", name);

}

NSLog(@"-------------------分割线------------------");

objc_property_t *propertyList = class_copyPropertyList(classPerson, &count); // 获取属性数组

for (int i = 0; i < count; i++) {

const char *cname = property_getName(propertyList[i]);

NSString *name = [NSString stringWithUTF8String:cname];

NSLog(@"%@", name);

}

}

以上代码的输出为:

2015-06-05 22:28:16.194 runtime终极[4192:195757] _height

2015-06-05 22:28:16.195 runtime终极[4192:195757] _age

2015-06-05 22:28:16.195 runtime终极[4192:195757] _name

2015-06-05 22:28:16.195 runtime终极[4192:195757] -------------------分割线------------------

2015-06-05 22:28:16.195 runtime终极[4192:195757] name

2015-06-05 22:28:16.195 runtime终极[4192:195757] age

为什么会有上面的输出结果,因为@property会做三份工作:
1.生成一个带下划线的成员变量
2.生成这个成员变量的get方法
3.生成这个成员变量的set方法

因此会输出三个成员变量_height、_age和_name。需要注意的是属性名是不带下划线的,和定义时的名字一样。因此可以说:ivarList可以获取到@property关键字定义的属性 ,而propertyList不可以获取到成员变量。也就是:使用ivarList是可以将所有的成员变量和属性都获取的。

当属性是readonly的而且重写了getter时,这种情况还是会遇见的,比如一个属性是计算型属性,需要依赖其他属性的值计算而来。此时生成的带下划线的成员变量就不在了, 通过ivarList不能获取该属性了。因此当有这种值的时候,无论使用ivarList还是使用propertyList都无法获取全部的属性或变量。

在进行下一个话题之前:先需要弄清楚另一个问题:对于一个readonly的属性,到底是didSet+set好,还是重写getter好?

大部分的readonly的属性是计算型的,依旧是依赖于其他属性,因此可以使用didSet+set,也就是在其他属性的set方法内,将本属性set。 但是didSet+set有时候完全没有必要,不符合懒加载的规则,浪费了计算能力,用重写getter的方法好一些。 因此重写getter总是会好一点。

回归正题:在KVC时,想要获取全部的成员变量和属性, 怎么办呢?

首先要了解setValue: forKeyPath:方法的底层实现:以name属性为例

1.首先先去类的方法列表去寻找有木有setName:,如果有,就直接调用[person setName:value]

2.找找有没有带下划线的成员变量_name,如果有 _name = value;

3.找有没有成员变量name,如果有 name = value;

4.如果都没有找到,就直接报错。

因此对于readonly的又重写了getter的属性而言:如果对propertyList的属性一次使用kvc,就会报错,因此为保证代码正常,不能使用propertyList的属性进行kvc;

另外:这种属性本来就是计算型的了,为什么还有为它赋值呢,因此对它进行kvc也不合情理。

当使用ivaList时,直接就无法获取到这种属性,因此是kvc的最佳方案。再者,使用propertyList无法获取成员变量(_height),无法对成员变量进行赋值。而使用ivaList是可以将该赋值的成员变量都获取的。

以上就是使用ivar还是使用property进行kvc的论证。

话题外: 很多类 有些成员变量 既没有暴露给外部调用的getter又没有setter,只是用@private声明了一下:为什么??
猜测是:是方法调用时使用的中间变量,因为是跟随对象产生,不适合使用静态static,又因为外部不会使用,所以没必要给外部提供接口,但是可能有好几个方法都需要这个量,不适合做局部变量,所以就这样定义了。

对于这种情况,要想不对这种成员变量赋值,在KVC时又可以这样改进一下,通过ivarList获取,去掉propertyList中没有的成员变量,这样就过滤掉了上面的那种成员变量了。

runtime 运行机制的更多相关文章

  1. IOS runtime运行机制详解(一)

    OC运行机制是指,可以运行的时候动态调用函数.因为C语言必须在编译的时候就决定调用哪个函数. 我们平时写的OC代码,它在运行的时候也是转换成了runtime的方式运行的.任何方法调用本质:就是发送一个 ...

  2. OC的runtime运行机制

    什么是runtime runtime就是一套底层的c语言API(Application Programming Interface)里面包括很多强大实用的c语言类型.c语言函数. 实际上,平时我们编写 ...

  3. oc - runtime运行机制

      Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时做的事放到了运行时来处理.同时OC也是一门简单的语言,很大一部分是C的内容,只是在语言层面上加了关键字和语法,真正让OC强大 ...

  4. runtime 运行机制2

    Mike_zh QQ:82643885 end: blogTitle 博客的标题和副标题 博客园 首页 新随笔 联系 订阅 <a id="MyLinks1_XMLLink" ...

  5. runtime运行机制方法学习

    runtime这玩意第一次听说时都不知道是什么,经过了解后才知道它就是oc动态语言的机制,没有它那oc就不能称为动态语言.在之前可能大家对runtime了解都不深,随着编程技能的日益加深和需要,大家开 ...

  6. runtime 运行时机制 完全解读

    runtime 运行时机制 完全解读   目录[-] import import 我们前面已经讲过一篇runtime 原理,现在这篇文章主要介绍的是runtime是什么以及怎么用!希望对读者有所帮助! ...

  7. iOS开发——高级特性&Runtime运行时特性详解

    Runtime运行时特性详解 本文详细整理了 Cocoa 的 Runtime 系统的知识,它使得 Objective-C 如虎添翼,具备了灵活的动态特性,使这门古老的语言焕发生机.主要内容如下: 引言 ...

  8. Objective-O Runtime 运行时初体验

    Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理.这种动态语言的优势在于:我们写代码时更具灵活性,如我们可以把消息转发给我们想要的对象,或者随意交换一 ...

  9. iOS开发——高级技术OC篇&运行时(Runtime)机制

    运行时(Runtime)机制 本文将会以笔者个人的小小研究为例总结一下关于iOS开发中运行时的使用和常用方法的介绍,关于跟多运行时相关技术请查看笔者之前写的运行时高级用法及相关语法或者查看响应官方文档 ...

随机推荐

  1. 关于JDK 安装,以及Java环境的设置

    关于JDK 安装,以及Java环境的设置 1.下载JDK1.6,选择对应的安装路径 2.配置相应的Java 环境变量 A.属性名称:JAVA_HOME 属性值:C:\Program Files\Jav ...

  2. [jv-convert] Error 1,[all-recursive] Error 1

    g++4.6编译是报错: make profiledbootstrap /var/tmp/gcc4/gcc-4.0.1/sparc-sun-solaris2.8/gcc/gcj -B/var/tmp/ ...

  3. iOS图片加载到内存中占用内存情况

    我的测试结果: 图片占用内存   图片尺寸           .png文件大小 1MB              512*512          316KB 4MB              10 ...

  4. SQL、Linq、lamda表达式 同一功能不同写法

    一.SQL.Linq.lamda表达式 同一功能不同写法 SQL LINQ Lambda SELECT * FROM HumanResources.Employee from e in Employe ...

  5. 在c#中把字符串转为变量名并获取变量值的小例子(转)

    public class Program { public string str = "spp"; public string spp = "Hello World!&q ...

  6. google垂直搜索结果

    佰年金融 所谓“垂直”很干净地分离出来,而不是在谷歌算作有机列表的结果,但该行已开始模糊.例如,现在许多视频结果似乎是直接整合为有机(万SERP的例子).我治疗的新的“有深度的文章”作为一个垂直的结果 ...

  7. printAB()

    #include <iostream> void printA() { std::cout << "A" << std::endl; } voi ...

  8. liToSpan

    <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...

  9. Angularjs学习笔记(一)

    大部分传统的模板系统,对模板的渲染是个线性单向的过程:模板或变量与模板混合在一起产生结果的标记集合.任何对模型的改变都需要通过模板的重新计算.但AngularJS有所不同,任何用户引发的视图的改变,都 ...

  10. VS工程里的文件都是啥?如何打包? 2015-03-04

    打完补充:以下内容全部是我一家之言,只是愿意分享,内容如有不妥还请见谅. ====================================================== 刚才接收了一份代 ...