Fork me on GitHub

runtime能做些什么

前言

面试的时候面试官问:讲讲对runtime的理解,runtime能做些什么? 我基本上就是回答动态添加属性、添加方法、方法的替换……等网络罗列的一大堆。但是具体怎么做,这个嘛,我也说不出来。基于此,我特地研究了一下runtime.h这个文件,过滤了一下里面所有的方法并且了解基本功能并记录在此。

准备

进入到runtime.h文件里面,发现里面有些方法标记的是OBJC_ARC_UNAVAILABLE,所以我们需要单独创建一个MRC项目,以便于尝试所有方法。

创建项目

这里我们创建Command Line Tool工程即可,如图一:
图一

MRC

项目创建完成后,默认是ARC模式的,这里我们修改为MRC。在TATGET-> Build Settings->Objective-C Automatic Reference Counting->NO,默认为YES表示使用ARC模式,这里我们修改为NO即可。

创建我们的测试类,创建Animal类,添加一个name属性和一个speak方法,一起看下类的构造:
图二
图三

方法介绍

准备好了之后,来到main.m文件,导入objc/runtime.hAnimal.h头文件文件。

实例方法

object_copy

原型:

1
2
3
OBJC_EXPORT id _Nullable object_copy(id _Nullable obj, size_t size)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
OBJC_ARC_UNAVAILABLE;

描述:拷贝对象用的,和copy方法一样,返回一个新的对象。
参数:obj被拷贝的对象,size对象的占用内存大小,从源码可知,size最小为16。
注意:这个方法是OBJC_ARC_UNAVAILABLE,在ARC下不可用,而且如果参数objnil 或者是TaggedPointer,则直接返回obj,并不进行拷贝。
图片四

object_dispose

原型:

1
2
3
OBJC_EXPORT id _Nullable object_dispose(id _Nullable obj)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
OBJC_ARC_UNAVAILABLE;

描述:销毁对象,并且释放空间。
参数:obj要释放的对象。
注意:在demo中,调用此方法后,对象还是能正常使用,没有查找到原因,改天研究下单列一篇文章。
源码:

1
2
3
4
5
6
7
id object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}

从源码中可以看到,首先判断obj是否为空,否则调用objc_destructInstance销毁对象,然后调用free()释放空间。
图五

object_getClass

原型:

1
2
3
OBJC_EXPORT Class _Nullable
object_getClass(id _Nullable obj)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述:获取对象的Class,和对象直接调用class方法基本一致,内部实现是返回objisa指针,对于isa指针这里不赘述。
注意:如果objnil,则返回值为Nil
图六

object_setClass

原型:

1
2
3
OBJC_EXPORT Class _Nullable
object_setClass(id _Nullable obj, Class _Nonnull cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述: 修改一个对象的类,目的是修改一个对象的isa指针,返回值是旧的class
注意:调用此方法之后,对象的类型就会改变,所以在开发中应该谨慎使用此方法。
由下图中可以看到,调用object_setClass方法后,a1由原来的Animal 变成NSObject对象,然后在向a发送消息立即崩溃。崩溃信息提示NSObject不存在setName方法。
图七

object_isClass

原型:

1
2
3
OBJC_EXPORT BOOL
object_isClass(id _Nullable obj)
OBJC_AVAILABLE(10.10, 8.0, 9.0, 1.0, 2.0);

描述:判断一个对象是否是类对象,注意:类也是一个对象,具体为什么,这里不会赘述。
由下图可以看出,a1不是一个类,所以他不是类对象,Animal是一个类,所以返回TRUE
图八

object_getIvar

原型:

1
2
3
OBJC_EXPORT id _Nullable
object_getIvar(id _Nullable obj, Ivar _Nonnull ivar)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述: 获取objivar的值。和这个方法功能类似的还有一个方法object_setInstanceVariable,但是这个获取的结果速度回更快一些,从object_setInstanceVariable源码中可以看出来,最终也是调用的这个方法。
为了测试此方法,我们为Animal类添加一个变量age
由下图也可以看出属性和变量的区别。
图九

object_getInstanceVariable

原型:

1
2
3
4
5
OBJC_EXPORT Ivar _Nullable
object_getInstanceVariable(id _Nullable obj, const char * _Nonnull name,
void * _Nullable * _Nullable outValue)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
OBJC_ARC_UNAVAILABLE;

描述:这个方法和上个方法类似,也是获取属性或者变量的值,只不过参数和返回值不同。
参数:obj被获取的对象;name变量的名称,字符串格式;outValue这个变量的值。
通过源码可以看出这个方法的实现过程就是通过name获取Ivar,然后调用上述方法。所以官方注释上说此方法慢与上述方法。这里就不上图了,图和👆的一样。

object_setIvar
object_setIvarWithStrongDefault

原型:

1
2
3
OBJC_EXPORT void
object_setIvar(id _Nullable obj, Ivar _Nonnull ivar, id _Nullable value)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
1
2
3
4
OBJC_EXPORT void
object_setIvarWithStrongDefault(id _Nullable obj, Ivar _Nonnull ivar,
id _Nullable value)
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);

描述:因为这两个方法基本相同,所以这里放在一块说了,这两个方法是给变量赋值,相当于a1.name=我是小狗
这两个方法的区别在于第一个会给属性用Unretained修饰,第二个会用Strong修饰,注意这个前提是这个属性没有被修饰的情况下才会起作用。比如name已经被copy修饰了,那就保持原来的样子,那么这两个方法是没有区别的。
参数:obj被赋值的对象;Ivar即将赋值的变量,value被赋的值。
注意:如果对象没有这个属性,是不能赋值的。
图十

object_setInstanceVariable
object_setInstanceVariableWithStrongDefault

原型:

1
2
3
4
5
OBJC_EXPORT Ivar _Nullable
object_setInstanceVariable(id _Nullable obj, const char * _Nonnull name,
void * _Nullable value)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
OBJC_ARC_UNAVAILABLE;
1
2
3
4
5
6
OBJC_EXPORT Ivar _Nullable
object_setInstanceVariableWithStrongDefault(id _Nullable obj,
const char * _Nonnull name,
void * _Nullable value)
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0)
OBJC_ARC_UNAVAILABLE;

描述:这两个方法和上面两个方法基本相同,只是参数和返回值不同,从源码可以看出内部调用的也是上述两个方法而已。直接演示使用方法:
图十一

获得类定义

objc_getClass
objc_lookUpClass

原型:

1
2
3
OBJC_EXPORT Class _Nullable
objc_getClass(const char * _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
1
2
3
OBJC_EXPORT Class _Nullable
objc_lookUpClass(const char * _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

描述:上面两个方法都是通过字符串获取一个Class,并且整个类是在工程里面存在的,否则会返回nil
注释上说objc_getClass方法会在第一次没有找到Class的情况下会再次查询一变并且执行回调。
从源码上面看着两个方法都是执行了look_up_class(const char *aClassName, bool includeUnconnected, bool includeClassHandler)这个方法。
区别在于includeClassHandler这个参数。objc_getClass传的是YES,而后者是NO
look_up_class实现上看,旧版本的实现是按照注释上所说的一样。但是新版本的这个参数被标记为unused,基本上现在两个方法相同。
图十二

objc_getRequiredClass

原型:

1
2
3
OBJC_EXPORT Class _Nonnull
objc_getRequiredClass(const char * _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

描述:这个方法和上面的objc_getClass方法基本一致,内部调用的就是objc_getClass这个方法,只不过在没有找到方法的情况下,会报链接失败。贴下源码:

1
2
3
4
5
6
Class objc_getRequiredClass(const char *aClassName)
{
Class cls = objc_getClass(aClassName);
if (!cls) _objc_fatal("link error: class '%s' not found.", aClassName);
return cls;
}

图十三

objc_getMetaClass

原型:

1
2
3
OBJC_EXPORT Class _Nonnull
objc_getMetaClass(const char * _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

描述:获取类的元类,这个方法和objc_getRequiredClass基本类似,也是调用了objc_getClass这个方法,只不过在没有找到Class的时候会报提示信息,不会Crash,然后会返回Nil,最终返回Classisa指针。从图中看到控制台里面打印信息。
贴下源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Class objc_getMetaClass(const char *aClassName)
{
Class cls;

if (!aClassName) return Nil;

cls = objc_getClass (aClassName);
if (!cls)
{
_objc_inform ("class `%s' not linked into application", aClassName);
return Nil;
}

return cls->ISA();
}

图十四

objc_getClassList

原型:

1
2
3
OBJC_EXPORT int	
objc_getClassList(Class _Nonnull * _Nullable buffer, int bufferCount)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

描述:获取指定的个数类列表。buffer需要传入一个Class数组,bufferCount需要传入想要的个数,返回值是类的总数。如果bufferCount传入的数比类的总数大,则最多能返回所有的类。如果bufferCount比类的总数小,则会返回这个个数的类,从下图中可以看到已经有13062个类了。
注意:获取的类有可能不是NSObject的子类,所以要小心调用这些类的方法.
图中实例获取10个类:
图十五

objc_copyClassList

原型:

1
2
3
OBJC_EXPORT Class _Nonnull * _Nullable
objc_copyClassList(unsigned int * _Nullable outCount)
OBJC_AVAILABLE(10.7, 3.1, 9.0, 1.0, 2.0);

描述:这个方法和上面的方法类似,只不过这个是一次性获取所有的类,outCount向外输出类的总个数。
注意:这个方法返回值是Class指针,所以在使用完成后需要调用free()进行释放。
图十六

类方法

class_getName

原型:

1
2
3
OBJC_EXPORT const char * _Nonnull
class_getName(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述: 获取Class的名字,返回值类型是const char *。这个在简单不过,上图已经演示了。不在赘述。

class_isMetaClass

原型:

1
2
3
OBJC_EXPORT BOOL
class_isMetaClass(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述:判断一个Class是否是元类,返回值是bool类型。
图十七

class_getSuperclass

原型:

1
2
3
OBJC_EXPORT Class _Nullable
class_getSuperclass(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述:获取Class的父类。 和OCsuperclass功能一样。
从下图可以看出Animal的父类是NSObject
图十八

class_setSuperclass

原型:

1
2
3
4
5
6
7
OBJC_EXPORT Class _Nonnull
class_setSuperclass(Class _Nonnull cls, Class _Nonnull newSuper)
__OSX_DEPRECATED(10.5, 10.5, "not recommended")
__IOS_DEPRECATED(2.0, 2.0, "not recommended")
__TVOS_DEPRECATED(9.0, 9.0, "not recommended")
__WATCHOS_DEPRECATED(1.0, 1.0, "not recommended")
__BRIDGEOS_DEPRECATED(2.0, 2.0, "not recommended");

描述:改变某个Class的父类。一定要谨慎使用这个函数。被标记为not recommended。返回值是就的父类。
看下图,当把Animal的父类改成NSString之后,把a1当做字符串操作,运行没有任何问题。但是当调用Animal原来的方法之后,就会报方法找不到。
图十九

class_getVersion
class_setVersion
1
2
3
OBJC_EXPORT int
class_getVersion(Class _Nullable cls)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
1
2
3
OBJC_EXPORT void
class_setVersion(Class _Nullable cls, int version)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

描述:这两个方法比较简单,就放一块说了,其实就是设置和获取Class的版本。 直接上图:
图二十

class_getInstanceSize

原型:

1
2
3
OBJC_EXPORT size_t
class_getInstanceSize(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述:获取这个类所占用字节数。不详细讲了,网上都是讲解。

class_getInstanceVariable

原型:

1
2
3
OBJC_EXPORT Ivar _Nullable
class_getInstanceVariable(Class _Nullable cls, const char * _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

描述:通过一个Class 获取指定属性。 这个和object_getInstanceVariable基本一致,返回的是同一个Ivar对象。只不过object_getInstanceVariable 会向外输出值。
图二十一

class_getClassVariable

原型:

1
2
3
OBJC_EXPORT Ivar _Nullable
class_getClassVariable(Class _Nullable cls, const char * _Nonnull name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述: 这个方法至少我认为基本上没什么用处。由字面意思看是获取类变量,OC语法也不支持这个。
从源码上来看这个就是获取ISA指针的属性,图上演示的是等效的。
图二十二

class_copyIvarList

原型:

1
2
3
OBJC_EXPORT Ivar _Nonnull * _Nullable
class_copyIvarList(Class _Nullable cls, unsigned int * _Nullable outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述:我估计这个方法我们接触的比较多,因为在利用runtime归档 、解档需要用到这个方法;这个方法主要是返回Class的所有变量,返回值是一个Ivar数组。
注意:在用完Ivar数组之后,必须调用free()函数进行释放,否则会导致内存泄露。
图二十三

class_getInstanceMethod

原型:

1
2
3
OBJC_EXPORT Method _Nullable
class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

描述:通过SEL获取Method,这个方法估计也很熟悉,在方法替换的的时候,就是通过这个来获取实例方法的。
注意:如果方法只在.h声明,但是没有在.m里面实现的话,是找不到此方法的。这个方法会在当前Class没有找到对应的SEL的时候,会一直向父级查找, 如下图都能正确找到方法的地址。
图二十四

class_getClassMethod

原型:

1
2
3
OBJC_EXPORT Method _Nullable
class_getClassMethod(Class _Nullable cls, SEL _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

描述:获取类方法,和class_getInstanceMethod基本差不多。
为了测试此方法,我们在为Animal添加一个run方法,并且在.m里面实现,此方法能正确返回。如图:
图二十五
图二十六
图二十七

class_getMethodImplementation

原型:

1
2
3
OBJC_EXPORT IMP _Nullable
class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述: 获取方法的实现。

class_getMethodImplementation_stret

原型:

1
2
3
4
OBJC_EXPORT IMP _Nullable
class_getMethodImplementation_stret(Class _Nullable cls, SEL _Nonnull name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0)
OBJC_ARM64_UNAVAILABLE;

描述:获取方法的转发消息的实现。
图二十八

class_respondsToSelector

原型:

1
2
3
OBJC_EXPORT BOOL
class_respondsToSelector(Class _Nullable cls, SEL _Nonnull sel)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述: 判断一个类是否实现了某个方法。和[NSObject respondsToSelector:@selector()];方法一样。
图二十九

class_copyMethodList

原型:

1
2
3
OBJC_EXPORT Method _Nonnull * _Nullable
class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述:获取所有的Class实现的实例方法。不包括类方法,也不包括父类方法。
注意:此方法会返回Method数组,在用完之后需要调用free()释放,否则会导致内存泄露。
从下图可以看出,给类添加属性之后,会自动生成get set方法。
图三十

class_conformsToProtocol

原型:

1
2
3
OBJC_EXPORT BOOL
class_conformsToProtocol(Class _Nullable cls, Protocol * _Nullable protocol)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述:判断Class是否遵循某个协议.和[NSObject conformsToProtocol:]方法一样。
图三十一

class_copyProtocolList

原型:

1
2
3
OBJC_EXPORT Protocol * __unsafe_unretained _Nonnull * _Nullable 
class_copyProtocolList(Class _Nullable cls, unsigned int * _Nullable outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述:获取Class所有遵循的协议。返回值是一个OC类数组Protocol **
注意:在使用完后调用free()方法释放内存。
图三十三

class_getProperty

原型:

1
2
3
OBJC_EXPORT objc_property_t _Nullable
class_getProperty(Class _Nullable cls, const char * _Nonnull name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述:通过name获取指定的属性。 仅仅包含@property标记的属性。不包含变量和父级属性,如下图崩溃原因是没用获取到age属性,调用相应的方法直接报错。
图三十四

class_copyPropertyList

原型:

1
2
3
OBJC_EXPORT objc_property_t _Nonnull * _Nullable
class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述:获取所有的属性,仅仅包含@property标记的属性。不包含变量和父级属性。
注意:该方法返回值是一个objc_property_t 数组,并且在使用完后一定要调用free()方法释放内存,否则会导致内存泄露。
图三十二

class_getIvarLayout

原型:

1
2
3
OBJC_EXPORT const uint8_t * _Nullable
class_getIvarLayout(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
class_getWeakIvarLayout

原型:

1
2
3
OBJC_EXPORT const uint8_t * _Nullable
class_getWeakIvarLayout(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
class_addMethod

原型:

1
2
3
4
OBJC_EXPORT BOOL
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
class_replaceMethod

原型:

1
2
3
4
OBJC_EXPORT IMP _Nullable
class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
class_addIvar

原型:

1
2
3
4
OBJC_EXPORT BOOL
class_addIvar(Class _Nullable cls, const char * _Nonnull name, size_t size,
uint8_t alignment, const char * _Nullable types)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
class_addProtocol

原型:

1
2
3
OBJC_EXPORT BOOL
class_addProtocol(Class _Nullable cls, Protocol * _Nonnull protocol)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
class_addProperty

原型:

1
2
3
4
5
OBJC_EXPORT BOOL
class_addProperty(Class _Nullable cls, const char * _Nonnull name,
const objc_property_attribute_t * _Nullable attributes,
unsigned int attributeCount)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);
class_replaceProperty

原型:

1
2
3
4
5
OBJC_EXPORT void
class_replaceProperty(Class _Nullable cls, const char * _Nonnull name,
const objc_property_attribute_t * _Nullable attributes,
unsigned int attributeCount)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);
class_setIvarLayout

原型:

1
2
3
OBJC_EXPORT void
class_setIvarLayout(Class _Nullable cls, const uint8_t * _Nullable layout)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
class_setWeakIvarLayout

原型:

1
2
3
OBJC_EXPORT void
class_setWeakIvarLayout(Class _Nullable cls, const uint8_t * _Nullable layout)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
objc_getFutureClass

原型:

1
2
3
4
OBJC_EXPORT Class _Nonnull
objc_getFutureClass(const char * _Nonnull name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0)
OBJC_ARC_UNAVAILABLE;

实例化类

class_createInstance
objc_constructInstance
objc_destructInstance

添加类

objc_allocateClassPair
objc_registerClassPair
objc_duplicateClass
objc_disposeClassPair

方法

method_getName
method_getImplementation
method_getTypeEncoding
method_getNumberOfArguments
method_copyReturnType
method_copyArgumentType
method_getReturnType
method_getArgumentType
method_getDescription
method_setImplementation
method_exchangeImplementations

实例变量

ivar_getName
ivar_getTypeEncoding
ivar_getOffset

属性

property_getName
property_getAttributes
property_copyAttributeList
property_copyAttributeValue

协议

objc_getProtocol
objc_copyProtocolList
protocol_conformsToProtocol
protocol_isEqual
protocol_getName
protocol_getMethodDescription
protocol_copyMethodDescriptionList
protocol_getProperty
protocol_copyPropertyList
protocol_copyPropertyList2
protocol_copyProtocolList
objc_allocateProtocol
objc_registerProtocol
protocol_addMethodDescription
protocol_addProtocol
protocol_addProperty

objc_copyImageNames
class_getImageName
objc_copyClassNamesForImage

选择器

sel_getName
sel_registerName
sel_isEqual
objc_enumerationMutation
objc_setEnumerationMutationHandler
objc_setForwardHandler
imp_implementationWithBlock
imp_getBlock
imp_removeBlock
objc_loadWeak
objc_storeWeak

添加变量

objc_setAssociatedObject
objc_getAssociatedObject
objc_removeAssociatedObjects
------ 本文结束------
0%