前言
Runtime 是OC作为动态语言的一大特性,在开发过程中围绕着这个特性我们可以做很多"黑魔法"般的事情,但是这是一个双刃剑,如果运用的好的话,确实会对程序有很大的帮助,但是如果滥用就会导致很多意想不到的情况。特别是在与三方库配合使用的时候,稍不留神就会引发灾难性的后果。今天来看下 Runtime 的实现逻辑。
Runtime 简述
在Runtime执行过程中,最重要的一个概念就是 isa
。关于 isa 这个东西本篇不展开讲,简单理解为描述对象的指针就可以了。利用 Runtime 最大的作用就是在程序运行过程中动态替换对象的属性,方法等。知名的开源库 MJExtension
就是是运用的Runtime的这些特性才得以实现的。
如果要知道Runtime的实现原理,就需要先简单了解一下 OC 中 Class的结构
Class 结构
Class
的大概结构图就如下图所示:
其中methods
中的原属都为method_t
对象,method_t
是对方法/函数的封装,结构如下:
参数解释:
- IMP 代表函数的具体实现
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);
- SEL 代表方法/函数名,一般叫做选择器,底层结构和char *类似
- 可以通过@selector()和sel_registerName()获得
- 可以通过sel_getName()和NSStringFromSelector()转成字符串
- 不同类中相同名字的方法所对应的方法选择器是相同的
typedef struct objc_selector *SEL;
- types 包含了函数的返回值,参数编码和字符串
Code | Meaning |
---|---|
c | A char |
i | An int |
s | A short |
l | A long l is treated as a 32-bit quantity on 64-bit programs |
q | A long long |
C | An unsigned char |
I | An unsigned int |
S | An unsigned short |
L | An unsigned long |
Q | An unsigned long long |
f | float |
d | double |
B | A C++ bool or a C99 _Boll |
v | A void |
* | A character string(char *) |
@ | An object (whether statically typed or typed id) |
# | A class object(Class) |
: | A method selector(SEL) |
[array type] | An array |
{name=type...} | A structure |
(name=type...) | A union |
bnum | A bit field of num bits |
^type | A pointer to type |
? | An unknown type(among other things, this code is used for function pointers) |
方法缓存
- 方法调用之后,如果没有缓存过,会缓存到缓存列表中去,父类的方法也会缓存到当前类的缓存列表中
- 缓存列表容量不够的时候,会进行扩容,扩容方式为当前容量X2
- 缓存列表扩容的时候,会清空当前已缓存的信息
- 缓存列表结构为散列表
实际运用
- json-model 转换 (获取类的属性)
- method-swizzle (class_addMethod,class_replaceMethod,method_exchangeImplementations 等方法)
- 分类添加属性 (主要使用关联对象的方法)
Runtime常用API
-
动态创建一个类(参数:父类,类名,额外的内存空间)
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes) -
注册一个类(要在类注册之前添加成员变量)
void objc_registerClassPair(Class cls) -
销毁一个类
void objc_disposeClassPair(Class cls) -
获取isa指向的Class
Class object_getClass(id obj) -
设置isa指向的Class
Class object_setClass(id obj, Class cls) -
判断一个OC对象是否为Class
BOOL object_isClass(id obj) -
判断一个Class是否为元类
BOOL class_isMetaClass(Class cls) -
获取父类
Class class_getSuperclass(Class cls) -
获取一个实例变量信息
Ivar class_getInstanceVariable(Class cls, const char *name) -
拷贝实例变量列表(最后需要调用free释放)
Ivar *class_copyIvarList(Class cls, unsigned int *outCount) -
设置和获取成员变量的值
void object_setIvar(id obj, Ivar ivar, id value)
id object_getIvar(id obj, Ivar ivar) -
动态添加成员变量(已经注册的类是不能动态添加成员变量的)
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types) -
获取成员变量的相关信息
const char *ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v) -
获取一个属性
objc_property_t class_getProperty(Class cls, const char *name) -
拷贝属性列表(最后需要调用free释放)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount) -
动态添加属性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount) -
动态替换属性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount) -
获取属性的一些信息
const char *property_getName(objc_property_t property)
const char *property_getAttributes(objc_property_t property) -
获得一个实例方法、类方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name) -
方法实现相关操作
IMP class_getMethodImplementation(Class cls, SEL name)
IMP method_setImplementation(Method m, IMP imp)
void method_exchangeImplementations(Method m1, Method m2) -
拷贝方法列表(最后需要调用free释放)
Method *class_copyMethodList(Class cls, unsigned int *outCount) -
动态添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) -
动态替换方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types) -
获取方法的相关信息(带有copy的需要调用free去释放)
SEL method_getName(Method m)
IMP method_getImplementation(Method m)
const char *method_getTypeEncoding(Method m)
unsigned int method_getNumberOfArguments(Method m)
char *method_copyReturnType(Method m)
char *method_copyArgumentType(Method m, unsigned int index) -
选择器相关
const char *sel_getName(SEL sel)
SEL sel_registerName(const char *str) -
用block作为方法实现
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)
最后
以上就是本篇的内容,势必会有一些错误和遗漏,欢迎斧正~
网友评论