mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-10 16:29:55 +00:00
[ASDisplayNode] Add Event Tracing to Help Debugging (#2243)
* Add some simple event logging for ASDisplayNode Improve the tracing * Add header to copy files phase * Make event header public
This commit is contained in:
parent
8459c1e825
commit
25de53bb13
@ -428,6 +428,10 @@
|
||||
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 */; };
|
||||
CC4C2A771D88E3BF0039ACAB /* ASTraceEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = CC4C2A751D88E3BF0039ACAB /* ASTraceEvent.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CC4C2A781D88E3BF0039ACAB /* ASTraceEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = CC4C2A761D88E3BF0039ACAB /* ASTraceEvent.m */; };
|
||||
CC4C2A791D88E3BF0039ACAB /* ASTraceEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = CC4C2A761D88E3BF0039ACAB /* ASTraceEvent.m */; };
|
||||
CC4C2A7A1D8902350039ACAB /* ASTraceEvent.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CC4C2A751D88E3BF0039ACAB /* ASTraceEvent.h */; };
|
||||
CC54A81C1D70079800296A24 /* ASDispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = CC54A81B1D70077A00296A24 /* ASDispatch.h */; };
|
||||
CC54A81E1D7008B300296A24 /* ASDispatchTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC54A81D1D7008B300296A24 /* ASDispatchTests.m */; };
|
||||
CC7FD9DF1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7FD9DD1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m */; };
|
||||
@ -646,6 +650,7 @@
|
||||
dstPath = "include/$(PRODUCT_NAME)";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
CC4C2A7A1D8902350039ACAB /* ASTraceEvent.h in CopyFiles */,
|
||||
CC88F7AE1D80AF5E000D6D4E /* ASObjectDescriptionHelpers.h in CopyFiles */,
|
||||
F7CE6C981D2CDB5800BE4C15 /* ASInternalHelpers.h in CopyFiles */,
|
||||
F7CE6CB71D2CE2D000BE4C15 /* ASLayoutableExtensibility.h in CopyFiles */,
|
||||
@ -1111,6 +1116,8 @@
|
||||
CC4981B21D1A02BE004E13CC /* ASTableViewThrashTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTableViewThrashTests.m; sourceTree = "<group>"; };
|
||||
CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexSet+ASHelpers.h"; sourceTree = "<group>"; };
|
||||
CC4981BB1D1C7F65004E13CC /* NSIndexSet+ASHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexSet+ASHelpers.m"; sourceTree = "<group>"; };
|
||||
CC4C2A751D88E3BF0039ACAB /* ASTraceEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTraceEvent.h; sourceTree = "<group>"; };
|
||||
CC4C2A761D88E3BF0039ACAB /* ASTraceEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTraceEvent.m; sourceTree = "<group>"; };
|
||||
CC54A81B1D70077A00296A24 /* ASDispatch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASDispatch.h; sourceTree = "<group>"; };
|
||||
CC54A81D1D7008B300296A24 /* ASDispatchTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDispatchTests.m; sourceTree = "<group>"; };
|
||||
CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPhotosFrameworkImageRequest.h; sourceTree = "<group>"; };
|
||||
@ -1428,6 +1435,8 @@
|
||||
058D09E1195D050800B7D73C /* Details */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CC4C2A751D88E3BF0039ACAB /* ASTraceEvent.h */,
|
||||
CC4C2A761D88E3BF0039ACAB /* ASTraceEvent.m */,
|
||||
058D09E2195D050800B7D73C /* _ASDisplayLayer.h */,
|
||||
058D09E3195D050800B7D73C /* _ASDisplayLayer.mm */,
|
||||
058D09E4195D050800B7D73C /* _ASDisplayView.h */,
|
||||
@ -1860,6 +1869,7 @@
|
||||
34EFC7701B701CFA00AD841F /* ASStackLayoutDefines.h in Headers */,
|
||||
764D83D51C8EA515009B4FB8 /* AsyncDisplayKit+Debug.h in Headers */,
|
||||
E5711A2C1C840C81009619D4 /* ASIndexedNodeContext.h in Headers */,
|
||||
CC4C2A771D88E3BF0039ACAB /* ASTraceEvent.h in Headers */,
|
||||
254C6B7B1BF94DF4003EC431 /* ASTextKitRenderer+Positioning.h in Headers */,
|
||||
CC7FD9E21BB603FF005CCB2B /* ASPhotosFrameworkImageRequest.h in Headers */,
|
||||
DE4843DC1C93EAC100A1F33B /* ASLayoutTransition.h in Headers */,
|
||||
@ -2183,6 +2193,7 @@
|
||||
257754AE1BEE44CD00737CA5 /* ASTextKitRenderer+Positioning.mm in Sources */,
|
||||
ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */,
|
||||
AC47D9461B3BB41900AAEE9D /* ASRelativeSize.mm in Sources */,
|
||||
CC4C2A781D88E3BF0039ACAB /* ASTraceEvent.m in Sources */,
|
||||
205F0E121B371BD7007741D0 /* ASScrollDirection.m in Sources */,
|
||||
9C8898BB1C738B9800D6B02E /* ASTextKitFontSizeAdjuster.mm in Sources */,
|
||||
D785F6631A74327E00291744 /* ASScrollNode.m in Sources */,
|
||||
@ -2365,6 +2376,7 @@
|
||||
B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */,
|
||||
0442850A1BAA63FE00D16268 /* ASBatchFetching.m in Sources */,
|
||||
68FC85E61CE29B9400EDD713 /* ASNavigationController.m in Sources */,
|
||||
CC4C2A791D88E3BF0039ACAB /* ASTraceEvent.m in Sources */,
|
||||
34EFC76F1B701CF700AD841F /* ASRatioLayoutSpec.mm in Sources */,
|
||||
254C6B8B1BF94F8A003EC431 /* ASTextKitShadower.mm in Sources */,
|
||||
34EFC7661B701CD200AD841F /* ASRelativeSize.mm in Sources */,
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
|
||||
#import "ASDisplayNode.h"
|
||||
#import "ASLayoutRangeType.h"
|
||||
#import "ASTraceEvent.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -18,6 +19,20 @@ void ASPerformBlockOnMainThread(void (^block)());
|
||||
void ASPerformBlockOnBackgroundThread(void (^block)()); // DISPATCH_QUEUE_PRIORITY_DEFAULT
|
||||
ASDISPLAYNODE_EXTERN_C_END
|
||||
|
||||
#ifndef ASDISPLAYNODE_EVENTLOG_CAPACITY
|
||||
#define ASDISPLAYNODE_EVENTLOG_CAPACITY 20
|
||||
#endif
|
||||
|
||||
#ifndef ASDISPLAYNODE_EVENTLOG_ENABLE
|
||||
#define ASDISPLAYNODE_EVENTLOG_ENABLE DEBUG
|
||||
#endif
|
||||
|
||||
#if ASDISPLAYNODE_EVENTLOG_ENABLE
|
||||
#define ASDisplayNodeLogEvent(node, ...) [node _logEventWithBacktrace:[NSThread callStackSymbols] format:__VA_ARGS__]
|
||||
#else
|
||||
#define ASDisplayNodeLogEvent(node, ...)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Bitmask to indicate what performance measurements the cell should record.
|
||||
*/
|
||||
@ -110,6 +125,20 @@ extern NSString *const ASDisplayNodeLayoutGenerationNumberOfPassesKey;
|
||||
*/
|
||||
+ (void)setRangeModeForMemoryWarnings:(ASLayoutRangeMode)rangeMode;
|
||||
|
||||
#if ASDISPLAYNODE_EVENTLOG_ENABLE
|
||||
|
||||
/**
|
||||
* The primitive event tracing method. You shouldn't call this. Use the ASDisplayNodeLogEvent macro instead.
|
||||
*/
|
||||
- (void)_logEventWithBacktrace:(NSArray<NSString *> *)backtrace format:(NSString *)format, ... NS_FORMAT_FUNCTION(2, 3);
|
||||
|
||||
/**
|
||||
* @abstract The most recent trace events for this node. Max count is ASDISPLAYNODE_EVENTLOG_CAPACITY.
|
||||
*/
|
||||
@property (readonly, copy) NSArray *eventLog;
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -308,6 +308,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
- (void)_initializeInstance
|
||||
{
|
||||
[self _staticInitialize];
|
||||
_eventLogHead = -1;
|
||||
_contentsScaleForDisplay = ASScreenScale();
|
||||
_displaySentinel = [[ASSentinel alloc] init];
|
||||
|
||||
@ -323,6 +324,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
|
||||
_flags.canClearContentsOfLayer = YES;
|
||||
_flags.canCallSetNeedsDisplayOfLayer = YES;
|
||||
ASDisplayNodeLogEvent(self, @"init");
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
@ -428,7 +430,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
// Synchronous nodes may not be able to call the hierarchy notifications, so only enforce for regular nodes.
|
||||
ASDisplayNodeAssert(_flags.synchronous || !ASInterfaceStateIncludesVisible(_interfaceState), @"Node should always be marked invisible before deallocating; interfaceState: %lu, %@", (unsigned long)_interfaceState, self);
|
||||
ASDisplayNodeAssert(_flags.synchronous || !ASInterfaceStateIncludesVisible(_interfaceState), @"Node should always be marked invisible before deallocating. Node: %@", self);
|
||||
|
||||
self.asyncLayer.asyncDelegate = nil;
|
||||
_view.asyncdisplaykit_node = nil;
|
||||
@ -586,6 +588,51 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
}
|
||||
}
|
||||
|
||||
#if ASDISPLAYNODE_EVENTLOG_ENABLE
|
||||
- (void)_logEventWithBacktrace:(NSArray<NSString *> *)backtrace format:(NSString *)format, ...
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
ASTraceEvent *event = [[ASTraceEvent alloc] initWithObject:self
|
||||
backtrace:backtrace
|
||||
format:format
|
||||
arguments:args];
|
||||
va_end(args);
|
||||
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
// Create the array if needed.
|
||||
if (_eventLog == nil) {
|
||||
_eventLog = [NSMutableArray arrayWithCapacity:ASDISPLAYNODE_EVENTLOG_CAPACITY];
|
||||
}
|
||||
|
||||
// Increment the head index.
|
||||
_eventLogHead = (_eventLogHead + 1) % ASDISPLAYNODE_EVENTLOG_CAPACITY;
|
||||
if (_eventLogHead < _eventLog.count) {
|
||||
[_eventLog replaceObjectAtIndex:_eventLogHead withObject:event];
|
||||
} else {
|
||||
[_eventLog insertObject:event atIndex:_eventLogHead];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<ASTraceEvent *> *)eventLog
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
NSUInteger tail = (_eventLogHead + 1);
|
||||
NSUInteger count = _eventLog.count;
|
||||
|
||||
NSMutableArray<ASTraceEvent *> *result = [NSMutableArray array];
|
||||
|
||||
// Start from `tail` and go through array, wrapping around when we exceed end index.
|
||||
for (NSUInteger actualIndex = 0; actualIndex < ASDISPLAYNODE_EVENTLOG_CAPACITY; actualIndex++) {
|
||||
NSInteger ringIndex = (tail + actualIndex) % ASDISPLAYNODE_EVENTLOG_CAPACITY;
|
||||
if (ringIndex < count) {
|
||||
[result addObject:_eventLog[ringIndex]];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
ASDisplayNodeAssert(!_flags.layerBacked, @"Call to -view undefined on layer-backed nodes");
|
||||
@ -1646,6 +1693,7 @@ static bool disableNotificationsForMovingBetweenParents(ASDisplayNode *from, ASD
|
||||
_subnodes = [[NSMutableArray alloc] init];
|
||||
}
|
||||
|
||||
ASDisplayNodeLogEvent(self, @"%@ %@", NSStringFromSelector(_cmd), subnode);
|
||||
[_subnodes addObject:subnode];
|
||||
|
||||
// This call will apply our .hierarchyState to the new subnode.
|
||||
@ -1701,6 +1749,7 @@ static bool disableNotificationsForMovingBetweenParents(ASDisplayNode *from, ASD
|
||||
|
||||
if (!_subnodes)
|
||||
_subnodes = [[NSMutableArray alloc] init];
|
||||
ASDisplayNodeLogEvent(self, @"%@: %@", NSStringFromSelector(_cmd), subnode);
|
||||
[_subnodes insertObject:subnode atIndex:subnodeIndex];
|
||||
[subnode __setSupernode:self];
|
||||
|
||||
@ -1945,6 +1994,7 @@ static NSInteger incrementIfFound(NSInteger i) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASDisplayNodeLogEvent(self, @"%@: %@", NSStringFromSelector(_cmd), subnode);
|
||||
[_subnodes removeObjectIdenticalTo:subnode];
|
||||
|
||||
[subnode __setSupernode:nil];
|
||||
@ -2157,6 +2207,7 @@ static NSInteger incrementIfFound(NSInteger i) {
|
||||
}
|
||||
|
||||
if (supernodeDidChange) {
|
||||
ASDisplayNodeLogEvent(self, @"supernodeDidChange: %@, oldValue = %@", ASObjectDescriptionMakeTiny(newSupernode), ASObjectDescriptionMakeTiny(oldSupernode));
|
||||
// Hierarchy state
|
||||
ASHierarchyState stateToEnterOrExit = (newSupernode ? newSupernode.hierarchyState
|
||||
: oldSupernode.hierarchyState);
|
||||
@ -2422,11 +2473,13 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
ASLayoutableValidateLayout(layout);
|
||||
#endif
|
||||
}
|
||||
ASDisplayNodeLogEvent(self, @"computedLayout: %@", layout);
|
||||
return [layout filteredNodeLayoutTree];
|
||||
} else {
|
||||
// If neither -layoutSpecThatFits: nor -calculateSizeThatFits: is overridden by subclassses, preferredFrameSize should be used,
|
||||
// assume that the default implementation of -calculateSizeThatFits: returns it.
|
||||
CGSize size = [self calculateSizeThatFits:constrainedSize.max];
|
||||
ASDisplayNodeLogEvent(self, @"calculatedSize: %@", NSStringFromCGSize(size));
|
||||
return [ASLayout layoutWithLayoutable:self size:ASSizeRangeClamp(constrainedSize, size) sublayouts:nil];
|
||||
}
|
||||
}
|
||||
@ -2565,6 +2618,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
|
||||
ASDisplayNodeLogEvent(self, @"didLoad");
|
||||
for (ASDisplayNodeDidLoadBlock block in _onDidLoadBlocks) {
|
||||
block(self);
|
||||
}
|
||||
@ -2765,8 +2819,10 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
|
||||
if (nowPreload != wasPreload) {
|
||||
if (nowPreload) {
|
||||
ASDisplayNodeLogEvent(self, @"didEnterPreloadState: %@", NSStringFromASInterfaceState(newState));
|
||||
[self didEnterPreloadState];
|
||||
} else {
|
||||
ASDisplayNodeLogEvent(self, @"didExitPreloadState: %@", NSStringFromASInterfaceState(newState));
|
||||
[self didExitPreloadState];
|
||||
}
|
||||
}
|
||||
@ -2813,8 +2869,10 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
}
|
||||
|
||||
if (nowDisplay) {
|
||||
ASDisplayNodeLogEvent(self, @"didEnterDisplayState: %@", NSStringFromASInterfaceState(newState));
|
||||
[self didEnterDisplayState];
|
||||
} else {
|
||||
ASDisplayNodeLogEvent(self, @"didExitDisplayState: %@", NSStringFromASInterfaceState(newState));
|
||||
[self didExitDisplayState];
|
||||
}
|
||||
}
|
||||
@ -2826,8 +2884,10 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
|
||||
if (nowVisible != wasVisible) {
|
||||
if (nowVisible) {
|
||||
ASDisplayNodeLogEvent(self, @"didEnterVisibleState: %@", NSStringFromASInterfaceState(newState));
|
||||
[self didEnterVisibleState];
|
||||
} else {
|
||||
ASDisplayNodeLogEvent(self, @"didExitVisibleState: %@", NSStringFromASInterfaceState(newState));
|
||||
[self didExitVisibleState];
|
||||
}
|
||||
}
|
||||
@ -2855,6 +2915,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
if (interfaceState == ASInterfaceStateNone) {
|
||||
return; // This method is a no-op with a 0-bitfield argument, so don't bother recursing.
|
||||
}
|
||||
ASDisplayNodeLogEvent(self, @"%@ %@", NSStringFromSelector(_cmd), NSStringFromASInterfaceState(interfaceState));
|
||||
ASDisplayNodePerformBlockOnEveryNode(nil, self, ^(ASDisplayNode *node) {
|
||||
node.interfaceState &= (~interfaceState);
|
||||
});
|
||||
@ -2929,10 +2990,8 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newState != oldState) {
|
||||
LOG(@"setHierarchyState: oldState = %lu, newState = %lu", (unsigned long)oldState, (unsigned long)newState);
|
||||
}
|
||||
|
||||
ASDisplayNodeLogEvent(self, @"setHierarchyState: oldState = %@, newState = %@", NSStringFromASHierarchyState(oldState), NSStringFromASHierarchyState(newState));
|
||||
}
|
||||
|
||||
- (void)enterHierarchyState:(ASHierarchyState)hierarchyState
|
||||
@ -2979,6 +3038,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
ASDisplayNodeLogEvent(self, @"displayWillStart");
|
||||
// in case current node takes longer to display than it's subnodes, treat it as a dependent node
|
||||
[self _pendingNodeWillDisplay:self];
|
||||
|
||||
@ -2989,6 +3049,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
ASDisplayNodeLogEvent(self, @"displayDidFinish");
|
||||
[self _pendingNodeDidDisplay:self];
|
||||
|
||||
[_supernode subnodeDisplayDidFinish:self];
|
||||
@ -3338,15 +3399,15 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority";
|
||||
}
|
||||
|
||||
if (self.layerBacked) {
|
||||
CALayer *rootLayer = self.layer;
|
||||
CALayer *rootLayer = _layer;
|
||||
CALayer *nextLayer = rootLayer;
|
||||
while ((nextLayer = rootLayer.superlayer) != nil) {
|
||||
rootLayer = nextLayer;
|
||||
}
|
||||
|
||||
return [self.layer convertRect:self.threadSafeBounds toLayer:rootLayer];
|
||||
return [_layer convertRect:self.threadSafeBounds toLayer:rootLayer];
|
||||
} else {
|
||||
return [self.view convertRect:self.threadSafeBounds toView:nil];
|
||||
return [_view convertRect:self.threadSafeBounds toView:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@ -3396,6 +3457,7 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority";
|
||||
{
|
||||
if (ASEnvironmentTraitCollectionIsEqualToASEnvironmentTraitCollection(environmentTraitCollection, _environmentState.environmentTraitCollection) == NO) {
|
||||
_environmentState.environmentTraitCollection = environmentTraitCollection;
|
||||
ASDisplayNodeLogEvent(self, @"asyncTraitCollectionDidChange: %@", NSStringFromASEnvironmentTraitCollection(environmentTraitCollection));
|
||||
[self asyncTraitCollectionDidChange];
|
||||
}
|
||||
}
|
||||
@ -3411,7 +3473,7 @@ ASEnvironmentLayoutExtensibilityForwarding
|
||||
|
||||
- (void)asyncTraitCollectionDidChange
|
||||
{
|
||||
|
||||
// Subclass override
|
||||
}
|
||||
|
||||
#if TARGET_OS_TV
|
||||
|
||||
@ -36,7 +36,7 @@ ASDISPLAYNODE_INLINE BOOL ASInterfaceStateIncludesMeasureLayout(ASInterfaceState
|
||||
return ((interfaceState & ASInterfaceStateMeasureLayout) == ASInterfaceStateMeasureLayout);
|
||||
}
|
||||
|
||||
ASDISPLAYNODE_INLINE NSString * _Nonnull NSStringFromASInterfaceState(ASInterfaceState interfaceState)
|
||||
__unused static NSString * _Nonnull NSStringFromASInterfaceState(ASInterfaceState interfaceState)
|
||||
{
|
||||
NSMutableArray *states = [NSMutableArray array];
|
||||
if (interfaceState == ASInterfaceStateNone) {
|
||||
|
||||
@ -77,7 +77,7 @@ extern ASEnvironmentTraitCollection ASEnvironmentTraitCollectionMakeDefault();
|
||||
|
||||
extern ASEnvironmentTraitCollection ASEnvironmentTraitCollectionFromUITraitCollection(UITraitCollection *traitCollection);
|
||||
extern BOOL ASEnvironmentTraitCollectionIsEqualToASEnvironmentTraitCollection(ASEnvironmentTraitCollection lhs, ASEnvironmentTraitCollection rhs);
|
||||
|
||||
extern NSString *NSStringFromASEnvironmentTraitCollection(ASEnvironmentTraitCollection traits);
|
||||
#pragma mark - ASEnvironmentState
|
||||
|
||||
typedef struct ASEnvironmentState {
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
|
||||
#import "ASEnvironmentInternal.h"
|
||||
#import "ASAvailability.h"
|
||||
#import "ASObjectDescriptionHelpers.h"
|
||||
|
||||
ASEnvironmentLayoutOptionsState ASEnvironmentLayoutOptionsStateMakeDefault()
|
||||
{
|
||||
@ -61,6 +62,57 @@ BOOL ASEnvironmentTraitCollectionIsEqualToASEnvironmentTraitCollection(ASEnviron
|
||||
CGSizeEqualToSize(lhs.containerSize, rhs.containerSize);
|
||||
}
|
||||
|
||||
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
|
||||
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceIdiom(UIUserInterfaceIdiom idiom) {
|
||||
switch (idiom) {
|
||||
case UIUserInterfaceIdiomTV:
|
||||
return @"TV";
|
||||
case UIUserInterfaceIdiomPad:
|
||||
return @"Pad";
|
||||
case UIUserInterfaceIdiomPhone:
|
||||
return @"Phone";
|
||||
case UIUserInterfaceIdiomCarPlay:
|
||||
return @"CarPlay";
|
||||
default:
|
||||
return @"Unspecified";
|
||||
}
|
||||
}
|
||||
|
||||
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
|
||||
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIForceTouchCapability(UIForceTouchCapability capability) {
|
||||
switch (capability) {
|
||||
case UIForceTouchCapabilityAvailable:
|
||||
return @"Available";
|
||||
case UIForceTouchCapabilityUnavailable:
|
||||
return @"Unavailable";
|
||||
default:
|
||||
return @"Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
|
||||
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceSizeClass(UIUserInterfaceSizeClass sizeClass) {
|
||||
switch (sizeClass) {
|
||||
case UIUserInterfaceSizeClassCompact:
|
||||
return @"Compact";
|
||||
case UIUserInterfaceSizeClassRegular:
|
||||
return @"Regular";
|
||||
default:
|
||||
return @"Unspecified";
|
||||
}
|
||||
}
|
||||
|
||||
NSString *NSStringFromASEnvironmentTraitCollection(ASEnvironmentTraitCollection traits)
|
||||
{
|
||||
NSMutableArray<NSDictionary *> *props = [NSMutableArray array];
|
||||
[props addObject:@{ @"userInterfaceIdiom": AS_NSStringFromUIUserInterfaceIdiom(traits.userInterfaceIdiom) }];
|
||||
[props addObject:@{ @"containerSize": NSStringFromCGSize(traits.containerSize) }];
|
||||
[props addObject:@{ @"horizontalSizeClass": AS_NSStringFromUIUserInterfaceSizeClass(traits.horizontalSizeClass) }];
|
||||
[props addObject:@{ @"verticalSizeClass": AS_NSStringFromUIUserInterfaceSizeClass(traits.verticalSizeClass) }];
|
||||
[props addObject:@{ @"forceTouchCapability": AS_NSStringFromUIForceTouchCapability(traits.forceTouchCapability) }];
|
||||
return ASObjectDescriptionMakeWithoutObject(props);
|
||||
}
|
||||
|
||||
ASEnvironmentState ASEnvironmentStateMakeDefault()
|
||||
{
|
||||
return (ASEnvironmentState) {
|
||||
|
||||
25
AsyncDisplayKit/Details/ASTraceEvent.h
Normal file
25
AsyncDisplayKit/Details/ASTraceEvent.h
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// ASTraceEvent.h
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Adlai Holler on 9/13/16.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface ASTraceEvent : NSObject
|
||||
|
||||
/**
|
||||
* This method is dealloc safe.
|
||||
*/
|
||||
- (instancetype)initWithObject:(id)object
|
||||
backtrace:(NSArray<NSString *> *)backtrace
|
||||
format:(NSString *)format
|
||||
arguments:(va_list)arguments NS_FORMAT_FUNCTION(3,0);
|
||||
|
||||
@property (nonatomic, readonly) NSArray<NSString *> *backtrace;
|
||||
@property (nonatomic, strong, readonly) NSString *message;
|
||||
@property (nonatomic, readonly) NSTimeInterval timestamp;
|
||||
|
||||
@end
|
||||
60
AsyncDisplayKit/Details/ASTraceEvent.m
Normal file
60
AsyncDisplayKit/Details/ASTraceEvent.m
Normal file
@ -0,0 +1,60 @@
|
||||
//
|
||||
// ASTraceEvent.m
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Adlai Holler on 9/13/16.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ASTraceEvent.h"
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import "ASObjectDescriptionHelpers.h"
|
||||
|
||||
@interface ASTraceEvent ()
|
||||
@property (nonatomic, strong, readonly) NSString *objectDescription;
|
||||
@property (nonatomic, strong, readonly) NSString *threadDescription;
|
||||
@end
|
||||
|
||||
@implementation ASTraceEvent
|
||||
|
||||
- (instancetype)initWithObject:(id)object backtrace:(NSArray<NSString *> *)backtrace format:(NSString *)format arguments:(va_list)args
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
static NSTimeInterval refTime;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
refTime = CACurrentMediaTime();
|
||||
});
|
||||
|
||||
// Create the format string passed to us.
|
||||
_message = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
|
||||
_objectDescription = ASObjectDescriptionMakeTiny(object);
|
||||
|
||||
NSThread *thread = [NSThread currentThread];
|
||||
NSString *threadDescription = thread.name;
|
||||
if (threadDescription.length == 0) {
|
||||
if ([thread isMainThread]) {
|
||||
threadDescription = @"Main";
|
||||
} else {
|
||||
// Want these to be 4-chars to line up with "Main". It's possible that a collision could happen
|
||||
// here but it's so unbelievably likely to impact development, the risk is acceptable.
|
||||
NSString *ptrString = [NSString stringWithFormat:@"%p", thread];
|
||||
threadDescription = [ptrString substringFromIndex:MAX(0, ptrString.length - 4)];
|
||||
}
|
||||
}
|
||||
_threadDescription = threadDescription;
|
||||
|
||||
_backtrace = backtrace;
|
||||
_timestamp = CACurrentMediaTime() - refTime;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"<%@ (%@) t=%7.3f: %@>", _objectDescription, _threadDescription, _timestamp, _message];
|
||||
}
|
||||
|
||||
@end
|
||||
@ -50,16 +50,47 @@ typedef NS_OPTIONS(NSUInteger, ASHierarchyState)
|
||||
ASHierarchyStateLayoutPending = 1 << 3
|
||||
};
|
||||
|
||||
inline BOOL ASHierarchyStateIncludesLayoutPending(ASHierarchyState hierarchyState)
|
||||
ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesLayoutPending(ASHierarchyState hierarchyState)
|
||||
{
|
||||
return ((hierarchyState & ASHierarchyStateLayoutPending) == ASHierarchyStateLayoutPending);
|
||||
}
|
||||
|
||||
inline BOOL ASHierarchyStateIncludesRangeManaged(ASHierarchyState hierarchyState)
|
||||
ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesRangeManaged(ASHierarchyState hierarchyState)
|
||||
{
|
||||
return ((hierarchyState & ASHierarchyStateRangeManaged) == ASHierarchyStateRangeManaged);
|
||||
}
|
||||
|
||||
ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesRasterized(ASHierarchyState hierarchyState)
|
||||
{
|
||||
return ((hierarchyState & ASHierarchyStateRasterized) == ASHierarchyStateRasterized);
|
||||
}
|
||||
|
||||
ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesTransitioningSupernodes(ASHierarchyState hierarchyState)
|
||||
{
|
||||
return ((hierarchyState & ASHierarchyStateTransitioningSupernodes) == ASHierarchyStateTransitioningSupernodes);
|
||||
}
|
||||
|
||||
__unused static NSString * _Nonnull NSStringFromASHierarchyState(ASHierarchyState hierarchyState)
|
||||
{
|
||||
NSMutableArray *states = [NSMutableArray array];
|
||||
if (hierarchyState == ASHierarchyStateNormal) {
|
||||
[states addObject:@"Normal"];
|
||||
}
|
||||
if (ASHierarchyStateIncludesRangeManaged(hierarchyState)) {
|
||||
[states addObject:@"RangeManaged"];
|
||||
}
|
||||
if (ASHierarchyStateIncludesLayoutPending(hierarchyState)) {
|
||||
[states addObject:@"LayoutPending"];
|
||||
}
|
||||
if (ASHierarchyStateIncludesRasterized(hierarchyState)) {
|
||||
[states addObject:@"Rasterized"];
|
||||
}
|
||||
if (ASHierarchyStateIncludesTransitioningSupernodes(hierarchyState)) {
|
||||
[states addObject:@"TransitioningSupernodes"];
|
||||
}
|
||||
return [NSString stringWithFormat:@"{ %@ }", [states componentsJoinedByString:@" | "]];
|
||||
}
|
||||
|
||||
@interface ASDisplayNode ()
|
||||
{
|
||||
@protected
|
||||
|
||||
@ -121,7 +121,10 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
|
||||
|
||||
UIEdgeInsets _hitTestSlop;
|
||||
NSMutableArray *_subnodes;
|
||||
|
||||
NSMutableArray<ASTraceEvent *> *_eventLog;
|
||||
// The index of the most recent log entry. -1 until first entry.
|
||||
NSInteger _eventLogHead;
|
||||
|
||||
// Main thread only
|
||||
BOOL _automaticallyManagesSubnodes;
|
||||
_ASTransitionContext *_pendingLayoutTransitionContext;
|
||||
|
||||
@ -36,12 +36,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
ASDISPLAYNODE_EXTERN_C_BEGIN
|
||||
|
||||
/**
|
||||
* Returns e.g. <MYObject: 0xFFFFFFFF; name = "Object Name"; frame = (0 0; 50 50)>
|
||||
*
|
||||
* Note: `object` param is autoreleasing so that this function is dealloc-safe.
|
||||
* No, unsafe_unretained isn't acceptable here – the optimizer may deallocate object early.
|
||||
*/
|
||||
/// Useful for structs etc. Returns e.g. { position = (0 0); frame = (0 0; 50 50) }
|
||||
NSString *ASObjectDescriptionMakeWithoutObject(NSArray<NSDictionary *> * _Nullable propertyGroups);
|
||||
|
||||
/// Returns e.g. <MYObject: 0xFFFFFFFF; name = "Object Name"; frame = (0 0; 50 50)>
|
||||
NSString *ASObjectDescriptionMake(__autoreleasing id object, NSArray<NSDictionary *> * _Nullable propertyGroups);
|
||||
|
||||
/**
|
||||
|
||||
@ -10,7 +10,8 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "NSIndexSet+ASHelpers.h"
|
||||
|
||||
NSString *ASGetDescriptionValueString(id object) {
|
||||
NSString *ASGetDescriptionValueString(id object)
|
||||
{
|
||||
if ([object isKindOfClass:[NSValue class]]) {
|
||||
// Use shortened NSValue descriptions
|
||||
NSValue *value = object;
|
||||
@ -37,18 +38,33 @@ NSString *ASGetDescriptionValueString(id object) {
|
||||
return [object description];
|
||||
}
|
||||
|
||||
NSString *ASObjectDescriptionMake(__autoreleasing id object, NSArray<NSDictionary *> *propertyGroups) {
|
||||
NSMutableString *str = [NSMutableString stringWithFormat:@"<%@: %p", [object class], object];
|
||||
|
||||
NSString *_ASObjectDescriptionMakePropertyList(NSArray<NSDictionary *> * _Nullable propertyGroups)
|
||||
{
|
||||
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:@"; "]];
|
||||
return [components componentsJoinedByString:@"; "];
|
||||
}
|
||||
|
||||
NSString *ASObjectDescriptionMakeWithoutObject(NSArray<NSDictionary *> * _Nullable propertyGroups)
|
||||
{
|
||||
return [NSString stringWithFormat:@"{ %@ }", _ASObjectDescriptionMakePropertyList(propertyGroups)];
|
||||
}
|
||||
|
||||
NSString *ASObjectDescriptionMake(__autoreleasing id object, NSArray<NSDictionary *> *propertyGroups)
|
||||
{
|
||||
if (object == nil) {
|
||||
return @"(null)";
|
||||
}
|
||||
|
||||
NSMutableString *str = [NSMutableString stringWithFormat:@"<%@: %p", [object class], object];
|
||||
|
||||
NSString *propList = _ASObjectDescriptionMakePropertyList(propertyGroups);
|
||||
if (propList.length > 0) {
|
||||
[str appendFormat:@"; %@", propList];
|
||||
}
|
||||
[str appendString:@">"];
|
||||
return str;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user