从JSONModel看Objective-C的反射机制

前端之家收集整理的这篇文章主要介绍了从JSONModel看Objective-C的反射机制前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

移动互联时代,JSON作为一种数据传输格式几乎随处可见。作为iOS开发者,收到一串JSON字符串要怎么处理?我想多数情况下是需要将它转成自定义的NSObject对象再使用,对于这个转换的过程,大部分人是这么做的:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  1. NSDictionary* json = (fetch from Internet) ...
  2. User* user=[[User alloc] init];
  3. user.userId =[json objectForKey:@"userId"];
  4. user.nick= [json objectForKey:@"nick"];
  5. user.image = [json objectForKey:@"image"];
  6. user.age = [json objectForKey:@"age"];
  7. ...

这样的代码没错,但也绝对说不上优雅,model里面的属性越多,冗余的代码量也相应越多。对于这个问题,自然是有更好的解决方案,比如说这样:

  1. 1
  2. 2
  1. NSError* err = nil;
  2. User* user = [[User alloc] initWithDictionary:json error:&err];

两行代码足矣,当然,实现这个功能,实际上是把很多背后的工作交给JSONModel这个开源包去做了。至于其实现原理,则主要是基于Objective-C Runtime的反射机制。

关于反射

《Thinking in Java》中将反射称解释为运行时的类信息,说白了就是这个类信息在编译的时候是未知的,需要在程序运行期间动态获取,而这正是我们之前试图去解决的问题。对于从网络上获取到的一段JSON字符串,在代码编译期间当然是无法知晓的。虽然这里说的是Java语言,但是对于Objective-C,这种反射机制也是同样支持的。

JSONModel中的实现

打断点记录了下JSONModel这个类中的方法调用顺序如下:

调用顺序

对象属性获取则主要在最后一个inspectProperties方法

以下是inspectProperties方法代码片段:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  1. ···
  2.  
  3. //inspects the class,get's a list of the class properties
  4. -(void)__inspectProperties
  5. {
  6. //JMLog(@"Inspect class: %@",[self class]);
  7. NSMutableDictionary* propertyIndex = [NSMutableDictionary dictionary];
  8. //temp variables for the loops
  9. Class class = [self class];
  10. NSScanner* scanner = nil;
  11. NSString* propertyType = nil;
  12. // inspect inherited properties up to the JSONModel class
  13. while (class != [JSONModel class]) {
  14. //JMLog(@"inspecting: %@",NSStringFromClass(class));
  15. unsigned int propertyCount;
  16. objc_property_t *properties = class_copyPropertyList(class,&propertyCount);
  17. //loop over the class properties
  18. for (int i = 0; i < propertyCount; i++) {
  19.  
  20. JSONModelClassProperty* p = [[JSONModelClassProperty alloc] init];
  21.  
  22. //get property name
  23. objc_property_t property = properties[i];
  24. const char *propertyName = property_getName(property);
  25. p.name = [NSString stringWithUTF8String:propertyName];
  26. //JMLog(@"property: %@",p.name);
  27. //get property attributes
  28. char *attrs = property_getAttributes(property);
  29. NSString* propertyAttributes = [NSString stringWithUTF8String:attrs];
  30. if ([propertyAttributes hasPrefix:@"Tc,"]) {
  31. //mask BOOLs as structs so they can have custom convertors
  32. p.structName = @"BOOL";
  33. }
  34. scanner = [NSScanner scannerWithString: propertyAttributes];
  35. //JMLog(@"attr: %@",[NSString stringWithCString:attrs encoding:NSUTF8StringEncoding]);
  36. [scanner scanUpToString:@"T" intoString: nil];
  37. [scanner scanString:@"T" intoString:nil];
  38.  
  39. ···
  40.  
  41. //finally store the property index in the static property index
  42. objc_setAssociatedObject(self.class,&kClassPropertiesKey,[propertyIndex copy],OBJC_ASSOCIATION_RETAIN // This is atomic
  43. );

在这边可以看到基本步骤如下

  1. 通过调用自身的class方法获取当前类的元数据信息
  2. 通过runtime的 class_copyPropertyList 方法取得当前类的属性列表,以指针数组的形式返回
  3. 遍历指针数组,通过property_getName获取属性名,property_getAttributes获取属性类型
  4. 使用NSScanner来扫描属性类型字符串,将类似如下的形式"T@"NSNumber",&,N,V_id",处理成NSNumber,逐个属性循环处理
  5. 将所有处理好的数据放入propertyIndex这个字典中
  6. 通过objc_setAssociatedObject将这些数据关联到kClassPropertiesKey

使用时在properties方法中这样取出属性数据:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  1. //returns a list of the model's properties
  2. -(NSArray*)__properties__
  3. {
  4. //fetch the associated object
  5. NSDictionary* classProperties = objc_getAssociatedObject(if (classProperties) return [classProperties allValues];
  6.  
  7. //if here,the class needs to inspect itself
  8. [self __setup__];
  9. //return the property list
  10. classProperties = objc_getAssociatedObject(return [classProperties allValues];
  11. }

以上就是JSONModel中使用反射机制实现的类属性获取过程,相比常见的逐个取值赋值的方式,这种方法代码上的确简洁优雅了很多,特别是对于使用的类属性比较复杂的情况,免除了很多不必要的代码

猜你在找的Json相关文章