FLEXObjectExplorer.m 6.9 KB

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