RunTime之类与对象
我们知道,Objective-C是一门动态语言,它将很多静态语言在编译时期做的事放到了运行时来处理。用C++编写的程序通过编译器直接把函数地址硬编码进入可执行文件;而Objective-C无法通过编译器直接把函数地址硬编码进入可执行文件,而是在程序运行的时候,利用Runtime根据条件判断作出决定,函数标识与函数执行的真正内容之间的关联可以动态修改。这样我们在写代码的时候,声明一个方法,但不对该方法做实现,静态语言在编译时就会报错,但OC中编译时不会报错。这种动态特性给我们带来的好处在于:我们写代码时更具有灵活性,例如我们可以把消息转发给想要的对象,或者交换一个方法的实现等等。
当然,这也意味着OC语言不仅需要一个编译器,还需要一个运行时系统来执行编译的代码。这个运行时系统就像一个操作系统一样,让它所有的工作可以正常运行。这就是我们本篇要聊的Runtime
。
1.类(Class)
OC的类是由Class类型来表示的。这个Class类型是什么呢?
//objc.h文件
typedef struct objc_class *Class; //runtime.h文件
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY; //指向meta-class(元类) #if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; //父类,如果是根类,则为NULL
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; //该类的方法链表
//用于缓存最近使用的方法。一个接收者对象接收到一个消息时,它会根据isa指针去查找能够响应这个消息的对象。
//在实际使用中,这个对象只有一部分方法是常用的,很多方法很少用或者根本用不上。这种情况下,如果每次消息来时都是methodLists中遍历一遍,性能势必很差。
//这时,cache就派上用场了。在我们每次调用过一个方法后,这个方法就会被缓存到cache列表中,下次调用的时候runtime就会优先去cache中查找,如果cache没有,才到methodLists中查找方法。
//这样,对于那些经常用到的方法的调用,提高了调用的效率。
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; //协议链表
#endif } OBJC2_UNAVAILABLE;
关于cache,这里用一个示例来解说:
// 第一步:执行[NSString alloc]。由于NSString没有+alloc方法,于是去父类NSObject去查找。
// 第二步:检测NSObject是否响应+alloc方法,发现响应,于是检测NSString类,并根据其所需的内存空间大小开始分配内存空间,然后把isa指针指向NSString类。同时,+alloc也被加进cache列表里面。
// 第三步:执行-init方法,如果NSString响应该方法,则直接将其加入cache;如果不响应,则去父类查找。
// 第四步:如果再以[[NSString alloc] init]这种方式来创建字符串,则会直接从cache中取出相应的方法,直接调用。
NSString *str = [[NSString alloc] init];
cache是一个指向objc_cache结构体的指针,这个结构体的定义如下:
struct objc_cache {
//指定分配的缓存bucket的总数
unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
//指定实际占用的缓存bucket的总数
unsigned int occupied OBJC2_UNAVAILABLE;
//指向Method数据结构指针的数组
Method buckets[] OBJC2_UNAVAILABLE;
};
2.对象
我们知道,OC中有一个id类型,该类型的对象可以转换为任何一种对象。
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
}; /// A pointer to an instance of a class.
typedef struct objc_object *id;
从定义可以看到,id类型是一个objc_object结构类型的指针。objc_object结构体也只是一个指向其类的isa指针。这样,当我们向一个Objective-C对象发送消息时,运行时库会根据实例对象的isa指针找到这个实例对象所属的类。runtime库会在类的方法列表及父类的方法列表中去寻找与消息对应的selector指向的方法,找到后运行这个方法。
当创建一个特定类的实例对象时,分配的内存包含一个objc_object数据结构,然后是类的实例变量的数据。NSObject类的alloc和allocWithZone:方法使用函数class_createInstance来创建objc_object数据结构。
3.meta-class(元类)
所有的类本身也是一个对象,我们可以向这个对象发送消息(即调用类方法)。如:
[NSString string];
从上面的代码可以看到,+string
消息发送给了NSString类,而这个NSString也是一个对象。既然是对象,那么它也是一个objc_object指针,它包含一个指向其类的一个isa指针。那么这个isa指针指向哪里呢?为了调用+string
方法,这个类的isa指针必须指向一个包含这些类方法的一个objc_class结构体。这就引出了meta-class的概念:meta-class是一个类对象的类。
- 当我们向一个对象发送消息时,runtime会在这个对象所属的这个类的方法列表中查找方法;
- 而向一个类发送消息时,会在这个类的meta-class的方法列表中查找。
meta-class之所以重要,是因为它存储着一个类的所有方法。每个类都会有一个单独的meta-class,因为每个类的类方法基本不可能完全相同。
再深入一下,meta-class也是一个类,也可以向它发送一个消息,那么它的isa又是指向什么呢?为了不让这种结构无限延伸下去,Objective-C的设计者让所有的meta-class的isa指向基类的meta-class,以此作为它们的所属类。即任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而基类的meta-class的isa指针是指向它自己。
下图描述了类及相应meta-class类的继承关系:
【注意】:所有meta-class中isa指针都指向根meta-class。而根meta-class则指向自身。根meta-class是通过继承Root class产生的。与Root class结构体成员一致,不同的是根meta-class的isa指针指向自身。
对于NSObject继承体系来说,其实例方法对体系中的所有实例、类和meta-class都是有效的;而类方法对于体系内的所有类和meta-class都是有效的。
下面来看一个示例:
- (void)ex_registerClassPair {
//创建存储空间
Class newClass = objc_allocateClassPair([NSObject class], "GofClass", ); /**
动态添加方法 @param cls 类类型
@param name 选择器(SEL)
@param imp 函数指针
@param type 方法类型
*/
SuppressUndeclaredSelectorWarning(class_addMethod(newClass, @selector(testMetaClass), (IMP)TestMetaClass, "v@:")); //注册这个类
objc_registerClassPair(newClass);
id instance = [[newClass alloc] init];
SuppressUndeclaredSelectorWarning([instance performSelector:@selector(testMetaClass)]);
} //隐式参数
void TestMetaClass(id self, SEL _cmd) {
NSLog(@"This object is %p", self);
NSLog(@"Class is %@, super class is %@", [self class], [self superclass]);
Class currentClass = [self class];
for (int i = ; i < ; i++) {
NSLog(@"Following the isa pointer %d times gives %p", i, currentClass);
currentClass = objc_getClass((__bridge void *)currentClass);
}
NSLog(@"NSObject's class is %p", [NSObject class]);
NSLog(@"NSObject's meta class is %p", objc_getClass((__bridge void *)[NSObject class]));
}
运行之后,打印结果如下:
在for循环中,通过objc_getClass来获取对象的isa,并将其打印出来,依此一直回溯到NSObject的meta-class。分析打印结果,可以看到最后指针指向的地址是0x0,即NSObject的meta-class的类地址。
4.类相关函数
runtime提供了大量的函数来操作类和对象。类的操作方法大部分是以class_
为前缀的,而对象的操作方法大部分是以objc_
或object_
为前缀。
4.1获取类名
//1.获取类名
NSLog(@"获取类名:%s", class_getName([GofBaseViewController class]));
【注意】:如果传入的cls为Nil/NULL/nil,则返回nil。
4.2获取父类
//2.获取父类
NSLog(@"获取父类:%@", class_getSuperclass([GofBaseViewController class]));
NSLog(@"获取父类2:%@", [GofBaseViewController superclass]);
【注意】:如果传入的cls为Nil/NULL/nil或者cls为根类时,则返回nil。
4.3是否meta-class
//3.是否meta-class
NSObject *obj = [[NSObject alloc] init];
Class objectClsObj = object_getClass(obj);
Class objectClsObj_isa = object_getClass(objectClsObj);
NSLog(@"是否meta-class:%d", class_isMetaClass(objectClsObj)); //否
NSLog(@"是否meta-class:%d", class_isMetaClass(objectClsObj_isa)); //是
【注意】:如果传入的cls为meta-class,则返回YES;否则返回NO;
4.4实例变量大小
//4.获取实例变量大小
NSLog(@"获取实例变量大小:%zu", class_getInstanceSize(objectClsObj));
4.5成员变量
主要有以下相关函数:
//获取类中指定名称实例成员变量的信息
OBJC_EXPORT Ivar class_getInstanceVariable(Class cls, const char *name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
//获取类成员变量的信息
OBJC_EXPORT Ivar class_getClassVariable(Class cls, const char *name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//添加成员变量
OBJC_EXPORT BOOL class_addIvar(Class cls, const char *name, size_t size,
uint8_t alignment, const char *types)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//获取整个成员变量列表
OBJC_EXPORT Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
下面通过一个示例细看一下:
GofPerson.h文件
@interface GofPerson : NSObject
{
NSInteger age;
} @property (nonatomic, strong) NSString *name; //!<姓名 @end GofPerson.m文件
@interface GofPerson () @property (nonatomic, strong) NSString *phone; @end @implementation GofPerson @end GofPerson *person = [[GofPerson alloc] init];
Class objectClsObj = object_getClass(person); Ivar ivar0 = class_getInstanceVariable(objectClsObj, "age");
const char *age = ivar_getName(ivar0);
NSLog(@"成员变量:%s", age); Ivar ivar1 = class_getClassVariable([GofPerson class], "isa");
const char *classVariable = ivar_getName(ivar1);
NSLog(@"成员变量:%s", classVariable); //在这里添加无效
class_addIvar(objectClsObj, "name1", sizeof(id), log2(sizeof(id)), "@"); unsigned int count = ;
Ivar *ivars = class_copyIvarList([GofPerson class], &count); for (int i = ; i < count; i++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar); NSLog(@"成员变量:%s", name);
}
【说明】:class_addIvar方法只能在objc_allocateClassPair
函数与objc_registerClassPair
之间调用。
打印结果如下:
4.6属性
和上一小节一样,我们先看看主要都有哪些函数?
//获取指定的属性
OBJC_EXPORT objc_property_t class_getProperty(Class cls, const char *name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//获取属性列表
OBJC_EXPORT objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//添加属性
OBJC_EXPORT BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
//替换类的属性
OBJC_EXPORT void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
怎么使用呢?
GofPerson *person = [[GofPerson alloc] init];
Class objectClsObj = object_getClass(person); //获取指定的属性
objc_property_t property0 = class_getProperty(objectClsObj, "phone");
const char *phone = property_getName(property0);
NSLog(@"成员属性:%s", phone); //添加属性
objc_property_attribute_t attribute1 = {"T", "@\"NSString\""}; //type
objc_property_attribute_t attribute2 = {"C", ""}; //copy
objc_property_attribute_t attribute3 = {"N", ""}; //nonatomic
objc_property_attribute_t attribute4 = {"V", "_email"}; //variable name
objc_property_attribute_t attributesList[] = {attribute1, attribute2, attribute3, attribute4};
if(class_addProperty([GofPerson class], "email", attributesList, )) {
NSLog(@"add property success!");
}
else {
NSLog(@"add property failure!");
} //替换属性的信息(如果没有原属性会新建一个属性)
objc_property_attribute_t attribute11 = {"T", "@\"NSString\""}; //type
objc_property_attribute_t attribute22 = {"C", ""}; //copy
objc_property_attribute_t attribute33 = {"N", ""}; //nonatomic
objc_property_attribute_t attribute44 = {"V", "_mobile"}; //variable name
objc_property_attribute_t attributesList1[] = {attribute11, attribute22, attribute33, attribute44};
class_replaceProperty([GofPerson class], "mobile", attributesList1, ); //获取属性列表(分类中的属性也会显示)
unsigned int count = ;
objc_property_t *propertys = class_copyPropertyList([GofPerson class], &count); for (int i = ; i < count; i++) {
objc_property_t property = propertys[i];
const char *name = property_getName(property);
const char *attribute = property_getAttributes(property);
NSLog(@"propertyName: %s, attribute: %s", name, attribute); unsigned int attributeCount;
objc_property_attribute_t *attributeList = property_copyAttributeList(property, &attributeCount);
for (unsigned int j = ; j < attributeCount; j++) {
objc_property_attribute_t attribute = attributeList[j];
const char *name = attribute.name;
const char *value = attribute.value;
NSLog(@"attribute name: %s, value: %s", name, value);
}
}
free(propertys);
打印结果如下:
4.7方法
主要有以下函数来做方法操作:
//添加方法
OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp,
const char *types)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//获取实例方法
OBJC_EXPORT Method class_getInstanceMethod(Class cls, SEL name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
//获取类方法
OBJC_EXPORT Method class_getClassMethod(Class cls, SEL name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
//获取所有方法的数组
OBJC_EXPORT Method *class_copyMethodList(Class cls, unsigned int *outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//替代方法的实现
OBJC_EXPORT IMP class_replaceMethod(Class cls, SEL name, IMP imp,
const char *types)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//返回方法的具体实现
OBJC_EXPORT IMP class_getMethodImplementation(Class cls, SEL name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
OBJC_EXPORT IMP class_getMethodImplementation_stret(Class cls, SEL name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0)
OBJC_ARM64_UNAVAILABLE;
//类实例是否响应指定的selector
OBJC_EXPORT BOOL class_respondsToSelector(Class cls, SEL sel)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
我们先给上面定义的GofPerson类添加几个方法:
//GofPerson.h
@interface GofPerson : NSObject
{
NSInteger age;
} @property (nonatomic, strong) NSString *name; //!<姓名 + (GofPerson *)createPersonWithName:(NSString *)nameString
age:(NSInteger)ageInt; - (void)setName:(NSString *)nameString
age:(NSInteger)ageInt; @end //GofPerson.m
@interface GofPerson () @property (nonatomic, strong) NSString *phone; @end @implementation GofPerson + (GofPerson *)createPersonWithName:(NSString *)nameString
age:(NSInteger)ageInt
{
GofPerson *person = [[GofPerson alloc] init];
[person setName:@"LeeGof" age: phone:@""];
return person;
} - (void)setName:(NSString *)nameString
age:(NSInteger)ageInt
{
self.name = nameString;
age = ageInt;
} - (void)setName:(NSString *)nameString
age:(NSInteger)ageInt
phone:(NSString *)phoneString
{
self.name = nameString;
age = ageInt;
self.phone = phoneString;
} - (void)dealloc {
NSLog(@"GofPerson dealloc");
} @end
然后我们来对上面的方法操作函数做一个简单的示例:
/**
操作方法
*/
- (void)operateMethod
{
GofPerson *person = [[GofPerson alloc] init];
Class objectClsObj = object_getClass(person);
Class objectClsObj_isa = object_getClass(objectClsObj); //添加方法
SuppressUndeclaredSelectorWarning(class_addMethod(objectClsObj, @selector(newMethod), (IMP)testNewMethod, "v@:"));
//调用新添加的方法
SuppressUndeclaredSelectorWarning([person performSelector:@selector(newMethod)]); //获取实例方法,如果方法不存在,返回nil
Method instanceMethod = class_getInstanceMethod(objectClsObj, @selector(setName:age:));
NSLog(@"instanceMethod:%s", sel_getName(method_getName(instanceMethod))); //获取类方法
Method classMethod = class_getClassMethod(objectClsObj, @selector(createPersonWithName:age:));
NSLog(@"classMethod:%s", sel_getName(method_getName(classMethod))); //获取实例方法列表
unsigned int count = ;
Method *methods = class_copyMethodList(objectClsObj, &count);
for (int i = ; i < count; i++) {
Method methodItem = methods[i];
const char *methodType = method_getTypeEncoding(methodItem);// 获取方法参数类型和返回类型
NSLog(@"instance method item%d:%s %s",i, sel_getName(method_getName(methodItem)), methodType);
}
free(methods);
//获取类方法列表
unsigned int countClass = ;
Method *classMethods = class_copyMethodList(objectClsObj_isa, &countClass);
for (int i = ; i < countClass; i++) {
Method methodItem = classMethods[i];
const char *methodType = method_getTypeEncoding(methodItem);// 获取方法参数类型和返回类型
NSLog(@"class method item%d:%s %s",i, sel_getName(method_getName(methodItem)), methodType);
}
free(classMethods);
//替换方法实现 如果类中不存在newMethod指定的方法,则会和class_addMethod函数一样添加方法
SuppressUndeclaredSelectorWarning(class_replaceMethod(objectClsObj, @selector(newMethod), (IMP)replaceNewMethod, "v@:"));
//调用替换的方法
SuppressUndeclaredSelectorWarning([person performSelector:@selector(newMethod)]); //获取方法的具体实现. 该函数在向类实例发送消息时会被调用,并返回一个指向方法实现函数的指针。返回的函数指针可能是一个指向runtime内部的函数,而不一定是方法的实际实现。
//例如,如果类实例无法响应selector,则返回的函数指针将是运行时消息转发机制的一部分。
IMP implement = class_getMethodImplementation(objectClsObj, @selector(setName:age:));
//获取方法的具体实现.该方法的返回值类型为struct
IMP implement1 = class_getMethodImplementation_stret(objectClsObj, @selector(setName:age:)); //判断类实例是否响应指定的selector
BOOL canResponse = class_respondsToSelector(objectClsObj, @selector(setName:age:)); NSLog(@"类实例是否响应 : %d", canResponse);
} //隐式参数
void testNewMethod(id self, SEL _cmd) {
NSLog(@"Hello,newMethod");
} void replaceNewMethod(id self, SEL _cmd) {
NSLog(@"Hello,replaceNewMethod");
}
看一下打印结果:
4.8协议
协议相关的操作包含以下函数:
//类是否实现指定的协议.可以使用NSObject类的conformsToProtocol:
方法来代替
OBJC_EXPORT BOOL class_conformsToProtocol(Class cls, Protocol *protocol)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//添加协议
OBJC_EXPORT BOOL class_addProtocol(Class cls, Protocol *protocol)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//返回类实现的协议列表
OBJC_EXPORT Protocol * __unsafe_unretained *class_copyProtocolList(Class cls, unsigned int *outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
看一个简单的示例:
GofPerson *person = [[GofPerson alloc] init];
Class objectClsObj = object_getClass(person); Protocol *coding = objc_getProtocol("NSCoding"); //类是否实现了协议
BOOL isImplement = class_conformsToProtocol(objectClsObj, coding);
NSLog(@"类是否实现了协议: %d", isImplement); //添加协议
BOOL isSuccess = class_addProtocol(objectClsObj, coding);
NSLog(@"添加协议是否成功:%d", isSuccess); //类是否实现了协议
BOOL isImplement1 = class_conformsToProtocol(objectClsObj, coding);
NSLog(@"类是否实现了协议: %d", isImplement1); unsigned int count = ;
Protocol * __unsafe_unretained *protocols = class_copyProtocolList(objectClsObj, &count); for (int i = ; i < count; i++) {
Protocol *item = protocols[i];
NSLog(@"protocol item%d:%s",i, protocol_getName(item));
}
free(protocols);
打印结果:
4.9版本
版本相关的操作包含以下函数:
//获取版本号
OBJC_EXPORT int class_getVersion(Class cls)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
//设置版本号
OBJC_EXPORT void class_setVersion(Class cls, int version)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
这两个函数比较简单,示例如下:
//获取版本
int version = class_getVersion([GofPerson class]);
NSLog(@"版本:%d", version); //设置版本
class_setVersion([GofPerson class], );
4.10动态创建类
动态创建类包含以下函数:
// 创建新类并分配存储空间
OBJC_EXPORT Class objc_allocateClassPair(Class superclass, const char *name,
size_t extraBytes)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//销毁一个类
OBJC_EXPORT void objc_disposeClassPair(Class cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//在应用中注册由objc_allocateClassPair创建的类
OBJC_EXPORT void objc_registerClassPair(Class cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
使用在前面已经写过,这里温习一下:
- (void)createClass
{
//创建新类 如果是创建基类,那么superClass指定为Nil
Class newClass = objc_allocateClassPair([GofBaseViewController class], "GofClass", ); /**
动态添加方法 实例方法和实例变量应该添加到类自身上,而类方法应该添加到类的元类上 @param cls 类类型
@param name 选择器(SEL)
@param imp 函数指针
@param type 方法类型
*/
SuppressUndeclaredSelectorWarning(class_addMethod(newClass, @selector(newMethod), (IMP)newMethodImplement, "v@:"));
//在应用中注册这个类 如果已经注册,再注册会直接崩溃!!!
objc_registerClassPair(newClass); id instance = [[newClass alloc] init];
// id instance = class_createInstance(newClass, 0); //ARC下不能使用
SuppressUndeclaredSelectorWarning([instance performSelector:@selector(newMethod)]);
} //隐式参数
void newMethodImplement(id self, SEL _cmd) {
NSLog(@"newMethodImplement");
} - (void)dealloc
{
Class newClass = NSClassFromString(@"GofClass");
if (newClass) {
objc_disposeClassPair(newClass); //如果程序运行中还存在类或者其子类的实例时,则不能调用该方法
}
}
4.11对象操作
包含以下函数:
//MRC 创建对象
OBJC_EXPORT id class_createInstance(Class cls, size_t extraBytes)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ARC_UNAVAILABLE;
//MRC 销毁对象
OBJC_EXPORT void *objc_destructInstance(id obj)
OBJC_AVAILABLE(10.6, 3.0, 9.0, 1.0)
OBJC_ARC_UNAVAILABLE;
//MRC 对象拷贝
OBJC_EXPORT id object_copy(id obj, size_t size)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ARC_UNAVAILABLE;
//MRC 释放指定对象占用的内存
OBJC_EXPORT id object_dispose(id obj)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ARC_UNAVAILABLE;
//MRC 修改类实例的实例变量的值
OBJC_EXPORT Ivar object_setInstanceVariable(id obj, const char *name, void *value)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ARC_UNAVAILABLE;
//MRC 获取对象实例变量的值
OBJC_EXPORT Ivar object_getInstanceVariable(id obj, const char *name, void **outValue)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ARC_UNAVAILABLE;
//ARC 返回对象中实例变量的值
OBJC_EXPORT id object_getIvar(id obj, Ivar ivar)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//ARC 设置对象中实例变量的值
OBJC_EXPORT void object_setIvar(id obj, Ivar ivar, id value)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//返回给定对象的类名(objc.h)
OBJC_EXPORT const char *object_getClassName(id obj)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
//返回对象的类(runtime.h)
OBJC_EXPORT Class object_getClass(id obj)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//设置对象的类
OBJC_EXPORT Class object_setClass(id obj, Class cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
示例如下:
//MRC下操作
//动态创建对象
id object = class_createInstance([NSObject class], );
//MRC 拷贝对象
//常用于这样的场景:有类A和类B,且类B是类A的子类。类B通过添加一些额外的属性来扩展类A。
//现在我们创建了一个A类的实例对象,并希望在运行时将这个对象转换为B类的实例对象,这样可以添加数据到B类的属性中。
//这种情况下,我们没有办法直接转换,因为B类的实例会比A类的实例更大,没有足够的空间来放置对象。
id person = object_copy(object, class_getInstanceSize(GofPerson.class));
//设置对象的类
object_setClass(person, GofPerson.class);
//MRC 释放对象占用的内存
object_dispose(object); //MRC 修改实例变量的值
object_setInstanceVariable(person, "_name", @"bbb");
void *outValue = (void *)0x1;
//MRC 获取实例变量的值
object_getInstanceVariable(person, "_name", &outValue);
if (nil == outValue) {
NSLog(@"nil");
}
else
{
NSLog(@"outValue:%@", outValue);
}
if (person) {
[person release];
person = nil;
} //ARC下操作
GofPerson *person = [[GofPerson alloc] init];
Class objectClsObj = object_getClass(person); //ARC 获取实例变量的值
Ivar ivar = class_getInstanceVariable(objectClsObj, "_phone");
//ARC 修改实例变量的值
object_setIvar(person, ivar, @"");
NSLog(@"getIvar:%@", object_getIvar(person, ivar)); //获取类名
NSLog(@"类名:%s", object_getClassName(person));
【说明】:如果实例变量的Ivar已经知道,那么调用object_getIvar
会比object_getInstanceVariable
函数快,相同情况下,object_setIvar
也比object_setInstanceVariable
快。
4.12获取类定义
Objective-C动态运行库会自动注册我们代码中定义的所有的类。我们也可以在运行时创建类定义并注册它们。runtime提供了一系列函数来获取类定义相关的信息,这些函数主要包括:
//MRC 获取已注册的类定义的列表
OBJC_EXPORT int objc_getClassList(Class *buffer, int bufferCount)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
//创建并返回一个指向所有已注册类的指针列表
OBJC_EXPORT Class *objc_copyClassList(unsigned int *outCount)
OBJC_AVAILABLE(10.7, 3.1, 9.0, 1.0);
//返回指定类的类定义
//如果类在运行时未注册,则objc_lookUpClass
会返回nil,而objc_getClass
会调用类处理回调,并再次确认该类是否注册,如果确认未注册,则返回nil。
//而objc_getRequiredClass
函数的操作与objc_getClass
相同,只不过如果没有找到类,则会杀死进程
OBJC_EXPORT Class objc_lookUpClass(const char *name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
OBJC_EXPORT Class objc_getClass(const char *name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
OBJC_EXPORT Class objc_getRequiredClass(const char *name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
//返回指定类的元类
OBJC_EXPORT Class objc_getMetaClass(const char *name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
示例如下:
//MRC
int numClasses;
Class *classes = NULL;
numClasses = objc_getClassList(NULL, );
if (numClasses > ) {
classes = malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);
NSLog(@"number of classes: %d", numClasses);
for (int i = ; i < numClasses; i++) {
Class cls = classes[i];
NSLog(@"class name: %s", class_getName(cls));
}
free(classes);
} //ARC
unsigned int count = ;
Class *classes = objc_copyClassList(&count);
NSLog(@"number of classes: %d", count);
for (int i = ; i < count; i++) {
Class class = classes[i];
NSLog(@"class name: %s", class_getName(class));
}
5.小结
本篇介绍了运行时类与对象的相关数据结构,并对与之相关的函数做了一个简单的领读和演示。通过这些函数,我们可以灵活的运用运行时特性进行开发,比如动态的添加类、属性、成员变量、方法、协议等等。
RunTime之类与对象的更多相关文章
- 福利->KVC+Runtime获取类/对象的属性/成员变量/方法/协议并实现字典转模型
我们知道,KVC+Runtime可以做非常多的事情.有了这个,我们可以实现很多的效果. 这里来个福利,利用KVC+Runtime获取类/对象的所有成员变量.属性.方法及协议: 并利用它来实现字典转模型 ...
- 通过runtime打印出对象所有属性的值
通过runtime打印出对象所有属性的值 今天给给大家提供的关于NSObject的category,通过runtime打印属性的值,相当有用哦,以后你再也不用每个对象都通过NSLog来逐个打印属性值了 ...
- OC - runtime 之关联对象
header{font-size:1em;padding-top:1.5em;padding-bottom:1.5em} .markdown-body{overflow:hidden} .markdo ...
- JAVA调用系统命令或可执行程序--返回一个Runtime运行时对象,然后启动另外一个进程来执行命令
通过 java.lang.Runtime 类可以方便的调用操作系统命令,或者一个可执行程序,下面的小例子我在windows和linux分别测试过,都通过.基本原理是,首先通过 Runtime.getR ...
- runtime之实现对象序列化
/* iOS序列化,将对象转成二进制,保存到本地 */ 定义一个对象,让它实现NSCoding协议,保证对象的编码和解码,person有三个属性 @interface Person : NSObjec ...
- 利用runtime动态生成对象?
利用runtime我们能够动态生成对象.属性.方法这特性 假定我们要动态生成DYViewController,并为它创建属性propertyName 1)对象名 NSString *class = @ ...
- runtime实现对象存储型数据库——LHDB
前言 最近在GitHub上看了一份关于基于runtime封装的对象存储型数据库的开源代码,觉得非常值得分享记录一下,在IOS中对数据库的操作一般通过CoreData和SQLite,CoreData 虽 ...
- Runtime之成员变量&属性&关联对象
上篇介绍了Runtime类和对象的相关知识点,在4.5和4.6小节,也介绍了成员变量和属性的一些方法应用.本篇将讨论实现细节的相关内容. 在讨论之前,我们先来介绍一个很冷僻但又很有用的一个关键字:@e ...
- Runtime对象
Runtime简单概念: Runtime:每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接. * 这也是jvm实现跨平台的一个重要原因. * 可以通过 ge ...
随机推荐
- Optional 的基本用法
参考: https://www.cnblogs.com/xingzc/p/5778090.html http://www.runoob.com/java/java8-optional-class.ht ...
- 清除eclipse,STS workspace历史记录
记一下 打开eclipse下的/configuration/.settings目录 修改文件org.eclipse.ui.ide.prefs文件 把RECENT_WORKSPACES这项修改为你需要的 ...
- Web框架本质及第一个Django实例 Web框架
Web框架本质及第一个Django实例 Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web ...
- IIC 设备使用
通过 读 / 写 IIC 设备上特定的存储空间,来使用设备提供的功能: 存储空间地址 = 设备名 + 设备地址(Slave Address) + 寄存器地址 . 注:设备地址.寄存器地址.地址中写入数 ...
- virtual 函数只有在用指针或引用的方式访问,才会导致多态。
只有用指针和引用,才会动态绑定.才会在运行时候从虚表中找对应的成员函数. 如果只是用.访问成员函数,是静态绑定,在编译时刻就固定好的. 另外,父类的虚函数,子类不管加不加virtual关键字,都是虚函 ...
- css:常见布局问题
一.单列布局 1. 水平居中 1.1 使用inline-block和text-align .parent{text-align:center;} .child{display:inline-block ...
- ansible 问题
如下图,A服务器上用普通账号ansible有时就会报错,但有时却又正常,可以连接成功,用root账号执行ansible就完全没问题. 仅仅是这一台服务器有问题,其他都完全正常..ansible 文件删 ...
- Redis在linux上的配置
一.安装gcc 1.Redis在linux上的安装首先必须先安装gcc,这个是用来编译redis的源文件的.首先需要先切换的到root用户 2.然后开始安装gcc: yum install gcc- ...
- C++ 设置光标问题
一.隐藏光标 1.引入头文件window.h 2. 定义光标信息结构体变量 CONSOLE_CURSOR_INFO cursor info={1,0}; typedef struct _CONSO ...
- Codeforces Round #554 (Div. 2)-C(gcd应用)
题目链接:https://codeforces.com/contest/1152/problem/C 题意:给定a,b(<1e9).求使得lcm(a+k,b+k)最小的k,若有多个k,求最小的k ...