Explorar o código

Merge pull request #227 from NSExceptional/organize-refs-pr

Group similar "objects with ivars referencing this object"
Tanner Bennett %!s(int64=8) %!d(string=hai) anos
pai
achega
efa317f0d1

+ 137 - 27
Classes/GlobalStateExplorers/FLEXInstancesTableViewController.m

@@ -12,18 +12,49 @@
 #import "FLEXRuntimeUtility.h"
 #import "FLEXUtility.h"
 #import "FLEXHeapEnumerator.h"
+#import "FLEXObjectRef.h"
 #import <malloc/malloc.h>
 
 
 @interface FLEXInstancesTableViewController ()
 
-@property (nonatomic, strong) NSArray *instances;
-@property (nonatomic, strong) NSArray<NSString *> *fieldNames;
+/// Array of [[section], [section], ...]
+/// where [section] is [["row title", instance], ["row title", instance], ...]
+@property (nonatomic) NSArray<FLEXObjectRef *> *instances;
+@property (nonatomic) NSArray<NSArray<FLEXObjectRef*>*> *sections;
+@property (nonatomic) NSArray<NSString *> *sectionTitles;
+@property (nonatomic) NSArray<NSPredicate *> *predicates;
+@property (nonatomic, readonly) NSInteger maxSections;
 
 @end
 
 @implementation FLEXInstancesTableViewController
 
+- (id)initWithReferences:(NSArray<FLEXObjectRef *> *)references {
+    return [self initWithReferences:references predicates:nil sectionTitles:nil];
+}
+
+- (id)initWithReferences:(NSArray<FLEXObjectRef *> *)references
+              predicates:(NSArray<NSPredicate *> *)predicates
+           sectionTitles:(NSArray<NSString *> *)sectionTitles {
+    NSParameterAssert(predicates.count == sectionTitles.count);
+
+    self = [super init];
+    if (self) {
+        self.instances = references;
+        self.predicates = predicates;
+        self.sectionTitles = sectionTitles;
+
+        if (predicates.count) {
+            [self buildSections];
+        } else {
+            self.sections = @[references];
+        }
+    }
+
+    return self;
+}
+
 + (instancetype)instancesTableViewControllerForClassName:(NSString *)className
 {
     const char *classNameCString = [className UTF8String];
@@ -38,16 +69,15 @@
             }
         }
     }];
-    FLEXInstancesTableViewController *instancesViewController = [[self alloc] init];
-    instancesViewController.instances = instances;
-    instancesViewController.title = [NSString stringWithFormat:@"%@ (%lu)", className, (unsigned long)[instances count]];
-    return instancesViewController;
+    NSArray<FLEXObjectRef *> *references = [FLEXObjectRef referencingAll:instances];
+    FLEXInstancesTableViewController *viewController = [[self alloc] initWithReferences:references];
+    viewController.title = [NSString stringWithFormat:@"%@ (%lu)", className, (unsigned long)[instances count]];
+    return viewController;
 }
 
 + (instancetype)instancesTableViewControllerForInstancesReferencingObject:(id)object
 {
-    NSMutableArray *instances = [NSMutableArray array];
-    NSMutableArray<NSString *> *fieldNames = [NSMutableArray array];
+    NSMutableArray<FLEXObjectRef *> *instances = [NSMutableArray array];
     [FLEXHeapEnumerator enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id tryObject, __unsafe_unretained Class actualClass) {
         // Get all the ivars on the object. Start with the class and and travel up the inheritance chain.
         // Once we find a match, record it and move on to the next object. There's no reason to find multiple matches within the same object.
@@ -62,8 +92,7 @@
                     ptrdiff_t offset = ivar_getOffset(ivar);
                     uintptr_t *fieldPointer = (__bridge void *)tryObject + offset;
                     if (*fieldPointer == (uintptr_t)(__bridge void *)object) {
-                        [instances addObject:tryObject];
-                        [fieldNames addObject:@(ivar_getName(ivar))];
+                        [instances addObject:[FLEXObjectRef referencing:tryObject ivar:@(ivar_getName(ivar))]];
                         return;
                     }
                 }
@@ -71,11 +100,85 @@
             tryClass = class_getSuperclass(tryClass);
         }
     }];
-    FLEXInstancesTableViewController *instancesViewController = [[self alloc] init];
-    instancesViewController.instances = instances;
-    instancesViewController.fieldNames = fieldNames;
-    instancesViewController.title = [NSString stringWithFormat:@"Referencing %@ %p", NSStringFromClass(object_getClass(object)), object];
-    return instancesViewController;
+
+    NSArray<NSPredicate *> *predicates = [self defaultPredicates];
+    NSArray<NSString *> *sectionTitles = [self defaultSectionTitles];
+    FLEXInstancesTableViewController *viewController = [[self alloc] initWithReferences:instances
+                                                                             predicates:predicates
+                                                                          sectionTitles:sectionTitles];
+    viewController.title = [NSString stringWithFormat:@"Referencing %@ %p", NSStringFromClass(object_getClass(object)), object];
+    return viewController;
+}
+
++ (NSPredicate *)defaultPredicateForSection:(NSInteger)section
+{
+    // These are the types of references that we typically don't care about.
+    // We want this list of "object-ivar pairs" split into two sections.
+    BOOL(^isObserver)(FLEXObjectRef *, NSDictionary *) = ^BOOL(FLEXObjectRef *ref, NSDictionary *bindings) {
+        NSString *row = ref.reference;
+        return [row isEqualToString:@"__NSObserver object"] ||
+               [row isEqualToString:@"_CFXNotificationObjcObserverRegistration _object"];
+    };
+
+    /// These are common AutoLayout related references we also rarely care about.
+    BOOL(^isConstraintRelated)(FLEXObjectRef *, NSDictionary *) = ^BOOL(FLEXObjectRef *ref, NSDictionary *bindings) {
+        static NSSet *ignored = nil;
+        static dispatch_once_t onceToken;
+        dispatch_once(&onceToken, ^{
+            ignored = [NSSet setWithArray:@[
+                @"NSLayoutConstraint _container",
+                @"NSContentSizeLayoutConstraint _container",
+                @"NSAutoresizingMaskLayoutConstraint _container",
+                @"MASViewConstraint _installedView",
+                @"MASLayoutConstraint _container",
+                @"MASViewAttribute _view"
+            ]];
+        });
+
+        NSString *row = ref.reference;
+        return ([row hasPrefix:@"NSLayout"] && [row hasSuffix:@" _referenceItem"]) ||
+               ([row hasPrefix:@"NSIS"] && [row hasSuffix:@" _delegate"])  ||
+               ([row hasPrefix:@"_NSAutoresizingMask"] && [row hasSuffix:@" _referenceItem"]) ||
+               [ignored containsObject:row];
+    };
+
+    BOOL(^isEssential)(FLEXObjectRef *, NSDictionary *) = ^BOOL(FLEXObjectRef *ref, NSDictionary *bindings) {
+        return !(isObserver(ref, bindings) || isConstraintRelated(ref, bindings));
+    };
+
+    switch (section) {
+        case 0: return [NSPredicate predicateWithBlock:isEssential];
+        case 1: return [NSPredicate predicateWithBlock:isConstraintRelated];
+        case 2: return [NSPredicate predicateWithBlock:isObserver];
+
+        default: return nil;
+    }
+}
+
++ (NSArray<NSPredicate *> *)defaultPredicates {
+    return @[[self defaultPredicateForSection:0],
+             [self defaultPredicateForSection:1],
+             [self defaultPredicateForSection:2]];
+}
+
++ (NSArray<NSString *> *)defaultSectionTitles {
+    return @[@"", @"AutoLayout", @"Trivial"];
+}
+
+- (void)buildSections
+{
+    NSInteger maxSections = self.maxSections;
+    NSMutableArray *sections = [NSMutableArray array];
+    for (NSInteger i = 0; i < maxSections; i++) {
+        NSPredicate *predicate = self.predicates[i];
+        [sections addObject:[self.instances filteredArrayUsingPredicate:predicate]];
+    }
+
+    self.sections = sections;
+}
+
+- (NSInteger)maxSections {
+    return self.predicates.count ?: 1;
 }
 
 
@@ -83,12 +186,12 @@
 
 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
 {
-    return 1;
+    return self.maxSections;
 }
 
 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
 {
-    return [self.instances count];
+    return self.sections[section].count;
 }
 
 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
@@ -103,20 +206,27 @@
         cell.detailTextLabel.font = cellFont;
         cell.detailTextLabel.textColor = [UIColor grayColor];
     }
-    
-    id instance = self.instances[indexPath.row];
-    NSString *title = nil;
-    if ((NSInteger)[self.fieldNames count] > indexPath.row) {
-        title = [NSString stringWithFormat:@"%@ %@", NSStringFromClass(object_getClass(instance)), self.fieldNames[indexPath.row]];
-    } else {
-        title = [NSString stringWithFormat:@"%@ %p", NSStringFromClass(object_getClass(instance)), instance];
-    }
-    cell.textLabel.text = title;
-    cell.detailTextLabel.text = [FLEXRuntimeUtility descriptionForIvarOrPropertyValue:instance];
+
+    FLEXObjectRef *row = self.sections[indexPath.section][indexPath.row];
+    cell.textLabel.text = row.reference;
+    cell.detailTextLabel.text = [FLEXRuntimeUtility descriptionForIvarOrPropertyValue:row.object];
     
     return cell;
 }
 
+- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
+{
+    if (self.sectionTitles.count) {
+        // Return nil instead of empty strings
+        NSString *title = self.sectionTitles[section];
+        if (title.length) {
+            return title;
+        }
+    }
+
+    return nil;
+}
+
 
 #pragma mark - Table View Delegate
 

+ 22 - 0
Classes/GlobalStateExplorers/FLEXObjectRef.h

@@ -0,0 +1,22 @@
+//
+//  FLEXObjectRef.h
+//  FLEX
+//
+//  Created by Tanner Bennett on 7/24/18.
+//  Copyright (c) 2018 Flipboard. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface FLEXObjectRef : NSObject
+
++ (instancetype)referencing:(id)object;
++ (instancetype)referencing:(id)object ivar:(NSString *)ivarName;
+
++ (NSArray<FLEXObjectRef *> *)referencingAll:(NSArray *)objects;
+
+/// For example, "NSString 0x1d4085d0" or "NSLayoutConstraint _object"
+@property (nonatomic, readonly) NSString *reference;
+@property (nonatomic, readonly) id object;
+
+@end

+ 47 - 0
Classes/GlobalStateExplorers/FLEXObjectRef.m

@@ -0,0 +1,47 @@
+//
+//  FLEXObjectRef.m
+//  FLEX
+//
+//  Created by Tanner Bennett on 7/24/18.
+//  Copyright (c) 2018 Flipboard. All rights reserved.
+//
+
+#import "FLEXObjectRef.h"
+#import <objc/runtime.h>
+
+@implementation FLEXObjectRef
+
++ (instancetype)referencing:(id)object {
+    return [[self alloc] initWithObject:object ivarName:nil];
+}
+
++ (instancetype)referencing:(id)object ivar:(NSString *)ivarName {
+    return [[self alloc] initWithObject:object ivarName:ivarName];
+}
+
++ (NSArray<FLEXObjectRef *> *)referencingAll:(NSArray *)objects {
+    NSMutableArray<FLEXObjectRef *> *refs = [NSMutableArray array];
+    for (id obj in objects) {
+        [refs addObject:[self referencing:obj]];
+    }
+
+    return refs;
+}
+
+- (id)initWithObject:(id)object ivarName:(NSString *)ivar {
+    self = [super init];
+    if (self) {
+        _object = object;
+
+        NSString *class = NSStringFromClass(object_getClass(object));
+        if (ivar) {
+            _reference = [NSString stringWithFormat:@"%@ %@", class, ivar];
+        } else {
+            _reference = [NSString stringWithFormat:@"%@ %p", class, object];
+        }
+    }
+
+    return self;
+}
+
+@end

+ 8 - 0
FLEX.xcodeproj/project.pbxproj

@@ -166,6 +166,8 @@
 		94AAF0381BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 94AAF0361BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		94AAF0391BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 94AAF0371BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m */; };
 		94AAF03A1BAF2F0300DE8760 /* FLEXKeyboardShortcutManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 942DCD821BAE0AD300DB5DC2 /* FLEXKeyboardShortcutManager.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		C3DB9F642107FC9600B46809 /* FLEXObjectRef.h in Headers */ = {isa = PBXBuildFile; fileRef = C3DB9F622107FC9600B46809 /* FLEXObjectRef.h */; };
+		C3DB9F652107FC9600B46809 /* FLEXObjectRef.m in Sources */ = {isa = PBXBuildFile; fileRef = C3DB9F632107FC9600B46809 /* FLEXObjectRef.m */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -342,6 +344,8 @@
 		94A515241C4CA2080063292F /* FLEXToolbarItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FLEXToolbarItem.m; path = Classes/Toolbar/FLEXToolbarItem.m; sourceTree = SOURCE_ROOT; };
 		94AAF0361BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKeyboardHelpViewController.h; sourceTree = "<group>"; };
 		94AAF0371BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKeyboardHelpViewController.m; sourceTree = "<group>"; };
+		C3DB9F622107FC9600B46809 /* FLEXObjectRef.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXObjectRef.h; sourceTree = "<group>"; };
+		C3DB9F632107FC9600B46809 /* FLEXObjectRef.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXObjectRef.m; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -566,6 +570,8 @@
 				3A4C94A41B5B21410088C3F2 /* FLEXGlobalsTableViewController.m */,
 				3A4C94A51B5B21410088C3F2 /* FLEXInstancesTableViewController.h */,
 				3A4C94A61B5B21410088C3F2 /* FLEXInstancesTableViewController.m */,
+				C3DB9F622107FC9600B46809 /* FLEXObjectRef.h */,
+				C3DB9F632107FC9600B46809 /* FLEXObjectRef.m */,
 				3A4C94A71B5B21410088C3F2 /* FLEXLibrariesTableViewController.h */,
 				3A4C94A81B5B21410088C3F2 /* FLEXLibrariesTableViewController.m */,
 				3A4C94A91B5B21410088C3F2 /* FLEXLiveObjectsTableViewController.h */,
@@ -758,6 +764,7 @@
 				3A4C94FB1B5B21410088C3F2 /* FLEXArgumentInputStringView.h in Headers */,
 				3A4C95421B5B21410088C3F2 /* FLEXNetworkObserver.h in Headers */,
 				679F64861BD53B7B00A8C94C /* FLEXCookiesTableViewController.h in Headers */,
+				C3DB9F642107FC9600B46809 /* FLEXObjectRef.h in Headers */,
 				3A4C95401B5B21410088C3F2 /* FLEXNetworkTransactionTableViewCell.h in Headers */,
 				3A4C95241B5B21410088C3F2 /* FLEXFileBrowserTableViewController.h in Headers */,
 				94AAF0381BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h in Headers */,
@@ -927,6 +934,7 @@
 				779B1ED31C0C4D7C001F5E49 /* FLEXTableColumnHeader.m in Sources */,
 				3A4C94EA1B5B21410088C3F2 /* FLEXHierarchyTableViewController.m in Sources */,
 				3A4C95331B5B21410088C3F2 /* FLEXSystemLogTableViewCell.m in Sources */,
+				C3DB9F652107FC9600B46809 /* FLEXObjectRef.m in Sources */,
 				3A4C95021B5B21410088C3F2 /* FLEXArgumentInputTextView.m in Sources */,
 				94A515261C4CA2080063292F /* FLEXExplorerToolbar.m in Sources */,
 				3A4C94FA1B5B21410088C3F2 /* FLEXArgumentInputNumberView.m in Sources */,