objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)函数是runtime中的一个函数,用来 copy 一份类对象的属性列表,返回值为objc_property_t *类型的数组。然后就可以遍历这个数组取出每个属性值。几乎每个字典转模型框架都需要这个函数。

- (NSDictionary *)properties_aps {
NSMutableDictionary *props = [NSMutableDictionary dictionary];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([self class], &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
NSString *propertyName = [[[NSString alloc] initWithCString:property_getName(property)] autorelease];
id propertyValue = [self valueForKey:(NSString *)propertyName];
if (propertyValue) [props setObject:propertyValue forKey:propertyName];
}
free(properties);
return props;
}

这段代码中就包含了取属性的函数:

objc_property_t *properties = class_copyPropertyList([self class], &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
}

objc_property_t *类型的properties数组,取出下标,得到的是objc_property_t类型的数组元素。

这个写法突然感觉有些不解,因为如果我们用int来代替objc_property_t

int main(int argc, const char * argv[]) {
int *arr = {1,2,3};
arr[1] = 10;
int a = arr[1];
NSLog(@"%d", a);
return 0;
}

这样显然是不行的,int *只是一个指针,C语言中,数组的本质是指针加偏移量,所以当给arr[1]赋值时,相当于是给arr这个指针偏移了8bit,然而我们并没有为后面的地址开辟内存空间,自然就造成了EXC_BAD_ACCESS错误。

那么,该怎么写成runtime里的那段代码一样type *类型的foo数组变量,可以用foo[1]取得一个type类型的数组元素呢?

所以我们要分析,上面那个int的写法是怎么不对的。因为在给int *arr初始化时,并没有开辟一个sizeof(int)*count大小的内存空间。

之所以如果写成int arr[3]就可以,是因为这么写,C语言数组的本质是就是指针加偏移量。初始化了一个int指针,而且给了3*sizeof(int)大小的内存空间,返回了一个指针arr。所以,我们可以手动做这个步骤:

typedef int integer;
typedef integer * integer_pointer;
integer_pointer * addIntegerToArr(integer_pointer p, ...) {
integer_pointer *result = nil;
if (p) {
va_list args;
int count = 1;
integer_pointer current;
va_start(args, p);
while ((current = va_arg(args, integer_pointer))) {
++count;
}
va_end(args);
result = (integer_pointer *)malloc((count) * sizeof(integer_pointer));
result[0] = p;
va_start(args, p);
current = va_arg(args, integer_pointer);
for (int i = 1; i < count; i++) {
result[i] = current;
}
va_end(args);
}
return (integer_pointer *)result;
}
int main(int argc, const char * argv[]) {
integer a = 10;
integer b = 11;
integer_pointer pa = &a;
integer_pointer pb = &b;
integer_pointer *r = addIntegerToArr(pa, pb);
integer_pointer num = r[1];
NSLog(@"%d", *num);
return 0;
}

里面真正关键的一句代码就是result = (integer_pointer *)malloc((count) * sizeof(integer_pointer));,这样我们就开辟了一个内存空间,并且得到一个integer_pointer *类型的指针,所以可以通过对这个指针的偏移,来操作这个空间中的integer_pointer变量。

之所以result看起来是一个数组,可以用result[i]这种写法,就是因为上面的原因。

所以,还是要谨记,C语言中的数组,本质上就是指针加偏移。


虽然我们是iOS开发者,但是毕竟C语言不能放,闲来无事可以仿照runtime中很多C语言特征的写法,来实现一些语言层面的学习。虽然上面所说的内容,对真正精通C/C++的高手来说,还是太幼稚了,但是,思考的过程还是非常有趣。况且,在上面这个练习中,还顺手学习了一下可变参数的内容。

并没什么太高的技术含量,所以,此篇算作闲情而已。