说说什么是runtime
OC 是一个全动态语言,OC 的一切都是基于 Runtime 实现的
平时编写的OC代码, 在程序运行过程中, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者,比如:
OC :
[[Person alloc] init]
runtime :
objc_msgSend(objc_msgSend("Person" , "alloc"), "init")
runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API。
runtimeAPI的实现是用 C++ 开发的(源码中的实现文件都是mm),是一套苹果开源的框架。
使用过runtime吗,用它来做什么
上几篇文章的应用部分。
发送消息、交换方法、类\对象的关联对象、动态添加方法、配合KVC字典自动转模型、自动归档解档。
runtime的方法调用流程?
如果用实例对象调用实例方法,会到实例的isa指针指向的对象(也就是类对象)操作。
如果调用的是类方法,就会到类对象的isa指针指向的对象(也就是元类对象)中操作。
--> 1.首先,在相应操作的对象中的缓存方法列表中找调用的方法,如果找到,转向相应实现并执行。
--> 2.如果没找到,在相应操作的对象中的方法列表中找调用的方法,如果找到,转向相应实现并执行。
--> 3.如果没找到,去父类指针所指向的对象中执行1,2。
--> 4.依次类推,如果一直到根类还没找到,转向拦截调用。
--> 5.如果没有重写拦截调用的方法,程序报错。
Category和Extension的区别
1、Extension在编译期决议,它就是类的一部分,在编译期和头文件里的@interface以及实现文件里的@implement一起形成一个完整的类,它伴随类的产生而产生,亦随之一起消亡。Extension一般用来隐藏类的私有信息,你必须有一个类才能为这个类添加Extension,所以你无法为系统的类比如NSString添加Extension。
2、Category则完全不一样,它是在运行期决议的。
3、Extension可以添加属性、成员变量,而Category一般不可以。
总之,就Category和Extension的区别来看,Extension可以添加实例变量,而Category是无法添加实例变量的。因为Category在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这对编译型语言来说是灾难性的。
为什么Category中不能动态添加成员变量?
在runtime函数中,确实有一个class_addIvar()
函数用于给类添加成员变量,但是阅读过苹果的官方文档的人应该会看到:
This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.
大概的意思说,这个函数只能在“构建一个类的过程中”调用。一旦完成类定义,就不能再添加成员变量了。经过编译的类在程序启动后就被runtime加载,没有机会调用addIvar。程序在运行时动态构建的类需要在调用objc_registerClassPair之后才可以被使用,同样没有机会再添加成员变量。
为什么不能为一个类动态的添加成员变量,可以给类动态增加方法和属性?
因为方法和属性并不“属于”类实例,而成员变量“属于”类实例。我们所说的“类实例”概念,指的是一块内存区域,包含了isa指针和所有的成员变量。所以假如允许动态修改类成员变量布局,已经创建出的类实例就不符合类定义了,变成了无效对象。但方法定义是在objc_class中管理的,不管如何增删类方法,都不影响类实例的内存布局,已经创建出的类实例仍然可正常使用。
网友评论