FLEXObjectExplorer.m 6.9 KB

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