闲情——C语言指针和数组的探索

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++的高手来说,还是太幼稚了,但是,思考的过程还是非常有趣。况且,在上面这个练习中,还顺手学习了一下可变参数的内容。

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


更多内容欢迎访问我的博客http://suntao.me

你可能感兴趣的:(ios,objective-c)