FLEXObjectExplorer.m 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. //
  2. // FLEXObjectExplorer.m
  3. // FLEX
  4. //
  5. // Created by Tanner Bennett on 8/28/19.
  6. // Copyright © 2019 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXObjectExplorer.h"
  9. #import "FLEXUtility.h"
  10. #import "FLEXRuntimeUtility.h"
  11. #import "NSObject+Reflection.h"
  12. #import "FLEXRuntime+Compare.h"
  13. #import "FLEXRuntime+UIKitHelpers.h"
  14. #import "FLEXPropertyAttributes.h"
  15. #import "NSObject+Reflection.h"
  16. #import "FLEXMetadataSection.h"
  17. @interface FLEXObjectExplorer () {
  18. NSMutableArray<NSArray<FLEXProperty *> *> *_allProperties;
  19. NSMutableArray<NSArray<FLEXProperty *> *> *_allClassProperties;
  20. NSMutableArray<NSArray<FLEXIvar *> *> *_allIvars;
  21. NSMutableArray<NSArray<FLEXMethod *> *> *_allMethods;
  22. NSMutableArray<NSArray<FLEXMethod *> *> *_allClassMethods;
  23. NSMutableArray<NSArray<FLEXProtocol *> *> *_allConformedProtocols;
  24. }
  25. @end
  26. @implementation FLEXObjectExplorer
  27. #pragma mark - Initialization
  28. + (id)forObject:(id)objectOrClass
  29. {
  30. return [[self alloc] initWithObject:objectOrClass];
  31. }
  32. - (id)initWithObject:(id)objectOrClass
  33. {
  34. NSParameterAssert(objectOrClass);
  35. self = [super init];
  36. if (self) {
  37. _object = objectOrClass;
  38. _objectIsInstance = !object_isClass(objectOrClass);
  39. [self reloadMetadata];
  40. }
  41. return self;
  42. }
  43. #pragma mark - Public
  44. - (NSString *)objectDescription {
  45. // Hard-code UIColor description
  46. if ([self.object isKindOfClass:[UIColor class]]) {
  47. CGFloat h, s, l, r, g, b, a;
  48. [self.object getRed:&r green:&g blue:&b alpha:&a];
  49. [self.object getHue:&h saturation:&s brightness:&l alpha:nil];
  50. return [NSString stringWithFormat:
  51. @"HSL: (%.3f, %.3f, %.3f)\nRGB: (%.3f, %.3f, %.3f)\nAlpha: %.3f",
  52. h, s, l, r, g, b, a
  53. ];
  54. }
  55. NSString *description = [FLEXRuntimeUtility safeDescriptionForObject:self.object];
  56. if (!description.length) {
  57. NSString *address = [FLEXUtility addressOfObject:self.object];
  58. return [NSString stringWithFormat:@"Object at %@ returned empty description", address];
  59. }
  60. return description;
  61. }
  62. - (void)setClassScope:(NSInteger)classScope {
  63. _classScope = classScope;
  64. [self reloadScopedMetadata];
  65. }
  66. - (void)reloadMetadata {
  67. _allProperties = [NSMutableArray new];
  68. _allClassProperties = [NSMutableArray new];
  69. _allIvars = [NSMutableArray new];
  70. _allMethods = [NSMutableArray new];
  71. _allClassMethods = [NSMutableArray new];
  72. _allConformedProtocols = [NSMutableArray new];
  73. [self reloadClassHierarchy];
  74. // Loop over each class and each superclass, collect
  75. // the fresh and unique metadata in each category
  76. Class superclass = nil;
  77. NSInteger count = self.classHierarchy.count;
  78. NSInteger rootIdx = count - 1;
  79. for (NSInteger i = 0; i < count; i++) {
  80. Class cls = self.classHierarchy[i];
  81. superclass = (i < rootIdx) ? self.classHierarchy[i+1] : nil;
  82. [_allProperties addObject:[self
  83. metadataUniquedByName:[cls flex_allInstanceProperties]
  84. superclass:superclass
  85. kind:FLEXMetadataKindProperties
  86. ]];
  87. [_allClassProperties addObject:[self
  88. metadataUniquedByName:[cls flex_allClassProperties]
  89. superclass:superclass
  90. kind:FLEXMetadataKindClassProperties
  91. ]];
  92. [_allIvars addObject:[self
  93. metadataUniquedByName:[cls flex_allIvars]
  94. superclass:nil
  95. kind:FLEXMetadataKindIvars
  96. ]];
  97. [_allMethods addObject:[self
  98. metadataUniquedByName:[cls flex_allInstanceMethods]
  99. superclass:superclass
  100. kind:FLEXMetadataKindMethods
  101. ]];
  102. [_allClassMethods addObject:[self
  103. metadataUniquedByName:[cls flex_allClassMethods]
  104. superclass:superclass
  105. kind:FLEXMetadataKindClassMethods
  106. ]];
  107. [_allConformedProtocols addObject:[self
  108. metadataUniquedByName:[cls flex_protocols]
  109. superclass:superclass
  110. kind:FLEXMetadataKindProtocols
  111. ]];
  112. }
  113. // Set up UIKit helper data
  114. // Really, we only need to call this on properties and ivars
  115. // because no other metadata types support editing.
  116. for (NSArray *matrix in @[_allProperties, _allIvars, /* _allMethods, _allClassMethods, _allConformedProtocols */]) {
  117. for (NSArray *metadataByClass in matrix) {
  118. for (id<FLEXRuntimeMetadata> metadata in metadataByClass) {
  119. metadata.tag = metadata.isEditable ? @YES : nil;
  120. }
  121. }
  122. }
  123. [self reloadScopedMetadata];
  124. }
  125. #pragma mark - Private
  126. - (void)reloadScopedMetadata {
  127. _properties = self.allProperties[self.classScope];
  128. _classProperties = self.allClassProperties[self.classScope];
  129. _ivars = self.allIvars[self.classScope];
  130. _methods = self.allMethods[self.classScope];
  131. _classMethods = self.allClassMethods[self.classScope];
  132. _conformedProtocols = self.allConformedProtocols[self.classScope];
  133. }
  134. /// Accepts an array of flex metadata objects and discards objects
  135. /// with duplicate names, as well as properties and methods which
  136. /// aren't "new" (i.e. those which the superclass responds to)
  137. - (NSArray *)metadataUniquedByName:(NSArray *)list superclass:(Class)superclass kind:(FLEXMetadataKind)kind {
  138. // Remove items with same name and return filtered list
  139. NSMutableSet *names = [NSMutableSet new];
  140. return [list flex_filtered:^BOOL(id obj, NSUInteger idx) {
  141. NSString *name = [obj name];
  142. if ([names containsObject:name]) {
  143. return NO;
  144. } else {
  145. [names addObject:name];
  146. // Skip methods and properties which are just overrides
  147. switch (kind) {
  148. case FLEXMetadataKindProperties:
  149. if ([superclass instancesRespondToSelector:[obj likelyGetter]]) {
  150. return NO;
  151. }
  152. break;
  153. case FLEXMetadataKindClassProperties:
  154. if ([superclass respondsToSelector:[obj likelyGetter]]) {
  155. return NO;
  156. }
  157. break;
  158. case FLEXMetadataKindMethods:
  159. if ([superclass instancesRespondToSelector:NSSelectorFromString(name)]) {
  160. return NO;
  161. }
  162. break;
  163. case FLEXMetadataKindClassMethods:
  164. if ([superclass respondsToSelector:NSSelectorFromString(name)]) {
  165. return NO;
  166. }
  167. break;
  168. case FLEXMetadataKindProtocols:
  169. return YES; // Protocols are already uniqued
  170. break;
  171. // Ivars cannot be overidden
  172. case FLEXMetadataKindIvars: break;
  173. }
  174. return YES;
  175. }
  176. }];
  177. }
  178. #pragma mark - Superclasses
  179. - (void)reloadClassHierarchy {
  180. // The class hierarchy will never contain metaclass objects by this logic;
  181. // it is always the same for a given class and instances of it
  182. _classHierarchy = [[self.object class] flex_classHierarchy];
  183. }
  184. @end