//

//  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. Application、 session、iewstate,以及repeater 的commang用法

      Session:在不同的浏览器之间传值,像银行之类的网站为了安全把用户名密码保存在session里面.每一台电脑访问服务器,都会是独立的一套session,key值都一样,但是内容都是不一样的 以 ...

  2. prop和attr的区别

    在高版本的jquery引入prop方法后,什么时候该用prop?什么时候用attr?它们两个之间有什么区别?这些问题就出现了. 对于HTML元素本身就带有的固有属性,在处理时,使用prop方法. 对于 ...

  3. Python 3.5安装JPype

    使用命令pip install jpype1可安装jpype. 如果出现如下情况: creating build\lib.win-amd64-3.5\jpypex copying jpypex\__i ...

  4. Android应用开发项目结构分析

    初学Android开发,初步理解的Android应用项目结构,备忘. 一.清单文件AndroidManifest.xml 功能: 1.供Android平台调用,供其了解本应用的信息,因此,所有的组件( ...

  5. lnmp编译安装

    lnmp超详细编译安装教程 linux采用的是centos,软件包统一放在/usr/local/src目录下.由于 centos源比较老,而且更新起来也比较慢,此处建议你换成163源.提示:如果你真打 ...

  6. vba 笔记

    1.PlanWS5.Range("D5:E13").Copy   复制PlanWS5.Range("G5:H13").PasteSpecial Paste:=x ...

  7. 用原生js写碰撞变色效果

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. paramiko模块

    安装: # pycrypto,由于 paramiko 模块内部依赖pycrypto,所以先下载安装pycrypto (1) wget http://ftp.dlitz.net/pub/dlitz/cr ...

  9. MFC 对话框响应OnChar()

    最近在写程序时,出现了对话框里面不接收键盘消息的情况,特别搜索了以下内容 MFC中对话框是不会直接响应OnChar和OnKeyDown消息的,会被 其它控件拦截,测试时发现不会进入这两个函数,必须重定 ...

  10. 操作素有的ul的元素

    1 $(function() { var all = $(".test"); $(".test").each(function() { var y = $(th ...