LKS_TraceManager.m 12 KB

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