Objective-C运行时是一个运行时库,它支持Objective-C语言的动态属性,因此所有Objective-C应用程序都链接到它。Objective-C运行时库支持函数是在/usr/lib/libobjc.A.dylib中的共享库中实现的。
在Objective-C中编程时,通常不需要直接使用Objective-C运行时库。这个API主要用于开发Objective-C和其他语言之间的桥梁层,或者用于低级调试。
Objective-C运行时库的macOS实现是Mac独有的。对于其他平台,GNU编译器集合提供了一个具有类似API的不同实现。
字符串编码
运行时API中的所有char *都应该被认为具有UTF-8编码。
函数:
1.class_getName
返回类的名称。
宣言
const char * class_getName(Class cls);
参数---cls--一个类对象。
返回值
类的名称,或cls为Nil时的空字符串
2.class_getSuperclass
返回类的超类。
宣言:
Class class_getSuperclass(Class cls);
参数---cls--一个类对象。
返回值
类的超类,如果cls是根类则为Nil,如果cls为Nil则为Nil。
3.---class_isMetaClass
返回一个布尔值,该值指示类对象是否是元类
宣言
BOOL class_isMetaClass(Class cls);
参数---cls--一个类对象。
返回值
如果cls是一个元类,则为YES;如果cls是非元类,则为NO;如果cls为Nil,则为NO。
4.----class_getInstanceSize
宣言
size_t class_getInstanceSize(Class cls);
参数---cls--一个类对象。
返回值
类cls实例的字节大小,如果cls为Nil则为0。
5.-----class_getInstanceVariable
返回给定类的指定实例变量的Ivar。
宣言
Ivar class_getInstanceVariable(Class cls, const char *name);
参数
cls--您希望获得其实例变量的类。
name-- 要获取的实例变量定义的名称。
返回值
一个指向Ivar数据结构的指针,该结构包含关于由名称指定的实例变量的信息。
6.---class_getClassVariable
返回给定类的指定类变量的Ivar。
宣言
Ivar class_getClassVariable(Class cls, const char *name);
参数
cls----您希望获得其类变量的类定义。
name---- 要获取的类变量定义的名称。
返回值
一个指向Ivar数据结构的指针,该结构包含关于由名称指定的类变量的信息。
7.-----class_addIvar
向类中添加新实例变量。
宣言
BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, const char *types);
返回值
如果成功添加实例变量,则为YES,否则为NO(例如,该类已经包含一个同名的实例变量)。
讨论
此函数只能在objc_allocateClassPair之后和objc_registerClassPair之前调用。不支持向现有类中添加实例变量。
类不能是元类。不支持向元类添加实例变量。
实例变量的最小字节对齐是1<
8---class_copyIvarList
描述由类声明的实例变量。
宣言
Ivar _Nonnull * class_copyIvarList(Class cls, unsigned int *outCount);
参数---
cls------要检查的类。
outCount--- 返回时,包含返回数组的长度。如果outCount为NULL,则不返回长度。
返回值
类型Ivar的指针数组,描述类声明的实例变量。不包括超类声明的任何实例变量。该数组包含*outCount指针,后跟NULL结束符。必须使用free()释放数组。
如果该类没有声明实例变量,或者cls为Nil,则返回NULL,并且*outCount为0。
9----class_getIvarLayout
返回给定类的Ivar布局的描述。
宣言
const uint8_t * class_getIvarLayout(Class cls);
参数
cls--- 要检查的类。
返回值--cls的Ivar布局的描述。
10---class_setIvarLayout
设置给定类的Ivar布局。
void class_setIvarLayout(Class cls, const uint8_t *layout);
参数
cls--要修改的类。
布局--- cls的Ivars布局。
11---class_getWeakIvarLayout
返回给定类的弱Ivars布局的描述。
const uint8_t * class_getWeakIvarLayout(Class cls);
参数
cls--- 要检查的类。
12---class_setWeakIvarLayout
为给定类设置弱Ivars的布局。
void class_setWeakIvarLayout(Class cls, const uint8_t *layout);
参数
cls--- 要修改的类。
布局--- cls中弱Ivars的布局。
13---class_getProperty
返回具有给定类的给定名称的属性。
objc_property_t class_getProperty(Class cls, const char *name);
返回值
类型objc_property_t的指针,用于描述属性;如果类没有使用该名称声明属性,则为NULL;如果cls为Nil,则为NULL。
14--class_copyPropertyList
描述类声明的属性
objc_property_t _Nonnull * class_copyPropertyList(Class cls, unsigned int *outCount);
参数
cls----要检查的类。
outCount
返回时,包含返回数组的长度。如果outCount为NULL,则不返回长度。
返回值
类型objc_property_t的指针数组,描述类声明的属性。不包括超类声明的任何属性。该数组包含*outCount指针,后跟NULL结束符。必须使用free()释放数组。
如果cls声明没有属性,或者cls为Nil,则返回NULL和*outCount为0。
15--- class_addMethod
向具有给定名称和实现的类添加新方法。
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types);
参数
cls---要添加方法的类。
name-指定要添加的方法名称的选择器。
imp--一个函数,它是新方法的实现。函数必须至少有两个参数—self和_cmd。
types--描述方法参数类型的字符数组。有关可能的值,请参阅Objective-C运行时编程指南>类型编码。因为函数必须接受至少两个参数—self和_cmd,所以第二个和第三个字符必须是“@:”(第一个字符是返回类型)。
返回值
如果方法成功添加,则为YES,否则为NO(例如,该类已经包含一个同名的方法实现)。
讨论
class_addMethod将添加超类实现的重写,但不会替换该类中的现有实现。要更改现有的实现,请使用method_setImplementation。
Objective-C方法是一个至少包含两个参数的C函数——self和_cmd。例如,给定以下函数:
oid myMethodIMP(id self, SEL _cmd)
{
// implementation ....
}
你可以动态地将它作为一个方法添加到类中(称为resolveThisMethodDynamically),就像这样:
class_addMethod([self class], @selector(resolveThisMethodDynamically), (IMP) myMethodIMP, "v@:");
16---class_getClassMethod
返回一个指针,该指针指向描述给定类的给定类方法的数据结构。
Method class_getClassMethod(Class cls, SEL name);
参数
aClass---指向类定义的指针。传递包含要检索的方法的类。
aSelector---类型为SEL的指针。传递要检索的方法的选择器。
返回值
方法数据结构的指针,该指针对应于aSelector为aClass指定的类指定的选择器的实现,如果指定的类或其超类不包含具有指定选择器的类方法,则为NULL。
讨论
注意,这个函数搜索超类来实现,而class_copyMethodList没有。
17--- class_getInstanceMethod
返回给定类的指定实例方法。
Method class_getInstanceMethod(Class cls, SEL name);
参数
aClass--要检查的类。
aSelector--要检索的方法的选择器。
返回值
该方法对应于aSelector为aClass指定的类指定的选择器的实现,如果指定的类或其超类不包含具有指定选择器的实例方法,则该方法为NULL。
讨论
注意,这个函数搜索超类来实现,而class_copyMethodList没有。
18----class_getClassMethod
返回一个指针,该指针指向描述给定类的给定类方法的数据结构。
Method class_getClassMethod(Class cls, SEL name);
参数:
aClass---指向类定义的指针。传递包含要检索的方法的类。
aSelector--类型为SEL的指针。传递要检索的方法的选择器。
返回值
方法数据结构的指针,该指针对应于aSelector为aClass指定的类指定的选择器的实现,如果指定的类或其超类不包含具有指定选择器的类方法,则为NULL。
讨论
注意,这个函数搜索超类来实现,而class_copyMethodList没有。
19-- class_copyMethodList
描述由类实现的实例方法。
Method _Nonnull * class_copyMethodList(Class cls, unsigned int *outCount);
参数
cls---要检查的类。
outCount--返回时,包含返回数组的长度。如果outCount为NULL,则不返回长度。
返回值
描述类实现的实例方法的类型指针数组——不包括超类实现的任何实例方法。该数组包含*outCount指针,后跟NULL结束符。必须使用free()释放数组。
如果cls不实现实例方法,或者cls为Nil,则返回NULL和*outCount为0。
20---class_replaceMethod
替换给定类的方法的实现。
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types);
cls---要修改的类。
name---标识要替换其实现的方法的选择器。
imp---通过cls标识的类的名称标识的方法的新实现。
types--描述方法参数类型的字符数组。有关可能的值,请参阅Objective-C运行时编程指南>类型编码。因为函数必须接受至少两个参数—self和_cmd,所以第二个和第三个字符必须是“@:”(第一个字符是返回类型)。
返回值
通过cls标识的类的名称标识的方法的前面实现。
21---class_getMethodImplementation
返回在将特定消息发送到类的实例时将调用的函数指针。
IMP class_getMethodImplementation(Class cls, SEL name);
返回值
如果用类的实例调用[对象名],将调用的函数指针;如果cls为Nil,则调用NULL。
22--class_getMethodImplementation_stret
返回在将特定消息发送到类的实例时将调用的函数指针。
IMP class_getMethodImplementation_stret(Class cls, SEL name);
返回值
如果用类的实例调用[对象名],将调用的函数指针;如果cls为Nil,则调用NULL。
23--class_respondsToSelector
返回一个布尔值,该值指示类的实例是否响应特定的选择器。
BOOL class_respondsToSelector(Class cls, SEL sel);
返回值
如果类的实例响应选择器,则为YES,否则为NO。
24----class_addProtocol
向类添加协议。
BOOL class_addProtocol(Class cls, Protocol *protocol);
参数
cls-----要修改的类。
protocol---添加到cls的协议。
返回值
如果协议成功添加,则为YES,否则为NO(例如,该类已经符合该协议)。
25---class_addProperty
向类中添加属性。
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount);
参数
cls-----要修改的类。
name----属性的名称。
attributes---属性数组。
attributeCount---属性中的属性数。
返回值
是,如果属性添加成功;否则,NO(例如,如果类已经具有该属性,则该函数返回NO)。
26---class_replaceProperty
替换类的属性。
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount);
参数
cls-----要修改的类。
name----属性的名称。
attributes---属性数组。
attributeCount---属性中的属性数
27---class_conformsToProtocol
返回一个布尔值,该值指示类是否符合给定的协议。
BOOL class_conformsToProtocol(Class cls, Protocol *protocol);
返回值
如果cls符合协议,则为YES,否则为NO。
28---class_copyProtocolList
描述类所采用的协议。
Protocol * _Nonnull * class_copyProtocolList(Class cls, unsigned int *outCount);
返回值
类型为Protocol*的指针数组,描述类所采用的协议。不包括超类或其他协议所采用的任何协议。该数组包含*outCount指针,后跟NULL结束符。必须使用free()释放数组。
如果cls不采用任何协议,或者cls为Nil,则返回NULL和*outCount为0。
29----class_getVersion
返回类定义的版本号。
int class_getVersion(Class cls);
参数
theClass--指向类数据结构的指针。传递您希望获得版本的类定义。
返回值
表示类定义的版本号的整数。
30----class_setVersion
设置类定义的版本号。
void class_setVersion(Class cls, int version);
31---objc_getFutureClass
用于CoreFoundation的免费桥接。
Class objc_getFutureClass(const char *name);
与运行时交互
Objective-C程序在三个不同的层次上与运行时系统交互:通过Objective-C源代码;通过在基础框架的NSObject类中定义的方法;并通过直接调用运行时函数。
objective - c源代码
大多数情况下,运行时系统在后台自动运行。您可以通过编写和编译Objective-C源代码来使用它。
在编译包含Objective-C类和方法的代码时,编译器创建实现语言动态特性的数据结构和函数调用。数据结构捕捉类和类别定义以及协议声明中的信息;它们包括在用Objective-C编程语言定义类和协议时讨论的类和协议对象,以及方法选择器、实例变量模板和从源代码中提取的其他信息。正如在消息传递中所描述的,主运行时函数是发送消息的函数。它由源代码消息表达式调用。
NSObject方法
Cocoa中的大多数对象都是NSObject类的子类,所以大多数对象继承了它定义的方法。(值得注意的例外是NSProxy类;有关更多信息,请参见消息转发)。因此,它的方法建立了每个实例和每个类对象固有的行为。然而,在一些情况下,NSObject类仅仅定义了一个模板,用于说明应该如何完成某件事情;它本身并没有提供所有必需的代码。
例如,NSObject类定义了一个描述实例方法,该方法返回一个描述类内容的字符串。这主要用于调试—GDB打印对象命令打印从该方法返回的字符串。这个方法的NSObject实现不知道类包含什么,所以它返回一个带有对象名称和地址的字符串。NSObject的子类可以实现这个方法来返回更多的细节。例如,Foundation类NSArray返回它包含的对象的描述列表。
一些NSObject方法只是在运行时系统中查询信息。这些方法允许对象执行自省。此类方法的示例是类方法,它要求对象标识其类;isKindOfClass:和isMemberOfClass:,用于测试对象在继承层次结构中的位置;respondsToSelector:,指示对象是否能接受特定消息;conformsToProtocol:,表示对象是否声明要实现在特定协议中定义的方法;和methodForSelector:,它提供了方法实现的地址。这样的方法使对象能够自省。
运行时功能
运行时系统是一个动态共享库,其公共接口由位于/usr/include/ object .目录中的头文件中的一组函数和数据结构组成其中许多函数允许您使用纯C来复制编写Objective-C代码时编译器所做的事情。其他则构成通过NSObject类的方法导出的功能的基础。这些功能使开发运行时系统的其他接口和生成扩展开发环境的工具成为可能;在Objective-C中编程时不需要它们。然而,在编写Objective-C程序时,一些运行时函数有时可能是有用的。所有这些函数都记录在Objective-C运行时引用中。
规避动态绑定的唯一方法是获取方法的地址,然后直接调用它,就像调用函数一样。当一个特定的方法将被连续多次执行,并且您希望在每次执行该方法时避免消息传递的开销时,这种方法可能是合适的。
使用在NSObject类methodForSelector:中定义的方法,您可以请求指向实现方法的过程的指针,然后使用指针调用过程。methodForSelector:返回的指针必须小心地转换为适当的函数类型。在转换中应该包括返回类型和参数类型。
下面的示例展示了如何调用实现setFilled:方法的过程:
void (*setter)(id, SEL, BOOL);
int i;
setter = (void (*)(id, SEL, BOOL))[target
methodForSelector:@selector(setFilled:)];
for ( i = 0 ; i < 1000 ; i++ )
setter(targetList[i], @selector(setFilled:), YES);
传递给过程的前两个参数是接收对象(self)和方法选择器(_cmd)。这些参数隐藏在方法语法中,但在作为函数调用方法时必须显式。
使用methodForSelector:绕过动态绑定可以节省消息传递所需的大部分时间。但是,只有在多次重复某个特定消息的情况下,节省才会显著,如上所示的for循环中所示。
注意,methodForSelector:由Cocoa运行时系统提供;这不是Objective-C语言本身的特征。
例如,给定以下类声明:
@interface Lender : NSObject {
float alone;
}
@property float alone;
@end
你可以使用:
id LenderClass = objc_getClass("Lender");
unsigned int outCount;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
您可以使用property_getName函数来发现属性的名称:
const char *property_getName(objc_property_t property)
您可以使用class_getProperty和protocol_getProperty函数分别在类和协议中获得对具有给定名称的属性的引用:
objc_property_t class_getProperty(Class cls, const char *name)
objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)
将这些组合在一起,您可以使用以下代码打印与类关联的所有属性的列表:
id LenderClass = objc_getClass("Lender");
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}
网友评论