diff --git a/HeapInspector/HINSPDebug.h b/HeapInspector/HINSPDebug.h index a3bf30d..f6bb3e4 100644 --- a/HeapInspector/HINSPDebug.h +++ b/HeapInspector/HINSPDebug.h @@ -17,6 +17,12 @@ NS_ASSUME_NONNULL_BEGIN /// Stops the HeapInspector and removes the inspector's view + (void)stop; +// Shows HeapInspector (if not visible yet) and starts the record immediately ++ (void)startRecord; + +// Stops record - but does not hide the HeapInspector ++ (void)stopRecord; + /// Add some (or only one) class prefix like `UI` OR `MK` to record classes that match the prefix only. /// It's highly recommended to record a specific class or prefix - /// otherwise all Cocoa classes will be recorded, which slows down the performance. diff --git a/HeapInspector/HINSPDebug.m b/HeapInspector/HINSPDebug.m index 1016c81..c5e860d 100644 --- a/HeapInspector/HINSPDebug.m +++ b/HeapInspector/HINSPDebug.m @@ -118,6 +118,7 @@ - (HINSPHeapStackTableViewController *)heapStackControllerWithHeapStack:(NSArray - (void)stopRecord { + _window.recordButton.isRecording = NO; _recordedHeap = [HINSPHeapStackInspector recordedHeap]; [self showInfoLabel]; [NSObject endSnapshot]; @@ -128,6 +129,7 @@ - (void)beginRecord _recordedHeap = nil; [self resetInfoLabel]; [NSObject beginSnapshot]; + _window.recordButton.isRecording = YES; [HINSPHeapStackInspector performHeapShot]; } @@ -138,6 +140,14 @@ + (void)start twDebug = [[HINSPDebug alloc] init]; } ++ (void)startRecord +{ + if (!twDebug) { + [self start]; + } + [twDebug beginRecord]; +} + + (void)stop { [NSObject endSnapshot]; @@ -146,6 +156,10 @@ + (void)stop twDebug = nil; } ++ (void)stopRecord { + [twDebug stopRecord]; +} + + (void)addClassPrefixesToRecord:(NSArray *)classPrefixes { if (classPrefixes) { diff --git a/HeapInspector/HINSPHeapStackDetailTableViewController.m b/HeapInspector/HINSPHeapStackDetailTableViewController.m index 119f478..71a85e7 100644 --- a/HeapInspector/HINSPHeapStackDetailTableViewController.m +++ b/HeapInspector/HINSPHeapStackDetailTableViewController.m @@ -104,7 +104,10 @@ - (void)updateHeaderView { if ([self.inspectingObject conformsToProtocol:@protocol(NSObject)] && [self.inspectingObject respondsToSelector:@selector(description)]) { - _headerTextView.text = [self.inspectingObject description]; + NSInteger retainCount = CFGetRetainCount((__bridge CFTypeRef)self.inspectingObject); + NSString *retainCountString = [NSString stringWithFormat:@"Retain count: %ld\n", (long)retainCount]; + NSString *infoHeaderText = [retainCountString stringByAppendingString:[self.inspectingObject description]]; + _headerTextView.text = infoHeaderText; } } diff --git a/HeapInspector/HINSPHeapStackInspector.h b/HeapInspector/HINSPHeapStackInspector.h index 2806839..e40db1f 100644 --- a/HeapInspector/HINSPHeapStackInspector.h +++ b/HeapInspector/HINSPHeapStackInspector.h @@ -8,7 +8,7 @@ #import -typedef void (^RMHeapEnumeratorBlock)(__unsafe_unretained id object); +typedef void (^RMHeapEnumeratorBlock)(__unsafe_unretained id object, BOOL *stop); @interface HINSPHeapStackInspector : NSObject diff --git a/HeapInspector/HINSPHeapStackInspector.m b/HeapInspector/HINSPHeapStackInspector.m index a082aba..9dc2508 100644 --- a/HeapInspector/HINSPHeapStackInspector.m +++ b/HeapInspector/HINSPHeapStackInspector.m @@ -37,10 +37,11 @@ static inline void range_callback(task_t task, vm_range_t *ranges, unsigned rangeCount) { - RMHeapEnumeratorBlock block = (__bridge RMHeapEnumeratorBlock)context; - if (!block) { + RMHeapEnumeratorBlock enumeratorBlock = (__bridge RMHeapEnumeratorBlock)context; + if (!enumeratorBlock) { return; } + BOOL stop = NO; for (unsigned int i = 0; i < rangeCount; i++) { vm_range_t range = ranges[i]; rm_maybe_object_t *object = (rm_maybe_object_t *)range.address; @@ -55,20 +56,23 @@ static inline void range_callback(task_t task, if (tryClass && CFSetContainsValue(classesLoadedInRuntime, (__bridge const void *)(tryClass)) && canRecordObject((__bridge id)object)) { - block((__bridge id)object); + enumeratorBlock((__bridge id)object, &stop); + if (stop) { + break; + } } } } -+ (void)enumerateLiveObjectsUsingBlock:(RMHeapEnumeratorBlock)block ++ (void)enumerateLiveObjectsUsingBlock:(RMHeapEnumeratorBlock)completionBlock { - if (!block) { + if (!completionBlock) { return; } // Refresh the class list on every call in case classes are added to the runtime. [self updateRegisteredClasses]; - + // For another exmple of enumerating through malloc ranges (which helped my understanding of the api) see: // http://llvm.org/svn/llvm-project/lldb/tags/RELEASE_34/final/examples/darwin/heap_find/heap/heap_find.cpp // Also https://gist.github.com/samdmarshall/17f4e66b5e2e579fd396 @@ -77,16 +81,27 @@ + (void)enumerateLiveObjectsUsingBlock:(RMHeapEnumeratorBlock)block mach_port_t task = mach_task_self(); unsigned int zoneCount = 0; kern_return_t result = malloc_get_all_zones(task, memory_reader, &zones, &zoneCount); + BOOL __block stopEnumerator = NO; if (result == KERN_SUCCESS) { for (unsigned i = 0; i < zoneCount; i++) { malloc_zone_t *zone = (malloc_zone_t *)zones[i]; if (zone != NULL && zone->introspect != NULL) { - zone->introspect->enumerator(task, - (__bridge void *)(block), - MALLOC_PTR_IN_USE_RANGE_TYPE, - (vm_address_t)zone, - memory_reader, - range_callback); + RMHeapEnumeratorBlock enumeratorBlock = ^(__unsafe_unretained id object, BOOL *stop) { + completionBlock(object, &stopEnumerator); + if (stopEnumerator) { + *stop = YES; + } + }; + if (!stopEnumerator) { + zone->introspect->enumerator(task, + (__bridge void *)(enumeratorBlock), + MALLOC_PTR_IN_USE_RANGE_TYPE, + (vm_address_t)zone, + memory_reader, + range_callback); + } else { + break; + } } } } @@ -132,7 +147,7 @@ + (NSSet *)recordedHeap + (NSSet *)heap { NSMutableSet *objects = [NSMutableSet set]; - [HINSPHeapStackInspector enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id object) { + [HINSPHeapStackInspector enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id object, BOOL *stop) { // We cannot store the object itself - We want to avoid any retain calls. // We store the class name + pointer NSString *string = [NSString stringWithFormat:@"%s: %p", @@ -147,12 +162,13 @@ + (NSSet *)heap + (id)objectForPointer:(NSString *)pointer { id __block foundObject = nil; - [HINSPHeapStackInspector enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id object) { + [HINSPHeapStackInspector enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id object, BOOL *stop) { if ([pointer isEqualToString:[NSString stringWithFormat:@"%p",object]]) { + foundObject = object; + *stop = YES; } }]; - return foundObject; } diff --git a/HeapInspector/HINSPRecordButton.h b/HeapInspector/HINSPRecordButton.h index d1ffc40..a1a2c95 100644 --- a/HeapInspector/HINSPRecordButton.h +++ b/HeapInspector/HINSPRecordButton.h @@ -9,5 +9,5 @@ #import @interface HINSPRecordButton : UIControl - +@property (nonatomic) BOOL isRecording; @end diff --git a/HeapInspector/HINSPRecordButton.m b/HeapInspector/HINSPRecordButton.m index 577f9c8..abb9bf3 100644 --- a/HeapInspector/HINSPRecordButton.m +++ b/HeapInspector/HINSPRecordButton.m @@ -10,8 +10,6 @@ @interface HINSPRecordButton () - -@property (nonatomic) BOOL isRecording; @property (nonatomic, weak) CAShapeLayer *shapeLayer; @end @@ -31,13 +29,19 @@ - (instancetype)initWithFrame:(CGRect)frame - (void)tapped:(id)sender { self.isRecording = !self.isRecording; - UIColor *color = nil; - if (self.isRecording) { - color = [self recordingColor]; - } else { - color = [self defaultColor]; +} + +- (void)setIsRecording:(BOOL)isRecording { + if (_isRecording != isRecording) { + _isRecording = isRecording; + UIColor *color = nil; + if (isRecording) { + color = [self recordingColor]; + } else { + color = [self defaultColor]; + } + _shapeLayer.fillColor = color.CGColor; } - _shapeLayer.fillColor = color.CGColor; } #pragma mark - Setter diff --git a/HeapInspector/HINSPRefHistoryTableViewController.m b/HeapInspector/HINSPRefHistoryTableViewController.m index c487155..0cf3d48 100644 --- a/HeapInspector/HINSPRefHistoryTableViewController.m +++ b/HeapInspector/HINSPRefHistoryTableViewController.m @@ -25,7 +25,7 @@ - (instancetype)initWithObject:(id)object self = [super initWithObject:object]; if (self) { - self.title = [NSString stringWithFormat:@"Reference History: %s: %p", + self.title = [NSString stringWithFormat:@"%s: %p", object_getClassName(self.inspectingObject), self.inspectingObject]; diff --git a/HeapInspector/HINSPShowViewController.m b/HeapInspector/HINSPShowViewController.m index 7dd9f41..055c655 100644 --- a/HeapInspector/HINSPShowViewController.m +++ b/HeapInspector/HINSPShowViewController.m @@ -29,7 +29,7 @@ - (instancetype)initWithObject:(id)object self = [super init]; if (self) { self.title = @"Showing View"; - self.edgesForExtendedLayout = UIRectEdgeNone; + self.automaticallyAdjustsScrollViewInsets = YES; _objectToInspect = object; self.shouldShowEditButton = YES; }