HINSPDebug.m 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. //
  2. // RMDebug.m
  3. //
  4. //
  5. // Created by Christian Menschel on 16.08.14.
  6. // Copyright (c) 2014 tapwork. All rights reserved.
  7. //
  8. #import "HINSPDebug.h"
  9. #import "HINSPDebugWindow.h"
  10. #import "NSObject+HeapInspector.h"
  11. #import <objc/objc.h>
  12. #import <objc/runtime.h>
  13. #import "HINSPHeapStackInspector.h"
  14. #import "HINSPHeapStackTableViewController.h"
  15. static HINSPDebug *twDebug = nil;
  16. @implementation HINSPDebug
  17. {
  18. HINSPDebugWindow *_window;
  19. UIViewController *_rootViewController;
  20. NSSet *_recordedHeap;
  21. }
  22. #pragma mark - View Life Cycle
  23. - (instancetype)init
  24. {
  25. self = [super init];
  26. if (self) {
  27. [NSObject startSwizzle];
  28. [[self class] recordBacktraces:YES];
  29. CGRect rect = [UIScreen mainScreen].bounds;
  30. HINSPDebugWindow *window = [[HINSPDebugWindow alloc] initWithFrame:rect];
  31. [window setHidden:NO];
  32. window.windowLevel = UIWindowLevelStatusBar + 50;
  33. [window.recordButton addTarget:self
  34. action:@selector(recordButtonTapped:)
  35. forControlEvents:UIControlEventTouchUpInside];
  36. [window.recordedButton addTarget:self
  37. action:@selector(recordedHeapButtonTapped:)
  38. forControlEvents:UIControlEventTouchUpInside];
  39. [window.activeButton addTarget:self
  40. action:@selector(currentHeapButtonTapped:)
  41. forControlEvents:UIControlEventTouchUpInside];
  42. _window = window;
  43. // Create a blank rootViewController
  44. UIViewController *rootViewController = [[UIViewController alloc] init];
  45. rootViewController.view.alpha = 0.0;
  46. _rootViewController = rootViewController;
  47. _window.rootViewController = rootViewController;
  48. }
  49. return self;
  50. }
  51. - (void)showInfoLabel
  52. {
  53. NSUInteger count = [_recordedHeap count];
  54. NSString *text = [NSString stringWithFormat:@"Objects alive: %lu",
  55. (unsigned long)count];
  56. _window.infoLabel.text = text;
  57. if (count > 0) {
  58. _window.recordedButton.hidden = NO;
  59. }
  60. }
  61. - (void)resetInfoLabel
  62. {
  63. _window.infoLabel.text = nil;
  64. _window.recordedButton.hidden = YES;
  65. }
  66. #pragma mark - Actions
  67. - (void)currentHeapButtonTapped:(id)sender
  68. {
  69. NSArray *stack = [[HINSPHeapStackInspector heap] allObjects];
  70. if ([stack count] > 0) {
  71. HINSPHeapStackTableViewController *controller = [self heapStackControllerWithHeapStack:stack];
  72. controller.title = @"Heap";
  73. }
  74. }
  75. - (void)recordedHeapButtonTapped:(id)sender
  76. {
  77. if ([_recordedHeap count] > 0) {
  78. [NSObject endSnapshot];
  79. NSArray *stack = [_recordedHeap allObjects];
  80. HINSPHeapStackTableViewController *controller = [self heapStackControllerWithHeapStack:stack];
  81. controller.title = @"Recorded Heap";
  82. }
  83. }
  84. - (void)recordButtonTapped:(id)sender
  85. {
  86. if ([NSObject isSnapshotRecording]) {
  87. [self stopRecord];
  88. } else {
  89. [self beginRecord];
  90. }
  91. }
  92. #pragma mark - Private methods
  93. - (HINSPHeapStackTableViewController *)heapStackControllerWithHeapStack:(NSArray *)stack
  94. {
  95. HINSPHeapStackTableViewController *tv = [[HINSPHeapStackTableViewController alloc] initWithDataSource:stack];
  96. UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:tv];
  97. [_rootViewController presentViewController:navController animated:YES completion:nil];
  98. return tv;
  99. }
  100. - (void)stopRecord
  101. {
  102. _window.recordButton.isRecording = NO;
  103. _recordedHeap = [HINSPHeapStackInspector recordedHeap];
  104. [self showInfoLabel];
  105. [NSObject endSnapshot];
  106. }
  107. - (void)beginRecord
  108. {
  109. _recordedHeap = nil;
  110. [self resetInfoLabel];
  111. [NSObject beginSnapshot];
  112. _window.recordButton.isRecording = YES;
  113. [HINSPHeapStackInspector performHeapShot];
  114. }
  115. #pragma mark - Public methods
  116. + (void)start
  117. {
  118. twDebug = [[HINSPDebug alloc] init];
  119. }
  120. + (void)startRecord
  121. {
  122. if (!twDebug) {
  123. [self start];
  124. }
  125. [twDebug beginRecord];
  126. }
  127. + (void)stop
  128. {
  129. [NSObject endSnapshot];
  130. [NSObject removeAllClassPrefixesToRecord];
  131. [HINSPHeapStackInspector reset];
  132. twDebug = nil;
  133. }
  134. + (void)stopRecord {
  135. [twDebug stopRecord];
  136. }
  137. + (void)addClassPrefixesToRecord:(NSArray <NSString *> *)classPrefixes
  138. {
  139. if (classPrefixes) {
  140. [NSObject addClassPrefixesToRecord:classPrefixes];
  141. }
  142. }
  143. + (void)addSwiftModulesToRecord:(NSArray <NSString *> *)swiftModules
  144. {
  145. if (swiftModules) {
  146. NSMutableArray *modulesWithPrefix = [NSMutableArray array];
  147. for (NSString *swiftModule in swiftModules) {
  148. NSString *prefixed = [NSString stringWithFormat:@"%@.",swiftModule];
  149. [modulesWithPrefix addObject:prefixed];
  150. }
  151. [NSObject addClassPrefixesToRecord:[modulesWithPrefix copy]];
  152. }
  153. }
  154. + (void)recordBacktraces:(BOOL)recordBacktraces
  155. {
  156. [NSObject setRecordBacktrace:recordBacktraces];
  157. }
  158. @end