声明
本博客中文章不会在此处再更新,只会在微信公众号中更新,请关注微信公众号,以获取最新的学习资源和更多学习资源。本博文末尾有微信公众号二维码,扫一扫添加关注。
原文出自:微信公众号iOSDevShares的文章
引言
相信很多同学都听过运行时,但是我相信还是有很多同学不了解什么是运行时,到底在项目开发中怎么用?什么时候适合使用?想想我们的项目中,到底在哪里使用过运行时呢?还能想起来吗?另外,在面试的时候,是否经常有笔试中要求运用运行时或者在面试时面试官会问是否使用过运行时,又是如何使用的?
回想自己,曾经在面试中被面试官拿运行时刁难过,也在笔试中遇到过。因此,后来就深入地学习了Runtime
机制,学习里面的API。所以才有了后来的组件封装中使用运行时。
相信我们都遇到过这样一个问题:我想在扩展(category)中添加一个属性,如果iOS是不允许给扩展类扩展属性的,那怎么办呢?答案就是使用运行时机制
运行时机制
Runtime
是一套比较底层的纯C语言的API, 属于C语言库, 包含了很多底层的C语言API。
在我们平时编写的iOS代码中, 最终都是转成了runtime的C语言代码。
所谓运行时,也就是在编译时是不存在的,只是在运行过程中才去确定对象的类型、方法等。利用Runtime
机制可以在程序运行时动态修改类、对象中的所有属性、方法等。
还记得我们在网络请求数据处理时,调用了-setValuesForKeysWithDictionary:
方法来设置模型的值。这里什么原理呢?为什么能这么做?其实就是通过Runtime
机制来完成的,内部会遍历模型类的所有属性名,然后设置与key
对应的属性名的值。
我们在使用运行时的地方,都需要包含头文件:#import <objc/runtime.h>
。如果是Swift
就不需要包含头文件,就可以直接使用了。
获取对象所有属性名
利用运行时获取对象的所有属性名是可以的,但是变量名获取就得用另外的方法了。我们可以通过class_copyPropertyList
方法获取所有的属性名称。
下面我们通过一个Person
类来学习,这里的方法没有写成扩展,只是为了简化,将获取属性名的方法直接作为类的实例方法:
Objective-C版
@interface Person : NSObject {
NSString *_variableString;
}
// 默认会是什么呢?
@property (nonatomic, copy) NSString *name;
// 默认是strong类型
@property (nonatomic, strong) NSMutableArray *array;
// 获取所有的属性名
- (NSArray *)allProperties;
@end
下面主要是写如何获取类的所有属性名的方法。注意,这里的objc_property_t
是一个结构体指针objc_property *
,因此我们声明的properties
就是二维指针。在使用完成后,我们一定要记得释放内存,否则会造成内存泄露。这里是使用的是C语言的API,因此我们也需要使用C语言的释放内存的方法free
。
/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;
- (NSArray *)allProperties {
unsigned int count;
// 获取类的所有属性
// 如果没有属性,则count为0,properties为nil
objc_property_t *properties = class_copyPropertyList([self class], &count);
NSMutableArray *propertiesArray = [NSMutableArray arrayWithCapacity:count];
for (NSUInteger i = 0; i < count; i++) {
// 获取属性名称
const char *propertyName = property_getName(properties[i]);
NSString *name = [NSString stringWithUTF8String:propertyName];
[propertiesArray addObject:name];
}
// 注意,这里properties是一个数组指针,是C的语法,
// 我们需要使用free函数来释放内存,否则会造成内存泄露
free(properties);
return propertiesArray;
}
现在,我们来测试一下,我们的方法是否正确获取到了呢?看下面的打印结果就明白了吧!
Person *p = [[Person alloc] init];
p.name = @"Lili";
size_t size = class_getInstanceSize(p.class);
NSLog(@