摘要:函数是中的一个函数,用来一份类对象的属性列表,返回值为类型的数组。之所以如果写成就可以,是因为这么写,语言数组的本质是就是指针加偏移量。初始化了一个指针,而且给了大小的内存空间,返回了一个指针。
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
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/16731.html
摘要:它对数组和对象使用按值传递,但这是在的共享传参或拷贝的引用中使用的按值传参。例如在这里,变量和值在执行期间存储在堆栈中。返回值这是可选的,函数可以返回值,也可以不返回值。变量被推入堆栈,从而在执行时成为的副本。 这是专门探索 JavaScript 及其所构建的组件的系列文章的第 22 篇。 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 如果你错过了前面的章节,可...
摘要:它对数组和对象使用按值传递,但这是在的共享传参或拷贝的引用中使用的按值传参。例如在这里,变量和值在执行期间存储在堆栈中。返回值这是可选的,函数可以返回值,也可以不返回值。变量被推入堆栈,从而在执行时成为的副本。 这是专门探索 JavaScript 及其所构建的组件的系列文章的第 22 篇。 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 如果你错过了前面的章节,可...
摘要:本文引自我的博文加壳通用内存加载方案分析来源反逆向解决方案梆梆加固原理探寻作者该篇博文中的如何使加载加密的文件这部分。 本文引自我的博文 APK加壳【3】通用内存加载dex方案分析 来源 Andorid APK反逆向解决方案:梆梆加固原理探寻 CSDN 作者Jack_Jia 该篇博文中的:3. 如何使DexClassLoader加载加密的dex文件? 这部分。 方案 上...
摘要:为安全起见,要求在使用传值时,变量必须是已经被初始化的。你可以安全的使用全局或静态变量的指针的参数,比如上下文参数。本文首发地址在中使用语言的指针 Objective-C和C语言经常需要使用到指针。Swift中的数据类型由于良好的设计,使其可以和基于指针的C语言API无缝混用。同时Swift也可以自动处理大多数将指针作为参数的情况。在这篇文章里,我们可以看到在Swift语言中如何将变量...
阅读 1594·2021-09-14 18:03
阅读 2142·2019-08-30 15:48
阅读 1004·2019-08-30 14:09
阅读 325·2019-08-30 12:55
阅读 2601·2019-08-29 11:29
阅读 1335·2019-08-26 13:43
阅读 2200·2019-08-26 13:30
阅读 2254·2019-08-26 12:17