IOS中Runtime学习笔记
一.什么是Runtime (运行时机制)
- Objective-C语言是一门动态语言,他将很多的静态语言在编译和链接时做的事情放到了运行时来Handle. 这中语言的优点: 会让我们写代码时更具灵活性,在运行的时候我们可以把消息转发给我们想要的对象,或者随意交换一个方法的实现等。
- 这种特性意味着Objective-C不仅需要一个编译器,还需要一个运行时系统来执行编译的代码。对于Objective-C来说,这个运行时系统就像一个操作系统一样:它让所有的工作可以正常的运行。这个运行时系统即Objc Runtime。Objc Runtime其实是一个Runtime库,它基本上是用C和汇编写的,这个库使得C语言有了面向对象的能力。
- 对于C语言,函数的调用在编译的时候会决定调用哪个函数。对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
- 在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。而在编译阶段,C语言调用未实现的函数就会报错。
二. 了解Objective-C中的类和对象:
1. 类--> Class
首先我们看一下苹果的API文档, 我们通过工程中导入#import <objc/objc.h>的头文件, 点击去我们会看到OC中的具体类是怎么定义的,如下面代码:
/// An opaque type that represents an Objective-C class.
译:一个不透明的类型代表一个objective - c类
typedef struct objc_class *Class;
通过上面的部分我们可以看出Objective-C类是由Class类型来表示的,实际上是一个指向objc_class结构体的一个指针. 到这里你肯定想知道objc_class的这个结构体是咋定义的呢? 下面就让我们看看具体它里面是什么东东️
// objc_class结构体
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; 父类
const char *name OBJC2_UNAVAILABLE; 类名
long version OBJC2_UNAVAILABLE; 类的版本信息,默认为0
long info OBJC2_UNAVAILABLE; 类信息,供运行期使用的一些位标识
long instance_size OBJC2_UNAVAILABLE; 该类的实例变量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; 该类的成员变量链表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; 方法定义的链表
struct objc_cache *cache OBJC2_UNAVAILABLE; 方法缓存
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; 协议链表
#endif
} OBJC2_UNAVAILABLE;
结构体的具体字段的解析:
isa: 需要注意的是在Objective-C中,所有的类自身也是一个对象,这个对象的Class里面也有一个isa指针,它指向metaClass(元类),metaclass 存储类的static类成员变量与static类成员方法(+开头的方法);实例对象中的 isa 指向类结构称作 class(普通的),class 结构存储类的普通成员变量与普通成员方法(-开头的方法,这也就是说通过类对象访问静态变量和函数 实例对象访问普通的变量和函数
super_class:指向该类的父类,如果该类已经是最顶层的根类(如NSObject或NSProxy),则super_class为NULL
cache:用于缓存最近使用的方法。一个接收者对象接收到一个消息时,它会根据isa指针去查找能够响应这个消息的对象。在实际使用中,这个对象只有一部分方法是常用的,很多方法其实很少用或者根本用不上。这种情况下,如果每次消息来时,我们都是methodLists中遍历一遍,性能势必很差。这时,cache就派上用场了。在我们每次调用过一个方法后,这个方法就会被缓存到cache列表中,下次调用的时候runtime就会优先去cache中查找,如果cache没有,才去methodLists中查找方法。这样,对于那些经常用到的方法的调用,但提高了调用的效率
info:运行期使用的标志位,比如0x1(CLS_CLASS)表示该类为普通class,0x2(CLS_META)表示该类为 metaclass。
** instance_size:实例大小,即内存所占空间。
** ivars:指向成员变量列表的指针。
methodLists:根据标志位的不同可能指向不同,比如可能指向实例方法列表,或者指向类方法列表。
cache:因为Objective-C的消息转发需要查找dispatch table甚至可能需要遍历继承体系,所以缓存最近使用的方法。
protocols:类需要遵守的协议。
2. 对象--> id:
typedef struct objc_object {
Class isa;
} *id;
可以发现, id可以用来表示任意一个对象,它是一个 objc_object 结构类型的指针,其第一个成员是一个 objc_class 结构类型的指针
我们的根类NSObject也同样是只有一个Class成员:
@interface NSObject <NSObject> {
Class isa;
}
这个isa到底是什么呢?官方介绍是这样的:
Every object is connected to the run-time system through its isa instance variable, inherited from the NSObject class. isa identifies the object's class; it points to a structure that's compiled from the class definition. Through isa, an object can find whatever information it needs at run timesuch as its place in the inheritance hierarchy, the size and structure of its instance variables, and the location of the method implementations it can perform in response to messages.
译: 每个对象连接到运行时系统通过isa实例变量,继承了NSObject类。isa标识对象的类,它指向一个结构,编译后的类定义。通过isa,一个对象可以找到任何信息需要在运行timesuch作为其在继承层次结构,规模和结构的实例变量和方法实现它可以执行的位置响应消息
可见,一个对象(Object)的isa指向了这个对象的类(Class),而这个对象的类(Class)的isa指向了metaclass。这样我们就可以找到静态方法和变量了.
3. 类创建的运行时过程:
① 类的实例对象的 isa 指向它的类;类的 isa 指向该类的 metaclass;
② 类的 super_class 指向其父类,如果该类为根类则值为 NULL;
③ metaclass 的 isa 指向根 metaclass,如果该 metaclass 是根 metaclass 则指向自身;
④ metaclass 的 super_class 指向父 metaclass,如果该 metaclass 是metaclass 则指向该 metaclass 对应的类;
⑤ Object-C 为每个类的定义生成两个 objc_class ,一个普通的 class,另一个即 metaclass。我们可以在运行期创建这两个 objc_class 数据结构,然后使用 objc_addClass将 class 注册到运行时系统中,以此实现动态地创建一个新的类。
三. 项目中具体使用Runtime库里面的函数介绍:
1. 类(Class)相关操作函数:
① ClassName操作的函数:
/**
* Returns the name of a class.
*
* @param cls A class object.
*
* @return The name of the class, or the empty string if \\\\e cls is \\\\c Nil.
获取类的类名,对于class_getName函数,如果传入的cls为Nil,则返回一个字字符串。
*/
const char *class_getName(Class cls)
/**
* Returns the class name of a given object.
*
* @param obj An Objective-C object.
*
* @return The name of the class of which \\\\e obj is an instance.
返回一个给定对象的类名
*/
const char *object_getClassName(id obj)
② super_class(父类) 与 meta_class(元类)主要操作函数
/**
* Returns the superclass of a class.
*
* @param cls A class object.
*
* @return The superclass of the class, or \\\\c Nil if
* \\\\e cls is a root class, or \\\\c Nil if \\\\e cls is \\\\c Nil.
*
* @note You should usually use \\\\c NSObject's \\\\c superclass method instead of this function.
获取类的父类,当cls为Nil或者cls为根类时,返回Nil。不过通常我们可以使用NSObject类的superclass方法来达到同样的目的。
*/
Class class_getSuperclass(Class cls)
/**
* Returns a Boolean value that indicates whether a class object is a metaclass.
*
* @param cls A class object.
*
* @return \\\\c YES if \\\\e cls is a metaclass, \\\\c NO if \\\\e cls is a non-meta class,
* \\\\c NO if \\\\e cls is \\\\c Nil.
判断给定的Class是否是一个元类,如果是cls是元类,则返回YES;如果否或者传入的cls为Nil,则返回NO
*/
BOOL class_isMetaClass(Class cls)
③ 实例变量大小
/**
* Returns the size of instances of a class.
*
* @param cls A class object.
*
* @return The size in bytes of instances of the class \\\\e cls, or \\\\c 0 if \\\\e cls is \\\\c Nil.
*/
OBJC_EXPORT size_t class_getInstanceSize(Class cls)
④ Ivars(成员变量)
// 在objc_class中,Ivars存放着所有成员变量.属性的信息,ivars是一个数组,数组中每个元素是指向Ivar(变量信息)的指针
/**
* Returns the \\\\c Ivar for a specified instance variable of a given class.
*
* @param cls The class whose instance variable you wish to obtain.
* @param name The name of the instance variable definition to obtain.
*
* @return A pointer to an \\\\c Ivar data structure containing information about
* the instance variable specified by \\\\e name.
获取class中指定名称实例成员变量的信息
*/
Ivar class_getInstanceVariable(Class cls, const char *name)
/**
* Returns the Ivar for a specified class variable of a given class.
*
* @param cls The class definition whose class variable you wish to obtain.
* @param name The name of the class variable definition to obtain.
*
* @return A pointer to an \\\\c Ivar data structure containing information about the class variable specified by \\\\e name.
获取类成员变量的信息
*/
OBJC_EXPORT Ivar class_getClassVariable(Class cls, const char *name)
/**
* Adds a new instance variable to a class.
*
* @return YES if the instance variable was added successfully, otherwise NO
* (for example, the class already contains an instance variable with that name).
*
* @note This function may only be called after objc_allocateClassPair and before objc_registerClassPair.
* Adding an instance variable to an existing class is not supported.
* @note The class must not be a metaclass. Adding an instance variable to a metaclass is not supported.
* @note The instance variable's minimum alignment in bytes is 1<<align. The minimum alignment of an instance
* variable depends on the ivar's type and the machine architecture.
* For variables of any pointer type, pass log2(sizeof(pointer_type)).
添加成员变量
*/
OBJC_EXPORT BOOL class_addIvar(Class cls, const char *name, size_t size,
uint8_t alignment, const char *types)
/**
* Describes the instance variables declared by a class.
*
* @param cls The class to inspect.
* @param outCount On return, contains the length of the returned array.
* If outCount is NULL, the length is not returned.
*
* @return An array of pointers of type Ivar describing the instance variables declared by the class.
* Any instance variables declared by superclasses are not included. The array contains *outCount
* pointers followed by a NULL terminator. You must free the array with free().
*
* If the class declares no instance variables, or cls is Nil, NULL is returned and *outCount is 0.
获取整个成员变量列表, 返回是一个数组,outCount指针返回数组的大小
*/
OBJC_EXPORT Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
⑤ Property(属性)操作函数
/**
* Returns a property with a given name of a given class.
*
* @param cls The class you want to inspect.
* @param name The name of the property you want to inspect.
*
* @return A pointer of type \\\\c objc_property_t describing the property, or
* \\\\c NULL if the class does not declare a property with that name,
* or \\\\c NULL if \\\\e cls is \\\\c Nil.
获取指定属性
*/
objc_property_t class_getProperty(Class cls, const char *name)
/**
* Describes the properties declared by a class.
*
* @param cls The class you want to inspect.
* @param outCount On return, contains the length of the returned array.
* If \\\\e outCount is \\\\c NULL, the length is not returned.
*
* @return An array of pointers of type \\\\c objc_property_t describing the properties
* declared by the class. Any properties declared by superclasses are not included.
* The array contains \\\\c *outCount pointers followed by a \\\\c NULL terminator. You must free the array with \\\\c free().
*
* If \\\\e cls declares no properties, or \\\\e cls is \\\\c Nil, returns \\\\c NULL and \\\\c *outCount is \\\\c 0.
获取属性列表,返回一个数组,outCount指针返回数组的大小
*/
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
/**
* Adds a property to a class.
*
* @param cls The class to modify.
* @param name The name of the property.
* @param attributes An array of property attributes.
* @param attributeCount The number of attributes in \\\\e attributes.
*
* @return \\\\c YES if the property was added successfully, otherwise \\\\c NO
* (for example, the class already has that property).
为类添加属性
*/
OBJC_EXPORT BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
/**
* Replace a property of a class.
*
* @param cls The class to modify.
* @param name The name of the property.
* @param attributes An array of property attributes.
* @param attributeCount The number of attributes in \\\\e attributes.
替换类中的属性
*/
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
⑥ Method(方法)操作
/**
* Adds a new method to a class with a given name and implementation.
*
* @param cls The class to which to add a method.
* @param name A selector that specifies the name of the method being added.
* @param imp A function which is the implementation of the new method. The function must take at least two arguments—self and _cmd.
* @param types An array of characters that describe the types of the arguments to the method.
*
* @return YES if the method was added successfully, otherwise NO
* (for example, the class already contains a method implementation with that name).
*
* @note class_addMethod will add an override of a superclass's implementation,
* but will not replace an existing implementation in this class.
* To change an existing implementation, use method_setImplementation.
添加方法
*/
BOOL class_addMethod(Class cls, SEL name, IMP imp,
const char *types)
/*
* 获取实例方法
*/
Method class_getInstanceMethod ( Class cls, SEL name );
/*
* 获取类方法
*/
Method class_getClassMethod ( Class cls, SEL name );
/*
* 获取所有方法的数组
*/
Method * class_copyMethodList ( Class cls, unsigned int *outCount );
/*
* 替代方法的实现
*/
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );
/*
* 返回方法的具体实现
*/
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );
/*
* 类实例是否响应指定的selector
*/
BOOL class_respondsToSelector ( Class cls, SEL sel );
⑦ Version(版本)
// 获取版本号
int class_getVersion ( Class cls );
// 设置版本号
void class_setVersion ( Class cls, int version );
实例将之后更新......