LKS_TraceManager.m 11 KB

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