LKS_TraceManager.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. //
  2. // LKS_TraceManager.m
  3. // LookinServer
  4. //
  5. // Created by Li Kai on 2019/5/5.
  6. // https://lookin.work
  7. //
  8. #import "LKS_TraceManager.h"
  9. #import <Objc/runtime.h>
  10. #import "LookinIvarTrace.h"
  11. #import "LookinServerDefines.h"
  12. #import "LKS_LocalInspectManager.h"
  13. @implementation LKS_TraceManager
  14. + (instancetype)sharedInstance {
  15. static dispatch_once_t onceToken;
  16. static LKS_TraceManager *instance = nil;
  17. dispatch_once(&onceToken,^{
  18. instance = [[super allocWithZone:NULL] init];
  19. });
  20. return instance;
  21. }
  22. + (id)allocWithZone:(struct _NSZone *)zone {
  23. return [self sharedInstance];
  24. }
  25. - (void)reload {
  26. // 把旧的先都清理掉
  27. [NSObject lks_clearAllObjectsTraces];
  28. [[[UIApplication sharedApplication].windows copy] enumerateObjectsUsingBlock:^(__kindof UIWindow * _Nonnull window, NSUInteger idx, BOOL * _Nonnull stop) {
  29. [self _addTraceForLayersRootedByLayer:window.layer];
  30. }];
  31. }
  32. - (void)_addTraceForLayersRootedByLayer:(CALayer *)layer {
  33. UIView *view = layer.lks_hostView;
  34. if ([view.superview lks_isChildrenViewOfTabBar]) {
  35. view.lks_isChildrenViewOfTabBar = YES;
  36. } else if ([view isKindOfClass:[UITabBar class]]) {
  37. view.lks_isChildrenViewOfTabBar = YES;
  38. }
  39. if (view) {
  40. [self _markIVarsInAllClassLevelsOfObject:view];
  41. if (view.lks_hostViewController) {
  42. [self _markIVarsInAllClassLevelsOfObject:view.lks_hostViewController];
  43. }
  44. [self _buildSpecialTraceForView:view];
  45. } else {
  46. [self _markIVarsInAllClassLevelsOfObject:layer];
  47. }
  48. [[layer.sublayers copy] enumerateObjectsUsingBlock:^(__kindof CALayer * _Nonnull sublayer, NSUInteger idx, BOOL * _Nonnull stop) {
  49. [self _addTraceForLayersRootedByLayer:sublayer];
  50. }];
  51. }
  52. - (void)_buildSpecialTraceForView:(UIView *)view {
  53. if (view.lks_hostViewController) {
  54. view.lks_specialTrace = [NSString stringWithFormat:@"%@.view", NSStringFromClass(view.lks_hostViewController.class)];
  55. } else if ([view isKindOfClass:[UIWindow class]]) {
  56. CGFloat currentWindowLevel = ((UIWindow *)view).windowLevel;
  57. if ([view isKindOfClass:[LKS_LocalInspectContainerWindow class]]) {
  58. view.lks_specialTrace = [NSString stringWithFormat:@"Lookin Private Window ( Level: %@ )", @(currentWindowLevel)];
  59. } else if (((UIWindow *)view).isKeyWindow) {
  60. view.lks_specialTrace = [NSString stringWithFormat:@"KeyWindow ( Level: %@ )", @(currentWindowLevel)];
  61. } else {
  62. view.lks_specialTrace = [NSString stringWithFormat:@"WindowLevel: %@", @(currentWindowLevel)];
  63. }
  64. } else if ([view isKindOfClass:[UITableViewCell class]]) {
  65. ((UITableViewCell *)view).backgroundView.lks_specialTrace = @"cell.backgroundView";
  66. ((UITableViewCell *)view).accessoryView.lks_specialTrace = @"cell.accessoryView";
  67. } else if ([view isKindOfClass:[UITableView class]]) {
  68. UITableView *tableView = (UITableView *)view;
  69. NSMutableArray<NSNumber *> *relatedSectionIdx = [NSMutableArray array];
  70. [[tableView visibleCells] enumerateObjectsUsingBlock:^(__kindof UITableViewCell * _Nonnull cell, NSUInteger idx, BOOL * _Nonnull stop) {
  71. NSIndexPath *indexPath = [tableView indexPathForCell:cell];
  72. cell.lks_specialTrace = [NSString stringWithFormat:@"{ sec:%@, row:%@ }", @(indexPath.section), @(indexPath.row)];
  73. if (![relatedSectionIdx containsObject:@(indexPath.section)]) {
  74. [relatedSectionIdx addObject:@(indexPath.section)];
  75. }
  76. }];
  77. [relatedSectionIdx enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  78. NSUInteger secIdx = [obj unsignedIntegerValue];
  79. UIView *secHeaderView = [tableView headerViewForSection:secIdx];
  80. secHeaderView.lks_specialTrace = [NSString stringWithFormat:@"sectionHeader { sec: %@ }", @(secIdx)];
  81. UIView *secFooterView = [tableView footerViewForSection:secIdx];
  82. secFooterView.lks_specialTrace = [NSString stringWithFormat:@"sectionFooter { sec: %@ }", @(secIdx)];
  83. }];
  84. } else if ([view isKindOfClass:[UICollectionView class]]) {
  85. UICollectionView *collectionView = (UICollectionView *)view;
  86. collectionView.backgroundView.lks_specialTrace = @"collectionView.backgroundView";
  87. if (@available(iOS 9.0, *)) {
  88. [[collectionView indexPathsForVisibleSupplementaryElementsOfKind:UICollectionElementKindSectionHeader] enumerateObjectsUsingBlock:^(NSIndexPath * _Nonnull indexPath, NSUInteger idx, BOOL * _Nonnull stop) {
  89. UIView *headerView = [collectionView supplementaryViewForElementKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
  90. headerView.lks_specialTrace = [NSString stringWithFormat:@"sectionHeader { sec:%@ }", @(indexPath.section)];
  91. }];
  92. [[collectionView indexPathsForVisibleSupplementaryElementsOfKind:UICollectionElementKindSectionFooter] enumerateObjectsUsingBlock:^(NSIndexPath * _Nonnull indexPath, NSUInteger idx, BOOL * _Nonnull stop) {
  93. UIView *footerView = [collectionView supplementaryViewForElementKind:UICollectionElementKindSectionFooter atIndexPath:indexPath];
  94. footerView.lks_specialTrace = [NSString stringWithFormat:@"sectionFooter { sec:%@ }", @(indexPath.section)];
  95. }];
  96. }
  97. [[collectionView visibleCells] enumerateObjectsUsingBlock:^(__kindof UICollectionViewCell * _Nonnull cell, NSUInteger idx, BOOL * _Nonnull stop) {
  98. NSIndexPath *indexPath = [collectionView indexPathForCell:cell];
  99. cell.lks_specialTrace = [NSString stringWithFormat:@"{ item:%@, sec:%@ }", @(indexPath.item), @(indexPath.section)];
  100. }];
  101. } else if ([view isKindOfClass:[UITableViewHeaderFooterView class]]) {
  102. UITableViewHeaderFooterView *headerFooterView = (UITableViewHeaderFooterView *)view;
  103. headerFooterView.textLabel.lks_specialTrace = @"sectionHeaderFooter.textLabel";
  104. headerFooterView.detailTextLabel.lks_specialTrace = @"sectionHeaderFooter.detailTextLabel";
  105. }
  106. }
  107. - (void)_markIVarsInAllClassLevelsOfObject:(NSObject *)object {
  108. [self _markIVarsOfObject:object class:object.class];
  109. }
  110. - (void)_markIVarsOfObject:(NSObject *)hostObject class:(Class)targetClass {
  111. if (!targetClass) {
  112. return;
  113. }
  114. NSArray<NSString *> *prefixesToTerminateRecursion = @[@"NSObject", @"UIResponder", @"UIButton", @"UIButtonLabel"];
  115. BOOL hasPrefix = [prefixesToTerminateRecursion lookin_any:^BOOL(NSString *prefix) {
  116. return [NSStringFromClass(targetClass) hasPrefix:prefix];
  117. }];
  118. if (hasPrefix) {
  119. return;
  120. }
  121. unsigned int outCount = 0;
  122. Ivar *ivars = class_copyIvarList(targetClass, &outCount);
  123. for (unsigned int i = 0; i < outCount; i ++) {
  124. Ivar ivar = ivars[i];
  125. NSString *ivarType = [[NSString alloc] lookin_safeInitWithUTF8String:ivar_getTypeEncoding(ivar)];
  126. if (![ivarType hasPrefix:@"@"] || ivarType.length <= 3) {
  127. continue;
  128. }
  129. NSString *ivarClassName = [ivarType substringWithRange:NSMakeRange(2, ivarType.length - 3)];
  130. Class ivarClass = NSClassFromString(ivarClassName);
  131. if (![ivarClass isSubclassOfClass:[UIView class]]
  132. && ![ivarClass isSubclassOfClass:[CALayer class]]
  133. && ![ivarClass isSubclassOfClass:[UIViewController class]]
  134. && ![ivarClass isSubclassOfClass:[UIGestureRecognizer class]]) {
  135. continue;
  136. }
  137. const char * ivarNameChar = ivar_getName(ivar);
  138. if (!ivarNameChar) {
  139. continue;
  140. }
  141. // 这个 ivarObject 可能的类型:UIView, CALayer, UIViewController, UIGestureRecognizer
  142. NSObject *ivarObject = object_getIvar(hostObject, ivar);
  143. if (!ivarObject) {
  144. continue;
  145. }
  146. LookinIvarTrace *ivarTrace = [LookinIvarTrace new];
  147. ivarTrace.hostObject = hostObject;
  148. ivarTrace.hostClassName = NSStringFromClass(targetClass);
  149. ivarTrace.ivarName = [[NSString alloc] lookin_safeInitWithUTF8String:ivarNameChar];
  150. if (hostObject == ivarObject) {
  151. ivarTrace.relation = LookinIvarTraceRelationValue_Self;
  152. } else if ([hostObject isKindOfClass:[UIView class]]) {
  153. CALayer *ivarLayer = nil;
  154. if ([ivarObject isKindOfClass:[CALayer class]]) {
  155. ivarLayer = (CALayer *)ivarObject;
  156. } else if ([ivarObject isKindOfClass:[UIView class]]) {
  157. ivarLayer = ((UIView *)ivarObject).layer;
  158. }
  159. if (ivarLayer && (ivarLayer.superlayer == ((UIView *)hostObject).layer)) {
  160. ivarTrace.relation = @"superview";
  161. }
  162. }
  163. if ([LKS_InvalidIvarTraces() containsObject:ivarTrace]) {
  164. continue;
  165. }
  166. if (!ivarObject.lks_ivarTraces) {
  167. ivarObject.lks_ivarTraces = [NSArray array];
  168. }
  169. if (![ivarObject.lks_ivarTraces containsObject:ivarTrace]) {
  170. ivarObject.lks_ivarTraces = [ivarObject.lks_ivarTraces arrayByAddingObject:ivarTrace];
  171. }
  172. }
  173. free(ivars);
  174. Class superClass = [targetClass superclass];
  175. [self _markIVarsOfObject:hostObject class:superClass];
  176. }
  177. static NSSet<LookinIvarTrace *> *LKS_InvalidIvarTraces() {
  178. static NSSet *list;
  179. static dispatch_once_t onceToken;
  180. dispatch_once(&onceToken, ^{
  181. NSMutableSet *set = [NSMutableSet set];
  182. [set addObject:({
  183. LookinIvarTrace *trace = [LookinIvarTrace new];
  184. trace.hostClassName = @"UIView";
  185. trace.ivarName = @"_window";
  186. trace;
  187. })];
  188. [set addObject:({
  189. LookinIvarTrace *trace = [LookinIvarTrace new];
  190. trace.hostClassName = @"UIViewController";
  191. trace.ivarName = @"_view";
  192. trace;
  193. })];
  194. [set addObject:({
  195. LookinIvarTrace *trace = [LookinIvarTrace new];
  196. trace.hostClassName = @"UIView";
  197. trace.ivarName = @"_viewDelegate";
  198. trace;
  199. })];
  200. [set addObject:({
  201. LookinIvarTrace *trace = [LookinIvarTrace new];
  202. trace.hostClassName = @"UIViewController";
  203. trace.ivarName = @"_parentViewController";
  204. trace;
  205. })];
  206. list = set.copy;
  207. });
  208. return list;
  209. }
  210. @end