From 46e0c3e502db73b51e69ac014d1841af06dfea64 Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Wed, 7 Sep 2016 20:11:27 -0700 Subject: [PATCH] Majorly Improve Descriptions (#2208) --- AsyncDisplayKit.xcodeproj/project.pbxproj | 17 +++ AsyncDisplayKit/ASCellNode.mm | 32 +++++ AsyncDisplayKit/ASDisplayNode.mm | 121 +++++++++++++----- AsyncDisplayKit/ASDisplayNodeExtras.h | 6 +- AsyncDisplayKit/ASTextNode.mm | 35 ++++- AsyncDisplayKit/Details/_ASDisplayLayer.mm | 17 ++- AsyncDisplayKit/Details/_ASDisplayView.mm | 28 +++- AsyncDisplayKit/Layout/ASLayout.mm | 15 ++- .../Private/ASDisplayNodeInternal.h | 3 +- .../Private/ASObjectDescriptionHelpers.h | 49 +++++++ .../Private/ASObjectDescriptionHelpers.m | 71 ++++++++++ 11 files changed, 341 insertions(+), 53 deletions(-) create mode 100644 AsyncDisplayKit/Private/ASObjectDescriptionHelpers.h create mode 100644 AsyncDisplayKit/Private/ASObjectDescriptionHelpers.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index fd6dab77..06040bc2 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -199,6 +199,9 @@ 68FC85EA1CE29C7D00EDD713 /* ASVisibilityProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FC85E71CE29C7D00EDD713 /* ASVisibilityProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; }; 68FC85EB1CE29C7D00EDD713 /* ASVisibilityProtocols.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */; }; 68FC85EC1CE29C7D00EDD713 /* ASVisibilityProtocols.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */; }; + 6959433E1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */; }; + 6959433F1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */; }; + 695943401D70815300B0EE1F /* ASDisplayNodeLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */; }; 696FCB311D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 696FCB301D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm */; }; 69708BA61D76386D005C3CF9 /* ASEqualityHashHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 69708BA41D76386D005C3CF9 /* ASEqualityHashHelpers.h */; }; 69708BA71D76386D005C3CF9 /* ASEqualityHashHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 69708BA51D76386D005C3CF9 /* ASEqualityHashHelpers.mm */; }; @@ -420,6 +423,9 @@ CC3B208C1C3F7A5400798563 /* ASWeakSet.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3B20881C3F7A5400798563 /* ASWeakSet.m */; }; CC3B208E1C3F7D0A00798563 /* ASWeakSetTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3B208D1C3F7D0A00798563 /* ASWeakSetTests.m */; }; CC3B20901C3F892D00798563 /* ASBridgedPropertiesTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC3B208F1C3F892D00798563 /* ASBridgedPropertiesTests.mm */; }; + CC446A2F1D80AAE00071FD03 /* ASObjectDescriptionHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = CC446A2D1D80AAE00071FD03 /* ASObjectDescriptionHelpers.h */; }; + CC446A301D80AAE00071FD03 /* ASObjectDescriptionHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = CC446A2E1D80AAE00071FD03 /* ASObjectDescriptionHelpers.m */; }; + CC446A311D80AAE00071FD03 /* ASObjectDescriptionHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = CC446A2E1D80AAE00071FD03 /* ASObjectDescriptionHelpers.m */; }; CC4981B31D1A02BE004E13CC /* ASTableViewThrashTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC4981B21D1A02BE004E13CC /* ASTableViewThrashTests.m */; }; CC4981BD1D1C7F65004E13CC /* NSIndexSet+ASHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = CC4981BB1D1C7F65004E13CC /* NSIndexSet+ASHelpers.m */; }; CC54A81C1D70079800296A24 /* ASDispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = CC54A81B1D70077A00296A24 /* ASDispatch.h */; }; @@ -427,6 +433,7 @@ CC7FD9DF1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7FD9DD1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m */; }; CC7FD9E11BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7FD9E01BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m */; }; CC7FD9E21BB603FF005CCB2B /* ASPhotosFrameworkImageRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CC88F7AE1D80AF5E000D6D4E /* ASObjectDescriptionHelpers.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CC446A2D1D80AAE00071FD03 /* ASObjectDescriptionHelpers.h */; }; CC8B05D61D73836400F54286 /* ASPerformanceTestContext.m in Sources */ = {isa = PBXBuildFile; fileRef = CC8B05D51D73836400F54286 /* ASPerformanceTestContext.m */; }; CC8B05D81D73979700F54286 /* ASTextNodePerformanceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC8B05D71D73979700F54286 /* ASTextNodePerformanceTests.m */; }; CCA221D31D6FA7EF00AF6A0F /* ASViewControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA221D21D6FA7EF00AF6A0F /* ASViewControllerTests.m */; }; @@ -639,6 +646,7 @@ dstPath = "include/$(PRODUCT_NAME)"; dstSubfolderSpec = 16; files = ( + CC88F7AE1D80AF5E000D6D4E /* ASObjectDescriptionHelpers.h in CopyFiles */, F7CE6C981D2CDB5800BE4C15 /* ASInternalHelpers.h in CopyFiles */, F7CE6CB71D2CE2D000BE4C15 /* ASLayoutableExtensibility.h in CopyFiles */, F7CE6C131D2CDB3E00BE4C15 /* ASPagerFlowLayout.h in CopyFiles */, @@ -966,6 +974,8 @@ 68FC85E11CE29B7E00EDD713 /* ASTabBarController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTabBarController.m; sourceTree = ""; }; 68FC85E71CE29C7D00EDD713 /* ASVisibilityProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASVisibilityProtocols.h; sourceTree = ""; }; 68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASVisibilityProtocols.m; sourceTree = ""; }; + 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDisplayNodeLayout.mm; sourceTree = ""; }; + 6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNodeLayout.h; sourceTree = ""; }; 696FCB301D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBackgroundLayoutSpecSnapshotTests.mm; sourceTree = ""; }; 69708BA41D76386D005C3CF9 /* ASEqualityHashHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASEqualityHashHelpers.h; path = TextKit/ASEqualityHashHelpers.h; sourceTree = ""; }; 69708BA51D76386D005C3CF9 /* ASEqualityHashHelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASEqualityHashHelpers.mm; path = TextKit/ASEqualityHashHelpers.mm; sourceTree = ""; }; @@ -1096,6 +1106,8 @@ CC3B20881C3F7A5400798563 /* ASWeakSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASWeakSet.m; sourceTree = ""; }; CC3B208D1C3F7D0A00798563 /* ASWeakSetTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASWeakSetTests.m; sourceTree = ""; }; CC3B208F1C3F892D00798563 /* ASBridgedPropertiesTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBridgedPropertiesTests.mm; sourceTree = ""; }; + CC446A2D1D80AAE00071FD03 /* ASObjectDescriptionHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASObjectDescriptionHelpers.h; sourceTree = ""; }; + CC446A2E1D80AAE00071FD03 /* ASObjectDescriptionHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASObjectDescriptionHelpers.m; sourceTree = ""; }; CC4981B21D1A02BE004E13CC /* ASTableViewThrashTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTableViewThrashTests.m; sourceTree = ""; }; CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexSet+ASHelpers.h"; sourceTree = ""; }; CC4981BB1D1C7F65004E13CC /* NSIndexSet+ASHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexSet+ASHelpers.m"; sourceTree = ""; }; @@ -1503,6 +1515,8 @@ 058D0A01195D050800B7D73C /* Private */ = { isa = PBXGroup; children = ( + CC446A2D1D80AAE00071FD03 /* ASObjectDescriptionHelpers.h */, + CC446A2E1D80AAE00071FD03 /* ASObjectDescriptionHelpers.m */, CC54A81B1D70077A00296A24 /* ASDispatch.h */, 058D0A02195D050800B7D73C /* _AS-objc-internal.h */, 058D0A03195D050800B7D73C /* _ASCoreAnimationExtras.h */, @@ -1856,6 +1870,7 @@ 34EFC7771B701D2D00AD841F /* ASStackUnpositionedLayout.h in Headers */, 9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */, 34EFC7731B701D0700AD841F /* ASStaticLayoutSpec.h in Headers */, + CC446A2F1D80AAE00071FD03 /* ASObjectDescriptionHelpers.h in Headers */, 254C6B781BF94DF4003EC431 /* ASTextKitContext.h in Headers */, B350620A1B010EFD0018CF92 /* ASTableView.h in Headers */, B350620C1B010EFD0018CF92 /* ASTableViewProtocols.h in Headers */, @@ -2144,6 +2159,7 @@ CC4981BD1D1C7F65004E13CC /* NSIndexSet+ASHelpers.m in Sources */, DB55C2631C6408D6004EDCF5 /* _ASTransitionContext.m in Sources */, 92074A631CC8BA1900918F75 /* ASImageNode+tvOS.m in Sources */, + CC446A301D80AAE00071FD03 /* ASObjectDescriptionHelpers.m in Sources */, 251B8EFA1BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m in Sources */, ACF6ED271B17843500DA7C62 /* ASLayoutSpec.mm in Sources */, 257754B01BEE44CD00737CA5 /* ASTextKitRenderer+TextChecking.mm in Sources */, @@ -2324,6 +2340,7 @@ 9CC606651D24DF9E006581A0 /* NSIndexSet+ASHelpers.m in Sources */, 92074A641CC8BA1900918F75 /* ASImageNode+tvOS.m in Sources */, B35062541B010EFD0018CF92 /* ASImageNode+CGExtras.m in Sources */, + CC446A311D80AAE00071FD03 /* ASObjectDescriptionHelpers.m in Sources */, 68355B401CB57A69001D4E68 /* ASImageContainerProtocolCategories.m in Sources */, B35062031B010EFD0018CF92 /* ASImageNode.mm in Sources */, 254C6B821BF94F8A003EC431 /* ASTextKitComponents.m in Sources */, diff --git a/AsyncDisplayKit/ASCellNode.mm b/AsyncDisplayKit/ASCellNode.mm index 6a153d58..6d204f0e 100644 --- a/AsyncDisplayKit/ASCellNode.mm +++ b/AsyncDisplayKit/ASCellNode.mm @@ -12,10 +12,13 @@ #import "ASEqualityHelpers.h" #import "ASDisplayNodeInternal.h" +#import "ASDisplayNode+FrameworkPrivate.h" #import #import #import #import +#import +#import #import #import @@ -276,6 +279,35 @@ withCellFrame:cellFrame]; } +- (NSMutableArray *)propertiesForDebugDescription +{ + NSMutableArray *result = [super propertiesForDebugDescription]; + + UIScrollView *scrollView = self.scrollView; + + ASDisplayNode *owningNode = scrollView.asyncdisplaykit_node; + if ([owningNode isKindOfClass:[ASCollectionNode class]]) { + [result addObject:@{ @"collectionNode" : ASObjectDescriptionMakeTiny(owningNode) }]; + } else if ([owningNode isKindOfClass:[ASTableNode class]]) { + [result addObject:@{ @"tableNode" : ASObjectDescriptionMakeTiny(owningNode) }]; + + } else if ([scrollView isKindOfClass:[ASCollectionView class]]) { + NSIndexPath *ip = [(ASCollectionView *)scrollView indexPathForNode:self]; + if (ip != nil) { + [result addObject:@{ @"indexPath" : ip }]; + } + [result addObject:@{ @"collectionView" : ASObjectDescriptionMakeTiny(scrollView) }]; + + } else if ([scrollView isKindOfClass:[ASTableView class]]) { + NSIndexPath *ip = [(ASTableView *)scrollView indexPathForNode:self]; + if (ip != nil) { + [result addObject:@{ @"indexPath" : ip }]; + } + [result addObject:@{ @"tableView" : ASObjectDescriptionMakeTiny(scrollView) }]; + } + + return result; +} @end diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 86c82869..fd2dca0f 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -31,7 +31,7 @@ #import "ASLayout.h" #import "ASLayoutSpec.h" #import "ASLayoutValidation.h" -#import "ASCellNode.h" +#import "ASCellNode+Internal.h" NSInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority; NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification = @"ASRenderingEngineDidDisplayScheduledNodes"; @@ -3119,6 +3119,93 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority"; return self; } +#pragma mark Debugging (Private) + +- (NSMutableArray *)propertiesForDescription +{ + NSMutableArray *result = [NSMutableArray array]; + if (self.name.length > 0) { + [result addObject:@{ @"name" : ASStringWithQuotesIfMultiword(self.name) }]; + } + return result; +} + +- (NSMutableArray *)propertiesForDebugDescription +{ + NSMutableArray *result = [NSMutableArray array]; + + if (self.name.length > 0) { + [result addObject:@{ @"name" : ASStringWithQuotesIfMultiword(self.name)}]; + } + + CGRect windowFrame = [self _frameInWindow]; + if (CGRectIsNull(windowFrame) == NO) { + [result addObject:@{ @"frameInWindow" : [NSValue valueWithCGRect:windowFrame] }]; + } + + if (_view != nil) { + [result addObject:@{ @"frame" : [NSValue valueWithCGRect:_view.frame] }]; + } else if (_layer != nil) { + [result addObject:@{ @"frame" : [NSValue valueWithCGRect:_layer.frame] }]; + } else { + [result addObject:@{ @"frame" : [NSValue valueWithCGRect:self.frame] }]; + } + + // Check supernode so that if we are cell node we don't find self. + ASCellNode *cellNode = ASDisplayNodeFindFirstSupernodeOfClass(self.supernode, [ASCellNode class]); + if (cellNode != nil) { + [result addObject:@{ @"cellNode" : ASObjectDescriptionMakeTiny(cellNode) }]; + } + + [result addObject:@{ @"interfaceState" : NSStringFromASInterfaceState(self.interfaceState)} ]; + + if (_view != nil) { + [result addObject:@{ @"view" : ASObjectDescriptionMakeTiny(_view) }]; + } else if (_layer != nil) { + [result addObject:@{ @"layer" : ASObjectDescriptionMakeTiny(_layer) }]; + } else if (_viewClass != nil) { + [result addObject:@{ @"viewClass" : _viewClass }]; + } else if (_layerClass != nil) { + [result addObject:@{ @"layerClass" : _layerClass }]; + } else if (_viewBlock != nil) { + [result addObject:@{ @"viewBlock" : _viewBlock }]; + } else if (_layerBlock != nil) { + [result addObject:@{ @"layerBlock" : _layerBlock }]; + } + + return result; +} + +- (NSString *)description +{ + return ASObjectDescriptionMake(self, [self propertiesForDescription]); +} + +- (NSString *)debugDescription +{ + return ASObjectDescriptionMake(self, [self propertiesForDebugDescription]); +} + +// This should only be called for debugging. It's not thread safe and it doesn't assert. +// NOTE: Returns CGRectNull if the node isn't in a hierarchy. +- (CGRect)_frameInWindow +{ + if (self.isNodeLoaded == NO || self.isInHierarchy == NO) { + return CGRectNull; + } + + if (self.layerBacked) { + CALayer *rootLayer = self.layer; + CALayer *nextLayer = rootLayer; + while ((nextLayer = rootLayer.superlayer) != nil) { + rootLayer = nextLayer; + } + + return [self.layer convertRect:self.threadSafeBounds toLayer:rootLayer]; + } else { + return [self.view convertRect:self.threadSafeBounds toView:nil]; + } +} #pragma mark - ASEnvironment @@ -3220,38 +3307,6 @@ ASEnvironmentLayoutExtensibilityForwarding @implementation ASDisplayNode (Debugging) -- (NSString *)description -{ - if (self.name) { - return [NSString stringWithFormat:@"<%@ %p name = %@>", self.class, self, self.name]; - } else { - return [super description]; - } -} - -- (NSString *)debugDescription -{ - NSString *notableTargetDesc = (_flags.layerBacked ? @" [layer]" : @" [view]"); - if (_view && _viewClass) { // Nonstandard view is loaded - notableTargetDesc = [NSString stringWithFormat:@" [%@ : %p]", _view.class, _view]; - } else if (_layer && _layerClass) { // Nonstandard layer is loaded - notableTargetDesc = [NSString stringWithFormat:@" [%@ : %p]", _layer.class, _layer]; - } else if (_viewClass) { // Nonstandard view class unloaded - notableTargetDesc = [NSString stringWithFormat:@" [%@]", _viewClass]; - } else if (_layerClass) { // Nonstandard layer class unloaded - notableTargetDesc = [NSString stringWithFormat:@" [%@]", _layerClass]; - } else if (_viewBlock) { // Nonstandard lazy view unloaded - notableTargetDesc = @" [block]"; - } else if (_layerBlock) { // Nonstandard lazy layer unloaded - notableTargetDesc = @" [block]"; - } - if (self.name) { - return [NSString stringWithFormat:@"<%@ %p name = %@%@>", self.class, self, self.name, notableTargetDesc]; - } else { - return [NSString stringWithFormat:@"<%@ %p%@>", self.class, self, notableTargetDesc]; - } -} - - (NSString *)descriptionForRecursiveDescription { NSString *creationTypeString = nil; diff --git a/AsyncDisplayKit/ASDisplayNodeExtras.h b/AsyncDisplayKit/ASDisplayNodeExtras.h index 2bfe5f17..5ee5a53c 100644 --- a/AsyncDisplayKit/ASDisplayNodeExtras.h +++ b/AsyncDisplayKit/ASDisplayNodeExtras.h @@ -46,13 +46,13 @@ ASDISPLAYNODE_INLINE NSString * _Nonnull NSStringFromASInterfaceState(ASInterfac [states addObject:@"MeasureLayout"]; } if (ASInterfaceStateIncludesPreload(interfaceState)) { - [states addObject:@" | Preload"]; + [states addObject:@"Preload"]; } if (ASInterfaceStateIncludesDisplay(interfaceState)) { - [states addObject:@" | Display"]; + [states addObject:@"Display"]; } if (ASInterfaceStateIncludesVisible(interfaceState)) { - [states addObject:@" | Visible"]; + [states addObject:@"Visible"]; } return [NSString stringWithFormat:@"{ %@ }", [states componentsJoinedByString:@" | "]]; } diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 62361e11..4b7f56a2 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -28,6 +28,7 @@ #import "ASLayout.h" #import "CGRect+ASConvenience.h" +#import "ASObjectDescriptionHelpers.h" /** * If set, we will record all values set to attributedText into an array @@ -222,15 +223,35 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; } } -- (NSString *)description +#pragma mark - Description + +- (NSString *)_plainStringForDescription { - ASDN::MutexLocker l(__instanceLock__); - - NSString *plainString = [[_attributedText string] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; - NSString *truncationString = [_composedTruncationText string]; - if (plainString.length > 50) + NSString *plainString = [[self.attributedText string] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; + if (plainString.length > 50) { plainString = [[plainString substringToIndex:50] stringByAppendingString:@"\u2026"]; - return [NSString stringWithFormat:@"<%@: %p; text = \"%@\"; truncation string = \"%@\"; frame = %@; renderer = %p>", self.class, self, plainString, truncationString, self.nodeLoaded ? NSStringFromCGRect(self.layer.frame) : nil, [self _renderer]]; + } + return plainString; +} + +- (NSMutableArray *)propertiesForDescription +{ + NSMutableArray *result = [super propertiesForDescription]; + NSString *plainString = [self _plainStringForDescription]; + if (plainString.length > 0) { + [result insertObject:@{ @"text" : ASStringWithQuotesIfMultiword(plainString) } atIndex:0]; + } + return result; +} + +- (NSMutableArray *)propertiesForDebugDescription +{ + NSMutableArray *result = [super propertiesForDebugDescription]; + NSString *plainString = [self _plainStringForDescription]; + if (plainString.length > 0) { + [result insertObject:@{ @"text" : ASStringWithQuotesIfMultiword(plainString) } atIndex:0]; + } + return result; } #pragma mark - ASDisplayNode diff --git a/AsyncDisplayKit/Details/_ASDisplayLayer.mm b/AsyncDisplayKit/Details/_ASDisplayLayer.mm index 10cdc146..8908fb1a 100644 --- a/AsyncDisplayKit/Details/_ASDisplayLayer.mm +++ b/AsyncDisplayKit/Details/_ASDisplayLayer.mm @@ -17,6 +17,7 @@ #import "ASDisplayNode.h" #import "ASDisplayNodeInternal.h" #import "ASDisplayNode+FrameworkPrivate.h" +#import "ASObjectDescriptionHelpers.h" @implementation _ASDisplayLayer { @@ -236,11 +237,21 @@ [strongAsyncDelegate cancelDisplayAsyncLayer:self]; } +// e.g. > - (NSString *)description { - // The standard UIView description is useless for debugging because all ASDisplayNode subclasses have _ASDisplayView-type views. - // This allows us to at least see the name of the node subclass and get its pointer directly from [[UIWindow keyWindow] recursiveDescription]. - return [NSString stringWithFormat:@"<%@, layer = %@>", self.asyncdisplaykit_node, [super description]]; + NSMutableString *description = [[super description] mutableCopy]; + ASDisplayNode *node = self.asyncdisplaykit_node; + if (node != nil) { + NSString *classString = [NSString stringWithFormat:@"%@-", [node class]]; + [description replaceOccurrencesOfString:@"_ASDisplay" withString:classString options:kNilOptions range:NSMakeRange(0, description.length)]; + NSUInteger insertionIndex = [description rangeOfString:@">"].location; + if (insertionIndex != NSNotFound) { + NSString *nodeString = [NSString stringWithFormat:@"; node = %@", node]; + [description insertString:nodeString atIndex:insertionIndex]; + } + } + return description; } @end diff --git a/AsyncDisplayKit/Details/_ASDisplayView.mm b/AsyncDisplayKit/Details/_ASDisplayView.mm index 3a38a674..e8506144 100644 --- a/AsyncDisplayKit/Details/_ASDisplayView.mm +++ b/AsyncDisplayKit/Details/_ASDisplayView.mm @@ -15,6 +15,7 @@ #import "ASDisplayNodeInternal.h" #import "ASDisplayNode+FrameworkPrivate.h" #import "ASDisplayNode+Subclasses.h" +#import "ASObjectDescriptionHelpers.h" @interface _ASDisplayView () @property (nonatomic, assign, readwrite) ASDisplayNode *asyncdisplaykit_node; @@ -44,11 +45,32 @@ #pragma mark - NSObject Overrides +// e.g. ; frame = ...> - (NSString *)description { - // The standard UIView description is useless for debugging because all ASDisplayNode subclasses have _ASDisplayView-type views. - // This allows us to at least see the name of the node subclass and get its pointer directly from [[UIWindow keyWindow] recursiveDescription]. - return [NSString stringWithFormat:@"<%@, view = %@>", _node, [super description]]; + NSMutableString *description = [[super description] mutableCopy]; + + ASDisplayNode *node = _node; + + if (node != nil) { + NSString *classString = [NSString stringWithFormat:@"%@-", [node class]]; + [description replaceOccurrencesOfString:@"_ASDisplay" withString:classString options:kNilOptions range:NSMakeRange(0, description.length)]; + NSUInteger semicolon = [description rangeOfString:@";"].location; + if (semicolon != NSNotFound) { + NSString *nodeString = [NSString stringWithFormat:@"; node = %@", node]; + [description insertString:nodeString atIndex:semicolon]; + } + // Remove layer description – it never contains valuable info and it duplicates the node info. Noisy. + NSRange layerDescriptionRange = [description rangeOfString:@"; layer = <.*>" options:NSRegularExpressionSearch]; + if (layerDescriptionRange.location != NSNotFound) { + [description replaceCharactersInRange:layerDescriptionRange withString:@""]; + // Our regex will grab the closing angle bracket and I'm not clever enough to come up with a better one, so re-add it if needed. + if ([description hasSuffix:@">"] == NO) { + [description appendString:@">"]; + } + } + } + return description; } #pragma mark - UIView Overrides diff --git a/AsyncDisplayKit/Layout/ASLayout.mm b/AsyncDisplayKit/Layout/ASLayout.mm index 575f593e..67d0d0fe 100644 --- a/AsyncDisplayKit/Layout/ASLayout.mm +++ b/AsyncDisplayKit/Layout/ASLayout.mm @@ -15,6 +15,7 @@ #import "ASLayoutSpecUtilities.h" #import +#import "ASObjectDescriptionHelpers.h" CGPoint const CGPointNull = {NAN, NAN}; @@ -38,7 +39,7 @@ static inline NSString * descriptionIndents(NSUInteger indents) return description; } -@interface ASLayout () +@interface ASLayout () /** * A boolean describing if the current layout has been flattened. @@ -229,10 +230,18 @@ static inline NSString * descriptionIndents(NSUInteger indents) #pragma mark - Description +- (NSMutableArray *)propertiesForDescription +{ + NSMutableArray *result = [NSMutableArray array]; + [result addObject:@{ @"layoutable" : (self.layoutableObject ?: (id)kCFNull) }]; + [result addObject:@{ @"position" : [NSValue valueWithCGPoint:self.position] }]; + [result addObject:@{ @"size" : [NSValue valueWithCGSize:self.size] }]; + return result; +} + - (NSString *)description { - return [NSString stringWithFormat:@"<, layoutable = %@, position = %@; size = %@; constrainedSizeRange = %@>", - self, self.layoutableObject, NSStringFromCGPoint(self.position), NSStringFromCGSize(self.size), NSStringFromASSizeRange(self.constrainedSizeRange)]; + return ASObjectDescriptionMake(self, [self propertiesForDescription]); } - (NSString *)recursiveDescription diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 3e9bc398..82b2e80a 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -20,6 +20,7 @@ #import "_ASTransitionContext.h" #import "ASLayoutTransition.h" #import "ASEnvironment.h" +#import "ASObjectDescriptionHelpers.h" @protocol _ASDisplayLayerDelegate; @class _ASDisplayLayer; @@ -53,7 +54,7 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo #define TIME_DISPLAYNODE_OPS 0 // If you're using this information frequently, try: (DEBUG || PROFILE) -@interface ASDisplayNode () +@interface ASDisplayNode () { @package _ASPendingState *_pendingViewState; diff --git a/AsyncDisplayKit/Private/ASObjectDescriptionHelpers.h b/AsyncDisplayKit/Private/ASObjectDescriptionHelpers.h new file mode 100644 index 00000000..fbda8cca --- /dev/null +++ b/AsyncDisplayKit/Private/ASObjectDescriptionHelpers.h @@ -0,0 +1,49 @@ +// +// ASObjectDescriptions.h +// AsyncDisplayKit +// +// Created by Adlai Holler on 9/7/16. +// Copyright © 2016 Facebook. All rights reserved. +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Your base class should conform to this and override `-debugDescription` + * to call `[self propertiesForDebugDescription]` and use `ASObjectDescriptionMake` + * to return a string. Subclasses of this base class just need to override + * `propertiesForDebugDescription`, call super, and modify the result as needed. + */ +@protocol ASDebugDescriptionProvider +@required +- (NSMutableArray *)propertiesForDebugDescription; +@end + +/** + * Your base class should conform to this and override `-description` + * to call `[self propertiesForDescription]` and use `ASObjectDescriptionMake` + * to return a string. Subclasses of this base class just need to override + * `propertiesForDescription`, call super, and modify the result as needed. + */ +@protocol ASDescriptionProvider +@required +- (NSMutableArray *)propertiesForDescription; +@end + +ASDISPLAYNODE_EXTERN_C_BEGIN + +/// Returns e.g. +NSString *ASObjectDescriptionMake(id object, NSArray * _Nullable propertyGroups); + +/// Returns e.g. +NSString *ASObjectDescriptionMakeTiny(id object); + +NSString * _Nullable ASStringWithQuotesIfMultiword(NSString * _Nullable string); + +ASDISPLAYNODE_EXTERN_C_END + +NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/Private/ASObjectDescriptionHelpers.m b/AsyncDisplayKit/Private/ASObjectDescriptionHelpers.m new file mode 100644 index 00000000..dfd0df6f --- /dev/null +++ b/AsyncDisplayKit/Private/ASObjectDescriptionHelpers.m @@ -0,0 +1,71 @@ +// +// ASObjectDescriptions.m +// AsyncDisplayKit +// +// Created by Adlai Holler on 9/7/16. +// Copyright © 2016 Facebook. All rights reserved. +// + +#import "ASObjectDescriptionHelpers.h" +#import +#import "NSIndexSet+ASHelpers.h" + +NSString *ASGetDescriptionValueString(id object) { + if ([object isKindOfClass:[NSValue class]]) { + // Use shortened NSValue descriptions + NSValue *value = object; + const char *type = value.objCType; + if (strcmp(type, @encode(CGRect)) == 0) { + CGRect rect = [value CGRectValue]; + return [NSString stringWithFormat:@"(%g %g; %g %g)", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height]; + } else if (strcmp(type, @encode(CGSize)) == 0) { + return NSStringFromCGSize(value.CGSizeValue); + } else if (strcmp(type, @encode(CGPoint)) == 0) { + return NSStringFromCGPoint(value.CGPointValue); + } + } else if ([object isKindOfClass:[NSIndexSet class]]) { + return [object as_smallDescription]; + } else if ([object isKindOfClass:[NSIndexPath class]]) { + // index paths like (0, 7) + NSIndexPath *indexPath = object; + NSMutableArray *strings = [NSMutableArray array]; + for (NSUInteger i = 0; i < indexPath.length; i++) { + [strings addObject:[NSString stringWithFormat:@"%lu", (unsigned long)[indexPath indexAtPosition:i]]]; + } + return [NSString stringWithFormat:@"(%@)", [strings componentsJoinedByString:@", "]]; + } + return [object description]; +} + +NSString *ASObjectDescriptionMake(id object, NSArray *propertyGroups) { + NSMutableString *str = [NSMutableString stringWithFormat:@"<%@: %p", [object class], object]; + + NSMutableArray *components = [NSMutableArray array]; + for (NSDictionary *properties in propertyGroups) { + [properties enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + [components addObject:[NSString stringWithFormat:@"%@ = %@", key, ASGetDescriptionValueString(obj)]]; + }]; + } + if (components.count > 0) { + [str appendString:@"; "]; + [str appendString:[components componentsJoinedByString:@"; "]]; + } + [str appendString:@">"]; + return str; +} + +NSString *ASObjectDescriptionMakeTiny(id object) { + return ASObjectDescriptionMake(object, nil); +} + +NSString *ASStringWithQuotesIfMultiword(NSString *string) { + if (string == nil) { + return nil; + } + + if ([string rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]].location != NSNotFound) { + return [NSString stringWithFormat:@"\"%@\"", string]; + } else { + return string; + } +}