利用运行时runtime机制,我们可以获取类文件的属性或者成员变量,还可以为类(当然包括某些分类)动态添加属性或者成员变量,要知道苹果是不允许我们用普通方法为分类增加属性的,但是通过运行时我们就可以做到。
获取类属性或者成员变量:
首先有几个相关的API要弄明白:
/** 获取类所有通过@property方式声明的成员变量或者属性(参数1:要获取成员变量或者属性的类,参数2:unsigned int变量的指针地址,用来存放获取到了多少个成员变量) */
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
/** 获取类所有通过@property以及在@interface大括号里边声明的成员变量或者属性(参数1、2传入参数同上) */
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
有关这两个方法的区别,请参考这里
首先我们新建一个PropertyTest
类,在.h
文件中声明几个成员变量和属性
{
float _webSiteTitle;
float _privateName;
float _privateAttribute;
}
@property (nonatomic, assign) int count;
@property (nonatomic, weak) id delegate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, strong) NSArray *names;
@property (nonatomic, strong) NSNumber *atomicProperty;
在.m
文件中,获取类所有成员变量以及属性
// 获取所有通过@property方式声明的属性
- (void)getAllProperties
{
unsigned int count = 0;
objc_property_t *pros = class_copyPropertyList([self class], &count);
for (unsigned int i=0; i<count; i++) {
objc_property_t pro = pros[i];
// 'property_getName'获取属性名称
const char *proName = property_getName(pro);
// 'property_getAttributes'获取属性具体描述
const char *proAttr = property_getAttributes(pro);
NSLog(@"proName:%s proAttr:%s", proName, proAttr);
unsigned int aCount = 0;
objc_property_attribute_t *attrs = property_copyAttributeList(pro, &aCount);
for (int j=0; j<aCount; j++) {
objc_property_attribute_t attr = attrs[j];
// 'objc_property_attribute_t'是个结构体,包含'value' 和 'name'两个值
const char *attrName = attr.name;
const char *attrValue = attr.value;
NSLog(@"proName:%s value:%s", attrName, attrValue);
}
// 用完之后一定记得手动释放,否则会造成内存泄漏
free(attrs);
}
free(pros);
}
// 获取所有属性(包括通过在` {} `里边声明的,以及通过@property方式声明的)
- (void)getAllMemberVariables
{
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([self class], &count);
for (unsigned int i=0; i<count; i++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
const char *type = ivar_getTypeEncoding(ivar);
NSLog(@"IvarsName:%s encodeType:%s", name, type);
}
free(ivars);
}
+ (void)test
{
PropertyTest *test = [[PropertyTest alloc] init];
[test getAllProperties];
}
首先我们来看下 getAllProperties 方法打印的结果:
打印结果如下:

让我们来分析下打印结果(第1、2、3和4行是一个属性)(这里我们只分析一个属性的情况,因为其他情况类似):
第一行:
proName(属性名):count proAttr(属性包含的特性):Ti,N,V_count
第二、三和四行:
分别是对第一行中proAttr的详细解释包括属性名和属性值。
那么第一行中打印的proAttr属性名和属性值分别代表什么呢?我们再来分析,根据苹果官方文档,一个属性的attribute这样组成: T, ... ,V_属性名
其中 *1、T表示属性类型,比如int类型为Ti,id类型为T@,NSString类型为T@"NSString"
2、中间的...根据不同类型的属性而定义为不同的值,比如'N'表示声明为conatomic,'C'表示属性声明为copy,'&'表示声明为retain... 以此类推
3、V_属性名 表示实例变量?
更详细的情况可以参考苹果官方文档的解释
我们再看下通过方法 getAllMemberVariables 打印的结果:

由打印结果我们可以看出,这个方法打印出了我们定义的所有属性以及成员变量,其中每一行的
第一个参数:_属性或者成员变量名称
第二个参数:表示变量类型,比如float类型为'f',int类型为'i',NSString类型为'@NSString'... 以此类推
更详细的情况可以参考苹果官方文档的解释
有了这两个方法,那么我们在动态地获取某个类的变量的时候就方便了很多,相似的,runtime中也有获取类的方法(method)、协议(protocol)等列表的API,有兴趣的朋友可以去学习下
网友评论