FLEXObjectExplorer.m 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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. NSMutableArray<FLEXStaticMetadata *> *_allInstanceSizes;
  25. NSMutableArray<FLEXStaticMetadata *> *_allImageNames;
  26. }
  27. @end
  28. @implementation FLEXObjectExplorer
  29. #pragma mark - Initialization
  30. + (id)forObject:(id)objectOrClass
  31. {
  32. return [[self alloc] initWithObject:objectOrClass];
  33. }
  34. - (id)initWithObject:(id)objectOrClass
  35. {
  36. NSParameterAssert(objectOrClass);
  37. self = [super init];
  38. if (self) {
  39. _object = objectOrClass;
  40. _objectIsInstance = !object_isClass(objectOrClass);
  41. [self reloadMetadata];
  42. }
  43. return self;
  44. }
  45. #pragma mark - Public
  46. - (NSString *)objectDescription {
  47. // Hard-code UIColor description
  48. if ([self.object isKindOfClass:[UIColor class]]) {
  49. CGFloat h, s, l, r, g, b, a;
  50. [self.object getRed:&r green:&g blue:&b alpha:&a];
  51. [self.object getHue:&h saturation:&s brightness:&l alpha:nil];
  52. return [NSString stringWithFormat:
  53. @"HSL: (%.3f, %.3f, %.3f)\nRGB: (%.3f, %.3f, %.3f)\nAlpha: %.3f",
  54. h, s, l, r, g, b, a
  55. ];
  56. }
  57. NSString *description = [FLEXRuntimeUtility safeDescriptionForObject:self.object];
  58. if (!description.length) {
  59. NSString *address = [FLEXUtility addressOfObject:self.object];
  60. return [NSString stringWithFormat:@"Object at %@ returned empty description", address];
  61. }
  62. return description;
  63. }
  64. - (void)setClassScope:(NSInteger)classScope {
  65. _classScope = classScope;
  66. [self reloadScopedMetadata];
  67. }
  68. - (void)reloadMetadata {
  69. _allProperties = [NSMutableArray new];
  70. _allClassProperties = [NSMutableArray new];
  71. _allIvars = [NSMutableArray new];
  72. _allMethods = [NSMutableArray new];
  73. _allClassMethods = [NSMutableArray new];
  74. _allConformedProtocols = [NSMutableArray new];
  75. _allInstanceSizes = [NSMutableArray new];
  76. _allImageNames = [NSMutableArray new];
  77. [self reloadClassHierarchy];
  78. // Loop over each class and each superclass, collect
  79. // the fresh and unique metadata in each category
  80. Class superclass = nil;
  81. NSInteger count = self.classHierarchyClasses.count;
  82. NSInteger rootIdx = count - 1;
  83. for (NSInteger i = 0; i < count; i++) {
  84. Class cls = self.classHierarchyClasses[i];
  85. superclass = (i < rootIdx) ? self.classHierarchyClasses[i+1] : nil;
  86. [_allProperties addObject:[self
  87. metadataUniquedByName:[cls flex_allInstanceProperties]
  88. superclass:superclass
  89. kind:FLEXMetadataKindProperties
  90. ]];
  91. [_allClassProperties addObject:[self
  92. metadataUniquedByName:[cls flex_allClassProperties]
  93. superclass:superclass
  94. kind:FLEXMetadataKindClassProperties
  95. ]];
  96. [_allIvars addObject:[self
  97. metadataUniquedByName:[cls flex_allIvars]
  98. superclass:nil
  99. kind:FLEXMetadataKindIvars
  100. ]];
  101. [_allMethods addObject:[self
  102. metadataUniquedByName:[cls flex_allInstanceMethods]
  103. superclass:superclass
  104. kind:FLEXMetadataKindMethods
  105. ]];
  106. [_allClassMethods addObject:[self
  107. metadataUniquedByName:[cls flex_allClassMethods]
  108. superclass:superclass
  109. kind:FLEXMetadataKindClassMethods
  110. ]];
  111. [_allConformedProtocols addObject:[self
  112. metadataUniquedByName:[cls flex_protocols]
  113. superclass:superclass
  114. kind:FLEXMetadataKindProtocols
  115. ]];
  116. // TODO: join instance size, image name, and class hierarchy into a single model object
  117. // This would greatly reduce the laziness that has begun to manifest itself here
  118. [_allInstanceSizes addObject:[FLEXStaticMetadata
  119. style:FLEXStaticMetadataRowStyleKeyValue
  120. title:@"Instance Size" number:@(class_getInstanceSize(cls))
  121. ]];
  122. [_allImageNames addObject:[FLEXStaticMetadata
  123. style:FLEXStaticMetadataRowStyleDefault
  124. title:@"Image Name" string:@(class_getImageName(cls))
  125. ]];
  126. }
  127. _classHierarchy = [FLEXStaticMetadata classHierarchy:self.classHierarchyClasses];
  128. // Set up UIKit helper data
  129. // Really, we only need to call this on properties and ivars
  130. // because no other metadata types support editing.
  131. for (NSArray *matrix in @[_allProperties, _allIvars, /* _allMethods, _allClassMethods, _allConformedProtocols */]) {
  132. for (NSArray *metadataByClass in matrix) {
  133. for (id<FLEXRuntimeMetadata> metadata in metadataByClass) {
  134. metadata.tag = metadata.isEditable ? @YES : nil;
  135. }
  136. }
  137. }
  138. [self reloadScopedMetadata];
  139. }
  140. #pragma mark - Private
  141. - (void)reloadScopedMetadata {
  142. _properties = self.allProperties[self.classScope];
  143. _classProperties = self.allClassProperties[self.classScope];
  144. _ivars = self.allIvars[self.classScope];
  145. _methods = self.allMethods[self.classScope];
  146. _classMethods = self.allClassMethods[self.classScope];
  147. _conformedProtocols = self.allConformedProtocols[self.classScope];
  148. _instanceSize = self.allInstanceSizes[self.classScope];
  149. _imageName = self.allImageNames[self.classScope];
  150. }
  151. /// Accepts an array of flex metadata objects and discards objects
  152. /// with duplicate names, as well as properties and methods which
  153. /// aren't "new" (i.e. those which the superclass responds to)
  154. - (NSArray *)metadataUniquedByName:(NSArray *)list superclass:(Class)superclass kind:(FLEXMetadataKind)kind {
  155. // Remove items with same name and return filtered list
  156. NSMutableSet *names = [NSMutableSet new];
  157. return [list flex_filtered:^BOOL(id obj, NSUInteger idx) {
  158. NSString *name = [obj name];
  159. if ([names containsObject:name]) {
  160. return NO;
  161. } else {
  162. [names addObject:name];
  163. // Skip methods and properties which are just overrides
  164. switch (kind) {
  165. case FLEXMetadataKindProperties:
  166. if ([superclass instancesRespondToSelector:[obj likelyGetter]]) {
  167. return NO;
  168. }
  169. break;
  170. case FLEXMetadataKindClassProperties:
  171. if ([superclass respondsToSelector:[obj likelyGetter]]) {
  172. return NO;
  173. }
  174. break;
  175. case FLEXMetadataKindMethods:
  176. if ([superclass instancesRespondToSelector:NSSelectorFromString(name)]) {
  177. return NO;
  178. }
  179. break;
  180. case FLEXMetadataKindClassMethods:
  181. if ([superclass respondsToSelector:NSSelectorFromString(name)]) {
  182. return NO;
  183. }
  184. break;
  185. case FLEXMetadataKindProtocols:
  186. case FLEXMetadataKindClassHierarchy:
  187. case FLEXMetadataKindOther:
  188. return YES; // These types are already uniqued
  189. break;
  190. // Ivars cannot be overidden
  191. case FLEXMetadataKindIvars: break;
  192. }
  193. return YES;
  194. }
  195. }];
  196. }
  197. #pragma mark - Superclasses
  198. - (void)reloadClassHierarchy {
  199. // The class hierarchy will never contain metaclass objects by this logic;
  200. // it is always the same for a given class and instances of it
  201. _classHierarchyClasses = [[self.object class] flex_classHierarchy];
  202. }
  203. @end