Runtime 介绍

1.简介

Objc是一门动态语言,所以它总是想办法把一些决定工作从编译连接推迟到运行时。也就是说只有编译器是不够的,还需要一个运行时系统 (runtime system) 来执行编译后的代码。这就是 Objective-C Runtime 系统存在的意义,它是整个Objc运行框架的一块基石。

1>OC 是一个全动态语言,OC 的一切都是基于 Runtime 实现的
平时编写的OC代码, 在程序运行过程中, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者
比如:
OC :
[[Person alloc] init]
runtime :
objc_msgSend(objc_msgSend("Person" , "alloc"), "init")

2>runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API
3>runtimeAPI的实现是用C和汇编,是一套苹果开源的框架(http://opensource.apple.com/tarballs/objc4/)

2.头文件

常用的函数定义在message.h和runtime.h这两个头文件中。

message.h中主要包含了一些向对象发送消息的函数,这是OC对象方法调用的底层实现。

runtime.h是运行时最重要的文件,其中包含了对运行时进行操作的方法

2.1操作对象的类型的定义

/// An opaque type that represents a method in a class definition.

///一个类型,代表着类定义中的一个方法

typedef struct objc_method *Method;

/// An opaque type that represents an instance variable.

///代表实例(对象)的变量

typedef struct objc_ivar *Ivar;

/// An opaque type that represents a category.

///代表一个分类

typedef struct objc_category *Category;

/// An opaque type that represents an Objective-C declared property.

///代表OC声明的属性

typedef struct objc_property *objc_property_t;

// Class代表一个类,它在objc.h中

这样定义的 typedef struct objc_class *Class;

2.2函数的定义

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

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

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

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

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

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

3.应用

3.1获取属性\成员标量列表

获取成员变量的列表可以使用class_copyIvarList函数,

获取属性列表可以使用class_copyPropertyList函数;

示例:

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);

        }

        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);

        }

@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中没有的成员变量,这样就过滤掉了上面的那种成员变量了。

3.1.1  应用1:KVC字典转模型

获取属性\成员列表一个重要的应用就是,一次取出模型中的属性\成员变量,根据它的名字获取字典中的key然后取出字典中这个key对应的value,使用setValue: forKeyPath:方法设置值。为什么要这样,而不再使用方法setValuesForKeysWithDictionary:。因为在setValuesForKeysWithDictionary:方法内部会执行这样一个过程
遍历字典里面的所有key,一个一个取出来,遍历每个key按照以下过程
1.取出key,
2.取出key的value,即dict[key],直接给模型的属性\成员变量赋值
3.怎么给模型的属性赋值,使用方法setValue:value forKeyPath:key进行赋值,这个方法的执行过程在前面已经提到。

因此,开发中经常遇到的字典中的key比模型中多时,会出现的 this class is not key-value compliant for ‘xxx’这个bug就很好解释了,通常是因为字典中的key,比模型中的属性\成员变量多。那么当模型中的属性比字典中多时,使用setValuesForKeysWithDictionary:会不不会有bug呢?经测试:当多出来的属性是对象数据类型时,为null,当属性是基本数据类型时,会有一个系统默认值(如int为0)。

3.1.2 应用:NScoding归档和解档

获取属性\成员列表另外一个重要的应用就是进行归档和解档,其原理和上面的kvc基本上一样

3.2  交换方法实现

交换方法实现的需求场景:自己创建了一个功能性的方法,在项目中多次被引用,当项目的需求发生改变时,要使用另一种功能代替这个功能,要求是不改变旧的项目(也就是不改变原来方法的实现)。

可以在类的分类中,再写一个新的方法(是符合新的需求的),然后交换两个方法的实现。这样,在不改变项目的代码,而只是增加了新的代码 的情况下,就完成了项目的改进。

交换两个方法的实现一般写在类的load方法里面,因为load方法会在程序运行前加载一次,而initialize方法会在类或者子类在 第一次使用的时候调用,当有分类的时候会调用多次

3.3 类\对象的关联对象

关联对象不是为类\对象添加属性或者成员变量(因为在设置关联后也无法通过ivarList或者propertyList取得) ,而是为类添加一个相关的对象,通常用于存储类信息,例如存储类的属性列表数组,为将来字典转模型的方便。 例如,将属性的名称存到数组中设置关联

objc_setAssociatedObject方法的参数解释:

第一个参数id object, 当前对象
第二个参数const void *key, 关联的key,是c字符串 
第三个参数id value, 被关联的对象 
第四个参数objc_AssociationPolicy policy关联引用的规则

3.4 动态添加方法,拦截未实现的方法

每个类都有都有一下两个类方法(来自NSObject)
+ (BOOL)resolveClassMethod:(SEL)sel 
+ (BOOL)resolveInstanceMethod:(SEL)sel
以上两个一个使用于类方法,一个适用于对象方法。在代码中调用没有实现的方法时,也就是sel标识的方法没有实现 都会现调用这两个方法中的一个(如果是类方法就调用第一个,如果是对象方法就调用第二个)拦截。 通常的做法是在resolve的内部指定sel对应的IMP,从而完成方法的动态创建和调用两个过程,也可以不指定IMP打印错误信息后直接返回。

每个方法的内部都默认包含两个参数,被称为隐式参数
id类型self(代表类或对象)和SEL类型的_cmd(方法编号)

class_addMethod函数参数的含义:

第一个参数Class cls, 类型
第二个参数SEL name, 被解析的方法
第三个参数 IMP imp, 指定的实现
第四个参数const char *types,方法的类型,具体参照类型的codeType那张图,但是要注意一点:Since the function must take at least two arguments—self and _cmd, the second and third characters must be “@:” (the first character is the return type).译为:因为函数必须至少有两个参数self和_cmd,第二个和第三个字符必须是“@:”。如果想要再增加参数,就可以从实现的第三个参数算起,看下面的例子就明白。
返回值:YES if the method was found and added to the receiver, otherwise NO.

3.5 动态创建一个类

动态创建一个类,为这个类添加成员变量和方法,并创建这个类型的对象

参考文献:http://www.cnblogs.com/Mike-zh/p/4557014.html

时间: 2024-12-15 08:57:21

Runtime 介绍的相关文章

runtime介绍及基本使用

1. 概念 runtime(运行时系统),是一套基于C语言API,包含在 <objc/runtime.h>和<objc/message.h>中,运行时系统的功能是在运行期间(而不是编译期或其他时机)通过代码去动态的操作类(获取类的内部信息和动态操作类的成员),如创建一个新类.为某个类添加一个新的方法或者为某个类添加实例变量.属性,或者交换两个方法的实现.获取类的属性列表.方法列表等和Java中的反射技术类似. 2. 探索 程序最终运行的是二进制的可执行文件,编译器需要将OC代码转换

利用runtime,避免UIButton 重复点击, 可变数组和可变字典为nil,或者数组越界导致的崩溃

Demo链接: https://github.com/ShaoWenLe/Runtimer-Demo.git 参考文章: http://www.jianshu.com/p/080a238c62b9 相关Runtime介绍: http://www.cocoachina.com/ios/20160523/16386.html http://www.cocoachina.com/ios/20160628/16843.html 1 #import <Foundation/Foundation.h> 2

Runtime的初步认识——结构体与类

Runtime的初步认识 Runtime的初步认识 Runtime介绍 类与结构体的关系 结构体解析 结构体的作用 Runtime介绍 学习一个东西至少要先知道它是个啥,你一定听说过"运行时是 Objective-C 的一个特色",这里的"运行时"就是指 runtime 了. runtime是在自 iOS 平台开放并基于 Objective-C 语言开发后的一个编程语言上的高级技术. 学习runtime的目的并不是为了开发,而是让你更好的理解 Objective-C

MJExtension和JSONModel的使用

1.使用目的:实现JSON 与model之间的转换. 我们经常要从服务器上获取JSON数据,将其转化为Model. 这个过程是无疑是痛苦的.对于JSON数据量相对较少,或者Model里面的属性值较少的情况,处理起来不大费劲.但上架的应用大多是数据量巨大,与后台交互频繁的.更糟的是,后台接口频繁变化,那么维护起来就相当费劲了,因为你每次都要根据新的接口文档来逐一解释数据.往往每次要花你半天时间去修改.调试代码. 2.JSONModel JSON -> Dictionary -> Model 以下

RunTime.getRuntime().exec()运行脚本命令介绍和阻塞

java在企业级项目开发中,无论是强制性的功能需要,还是为了简便java的实现,需要调用服务器命令脚本来执行.在java中,RunTime.getRuntime().exec()就实现了这个功能. 用法:         public Process exec(String command)-----在单独的进程中执行指定的字符串命令. public Process exec(String [] cmdArray)---在单独的进程中执行指定命令和变量 public Process exec(S

Object,String,StringBuffer,StringBuilder,System,Runtime,Date,Math介绍及用法(API)

1       Object对象 面向对象的核心思想:“找合适的对象,做适合的事情”. 合适的对象: 自己描述类,自己创建对象. sun已经描述了好多常用的类,可以使用这些类创建对象. API(Application Program Interface) sun定义的那么多类的终极父类是Object.Object描述的是所有类的通用属性与方法. 1.1   toString方法 toString() 返回对象的描述信息   [email protected]   类名@哈希码值的十六进制形式.

Linux Runtime PM介绍

一.Runtime PM引言 1. 背景 (1)display的需求 (2)系统整机动态功耗优化的需求 (3)upstream 2. 解决方案 (1)引入debounce (2)使用统一的workqueue来管理任务 (3)实时地关闭不需要工作的device (4)当device作为parent时,所有的child不工作时,关闭该device (5)引入pm_rutime 3. 性能指标 (1)快速开关屏场景,亮屏速度提升 (2)动态功耗,更为稳定:唤醒而不亮屏的场景,功耗更低 (3)有助于降低

Java中RunTime类介绍

Runtime 类代表着Java程序的运行时环境,每个Java程序都有一个Runtime实例,该类会被自动创建,我们可以通过Runtime.getRuntime() 方法来获取当前程序的Runtime实例. 获取当前Jvm的内存信息 /* * 获取当前jvm的内存信息,返回的值是 字节为单位 * */ public static void getFreeMemory() { //获取可用内存 long value = Runtime.getRuntime().freeMemory(); Syst

Objective-C runtime初识

Objective-C Runtime Describes the macOS Objective-C runtime library support functions and data structures. Overview(概述) 以下是官方文档中对Runtime给出的定义 The Objective-C runtime is a runtime library that provides support for the dynamic properties of the Objecti