[6.29] Better logging

This commit is contained in:
Adlai Holler
2017-06-30 11:01:43 -07:00
parent 928bc9f8ff
commit 9110fbacf4
43 changed files with 735 additions and 263 deletions
@@ -372,6 +372,8 @@
CCA282D11E9EBF6C0037E8B7 /* ASTipsWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA282CF1E9EBF6C0037E8B7 /* ASTipsWindow.m */; };
CCA5F62C1EEC9E9B0060C137 /* NSInvocation+ASTestHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA5F62B1EEC9E9B0060C137 /* NSInvocation+ASTestHelpers.m */; };
CCA5F62E1EECC2A80060C137 /* ASAssert.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA5F62D1EECC2A80060C137 /* ASAssert.m */; };
CCB1F95A1EFB60A5009C7475 /* ASLog.m in Sources */ = {isa = PBXBuildFile; fileRef = CCB1F9591EFB60A5009C7475 /* ASLog.m */; };
CCB1F95C1EFB6350009C7475 /* ASSignpost.h in Headers */ = {isa = PBXBuildFile; fileRef = CCB1F95B1EFB6316009C7475 /* ASSignpost.h */; };
CCB2F34D1D63CCC6004E6DE9 /* ASDisplayNodeSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCB2F34C1D63CCC6004E6DE9 /* ASDisplayNodeSnapshotTests.m */; };
CCB338E41EEE11160081F21A /* OCMockObject+ASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CCB338E31EEE11160081F21A /* OCMockObject+ASAdditions.m */; };
CCB338E71EEE27760081F21A /* ASTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = CCB338E61EEE27760081F21A /* ASTestCase.m */; };
@@ -835,6 +837,8 @@
CCA5F62A1EEC9E9B0060C137 /* NSInvocation+ASTestHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSInvocation+ASTestHelpers.h"; sourceTree = "<group>"; };
CCA5F62B1EEC9E9B0060C137 /* NSInvocation+ASTestHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSInvocation+ASTestHelpers.m"; sourceTree = "<group>"; };
CCA5F62D1EECC2A80060C137 /* ASAssert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASAssert.m; sourceTree = "<group>"; };
CCB1F9591EFB60A5009C7475 /* ASLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASLog.m; sourceTree = "<group>"; };
CCB1F95B1EFB6316009C7475 /* ASSignpost.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASSignpost.h; sourceTree = "<group>"; };
CCB2F34C1D63CCC6004E6DE9 /* ASDisplayNodeSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDisplayNodeSnapshotTests.m; sourceTree = "<group>"; };
CCB338E21EEE11160081F21A /* OCMockObject+ASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OCMockObject+ASAdditions.h"; sourceTree = "<group>"; };
CCB338E31EEE11160081F21A /* OCMockObject+ASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OCMockObject+ASAdditions.m"; sourceTree = "<group>"; };
@@ -1391,6 +1395,8 @@
058D0A44195D058D00B7D73C /* ASBaseDefines.h */,
1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */,
0516FA3B1A15563400B4EBED /* ASLog.h */,
CCB1F9591EFB60A5009C7475 /* ASLog.m */,
CCB1F95B1EFB6316009C7475 /* ASSignpost.h */,
);
path = Base;
sourceTree = "<group>";
@@ -1682,6 +1688,7 @@
9019FBBF1ED8061D00C45F72 /* ASYogaUtilities.h in Headers */,
DE84918D1C8FFF2B003D89E9 /* ASRunLoopQueue.h in Headers */,
CC0F88621E4281E200576FED /* ASSectionController.h in Headers */,
CCB1F95C1EFB6350009C7475 /* ASSignpost.h in Headers */,
A2763D7A1CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h in Headers */,
34EFC7611B701C9C00AD841F /* ASBackgroundLayoutSpec.h in Headers */,
B35062591B010F070018CF92 /* ASBaseDefines.h in Headers */,
@@ -2168,6 +2175,7 @@
B35061F91B010EFD0018CF92 /* ASControlNode.mm in Sources */,
8021EC1F1D2B00B100799119 /* UIImage+ASConvenience.m in Sources */,
B35062181B010EFD0018CF92 /* ASDataController.mm in Sources */,
CCB1F95A1EFB60A5009C7475 /* ASLog.m in Sources */,
767E7F8E1C90191D0066C000 /* AsyncDisplayKit+Debug.m in Sources */,
CCCCCCD61EC3EF060087FE10 /* ASTextDebugOption.m in Sources */,
34EFC75C1B701BD200AD841F /* ASDimension.mm in Sources */,
+2 -2
View File
@@ -322,13 +322,13 @@
if (ip != nil) {
[result addObject:@{ @"indexPath" : ip }];
}
[result addObject:@{ @"collectionNode" : ASObjectDescriptionMakeTiny(owningNode) }];
[result addObject:@{ @"collectionNode" : owningNode }];
} else if ([owningNode isKindOfClass:[ASTableNode class]]) {
NSIndexPath *ip = [(ASTableNode *)owningNode indexPathForNode:self];
if (ip != nil) {
[result addObject:@{ @"indexPath" : ip }];
}
[result addObject:@{ @"tableNode" : ASObjectDescriptionMakeTiny(owningNode) }];
[result addObject:@{ @"tableNode" : owningNode }];
} else if ([scrollView isKindOfClass:[ASCollectionView class]]) {
NSIndexPath *ip = [(ASCollectionView *)scrollView indexPathForNode:self];
+18 -3
View File
@@ -820,6 +820,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
if (_batchUpdateCount == 0) {
_changeSet = [[_ASHierarchyChangeSet alloc] initWithOldData:[_dataController itemCountsFromDataSource]];
_changeSet.rootActivity = as_activity_create("Perform async collection update", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
_changeSet.submitActivity = as_activity_create("Submit changes for collection update", _changeSet.rootActivity, OS_ACTIVITY_FLAG_DEFAULT);
}
_batchUpdateCount++;
}
@@ -837,6 +839,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
if (_batchUpdateCount == 0) {
_ASHierarchyChangeSet *changeSet = _changeSet;
// Nil out _changeSet before forwarding to _dataController to allow the change set to cause subsequent batch updates on the same run loop
_changeSet = nil;
changeSet.animated = animated;
@@ -848,8 +851,12 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
{
ASDisplayNodeAssertMainThread();
[self beginUpdates];
if (updates) {
updates();
as_activity_scope(_changeSet.rootActivity);
{
as_activity_scope(_changeSet.submitActivity);
if (updates) {
updates();
}
}
[self endUpdatesAnimated:animated completion:completion];
}
@@ -1595,10 +1602,12 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
- (void)_beginBatchFetching
{
as_activity_scope(as_activity_create("Batch fetch for collection node", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
[_batchContext beginBatchFetching];
if (_asyncDelegateFlags.collectionNodeWillBeginBatchFetch) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
GET_COLLECTIONNODE_OR_RETURN(collectionNode, (void)0);
as_log_debug(ASCollectionLog(), "Beginning batch fetch for %@ with context %@", collectionNode, _batchContext);
[_asyncDelegate collectionNode:collectionNode willBeginBatchFetchWithContext:_batchContext];
});
} else if (_asyncDelegateFlags.collectionViewWillBeginBatchFetch) {
@@ -1915,9 +1924,11 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
}
ASPerformBlockWithoutAnimation(!changeSet.animated, ^{
if(changeSet.includesReloadData) {
as_activity_scope(as_activity_create("Commit collection update", changeSet.rootActivity, OS_ACTIVITY_FLAG_DEFAULT));
if (changeSet.includesReloadData) {
_superIsPendingDataLoad = YES;
[super reloadData];
as_log_debug(ASCollectionLog(), "Did reloadData %@", self.collectionNode);
[changeSet executeCompletionHandlerWithFinished:YES];
} else {
[_layoutFacilitator collectionViewWillPerformBatchUpdates];
@@ -1954,13 +1965,17 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
numberOfUpdates++;
}
} completion:^(BOOL finished){
as_activity_scope(as_activity_create("Handle collection update completion", changeSet.rootActivity, OS_ACTIVITY_FLAG_DEFAULT));
as_log_verbose(ASCollectionLog(), "Update animation finished %{public}@", self.collectionNode);
// Flush any range changes that happened as part of the update animations ending.
[_rangeController updateIfNeeded];
[self _scheduleCheckForBatchFetchingForNumberOfChanges:numberOfUpdates];
[changeSet executeCompletionHandlerWithFinished:finished];
}];
as_log_debug(ASCollectionLog(), "Completed batch update %{public}@", self.collectionNode);
// Flush any range changes that happened as part of submitting the update.
as_activity_scope(changeSet.rootActivity);
[_rangeController updateIfNeeded];
}
});
+44 -16
View File
@@ -15,6 +15,7 @@
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASLayoutElementStylePrivate.h>
#import <AsyncDisplayKit/ASLog.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkSubclasses.h>
@@ -127,13 +128,8 @@ ASLayoutElementStyleExtensibilityForwarding
ASPrimitiveTraitCollectionDeprecatedImplementation
@end
#pragma mark -
#pragma mark - ASLayoutElementAsciiArtProtocol
@implementation ASDisplayNode (ASLayoutElementAsciiArtProtocol)
- (NSString *)asciiArtString
{
return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]];
@@ -141,11 +137,11 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
- (NSString *)asciiArtName
{
NSString *string = NSStringFromClass([self class]);
NSMutableString *result = [NSMutableString stringWithCString:object_getClassName(self) encoding:NSASCIIStringEncoding];
if (_debugName) {
string = [string stringByAppendingString:[NSString stringWithFormat:@"\"%@\"", _debugName]];
[result appendFormat:@" (%@)", _debugName];
}
return string;
return result;
}
@end
@@ -222,6 +218,7 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
*/
- (void)_setNeedsLayoutFromAbove
{
as_activity_scope(as_activity_create("Set needs layout from above", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
ASDisplayNodeAssertThreadAffinity(self);
// Mark the node for layout in the next layout pass
@@ -315,6 +312,8 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
}
}
as_activity_scope(as_activity_create("Update node layout for current bounds", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
as_log_verbose(ASLayoutLog(), "Node %@, bounds size %@, calculatedSize %@, calculatedIsDirty %d", self, NSStringFromCGSize(boundsSizeForLayout), NSStringFromCGSize(_calculatedDisplayNodeLayout->layout.size), _calculatedDisplayNodeLayout->isDirty());
// _calculatedDisplayNodeLayout is not reusable we need to transition to a new one
[self cancelLayoutTransition];
@@ -332,7 +331,20 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
// nextLayout was likely created by a call to layoutThatFits:, check if it is valid and can be applied.
// If our bounds size is different than it, or invalid, recalculate. Use #define to avoid nullptr->
if (nextLayout == nullptr || nextLayout->isDirty() == YES || layoutSizeDifferentFromBounds) {
BOOL pendingLayoutApplicable = NO;
if (nextLayout == nullptr) {
as_log_verbose(ASLayoutLog(), "No pending layout.");
} else if (nextLayout->isDirty()) {
as_log_verbose(ASLayoutLog(), "Pending layout is invalid.");
} else if (layoutSizeDifferentFromBounds) {
as_log_verbose(ASLayoutLog(), "Pending layout size %@ doesn't match bounds size.", NSStringFromCGSize(nextLayout->layout.size));
} else {
as_log_verbose(ASLayoutLog(), "Using pending layout %@.", nextLayout->layout);
pendingLayoutApplicable = YES;
}
if (!pendingLayoutApplicable) {
as_log_verbose(ASLayoutLog(), "Measuring with previous constrained size.");
// Use the last known constrainedSize passed from a parent during layout (if never, use bounds).
ASSizeRange constrainedSize = [self _locked_constrainedSizeForLayoutPass];
ASLayout *layout = [self calculateLayoutThatFits:constrainedSize
@@ -350,6 +362,7 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
// This can occur for either pre-calculated or newly-calculated layouts.
if (nextLayout->requestedLayoutFromAbove == NO
&& CGSizeEqualToSize(boundsSizeForLayout, nextLayout->layout.size) == NO) {
as_log_verbose(ASLayoutLog(), "Layout size doesn't match bounds size. Requesting layout from above.");
// The layout that we have specifies that this node (self) would like to be a different size
// than it currently is. Because that size has been computed within the constrainedSize, we
// expect that calling setNeedsLayoutFromAbove will result in our parent resizing us to this.
@@ -506,10 +519,13 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
measurementCompletion:(void(^)())completion
{
ASDisplayNodeAssertMainThread();
as_activity_scope(as_activity_create("Transition node layout", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
as_log_debug(ASLayoutLog(), "Transition layout for %@ sizeRange %@ anim %d asyncMeasure %d", self, NSStringFromASSizeRange(constrainedSize), animated, shouldMeasureAsync);
if (constrainedSize.max.width <= 0.0 || constrainedSize.max.height <= 0.0) {
// Using CGSizeZero for the sizeRange can cause negative values in client layout code.
// Most likely called transitionLayout: without providing a size, before first layout pass.
as_log_verbose(ASLayoutLog(), "Ignoring transition due to bad size range.");
return;
}
@@ -526,9 +542,14 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
// Every new layout transition has a transition id associated to check in subsequent transitions for cancelling
int32_t transitionID = [self _startNewTransition];
as_log_verbose(ASLayoutLog(), "Transition ID is %d", transitionID);
// NOTE: This block captures self. It's cheaper than hitting the weak table.
asdisplaynode_iscancelled_block_t isCancelled = ^{
return (BOOL)(_transitionID != transitionID);
BOOL result = (_transitionID != transitionID);
if (result) {
as_log_verbose(ASLayoutLog(), "Transition %d canceled, superseded by %d", transitionID, _transitionID.load());
}
return result;
};
// Move all subnodes in layout pending state for this transition
@@ -573,6 +594,7 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
if (isCancelled()) {
return;
}
as_activity_scope(as_activity_create("Commit layout transition", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
ASLayoutTransition *pendingLayoutTransition;
_ASTransitionContext *pendingLayoutTransitionContext;
{
@@ -598,10 +620,13 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
}
// Apply complete layout transitions for all subnodes
ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
[node _completePendingLayoutTransition];
node.hierarchyState &= (~ASHierarchyStateLayoutPending);
});
{
as_activity_scope(as_activity_create("Complete pending layout transitions for subtree", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
[node _completePendingLayoutTransition];
node.hierarchyState &= (~ASHierarchyStateLayoutPending);
});
}
// Measurement pass completion
// Give the subclass a change to hook into before calling the completion block
@@ -614,7 +639,10 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
[pendingLayoutTransition applySubnodeInsertions];
// Kick off animating the layout transition
[self animateLayoutTransition:pendingLayoutTransitionContext];
{
as_activity_scope(as_activity_create("Animate layout transition", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
[self animateLayoutTransition:pendingLayoutTransitionContext];
}
// Mark transaction as finished
[self _finishOrCancelTransition];
@@ -805,7 +833,7 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
*/
- (void)_completeLayoutTransition:(ASLayoutTransition *)layoutTransition
{
// Layout transition is not supported for nodes that are not have automatic subnode management enabled
// Layout transition is not supported for nodes that do not have automatic subnode management enabled
if (layoutTransition == nil || self.automaticallyManagesSubnodes == NO) {
return;
}
+2 -2
View File
@@ -514,7 +514,7 @@ NS_ASSUME_NONNULL_BEGIN
@end
#define ASDisplayNodeAssertThreadAffinity(viewNode) ASDisplayNodeAssert(!viewNode || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created")
#define ASDisplayNodeCAssertThreadAffinity(viewNode) ASDisplayNodeCAssert(!viewNode || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created")
#define ASDisplayNodeAssertThreadAffinity(viewNode) ASDisplayNodeAssert(!viewNode || ASMainThreadAssertionsAreDisabled() || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created")
#define ASDisplayNodeCAssertThreadAffinity(viewNode) ASDisplayNodeCAssert(!viewNode || ASMainThreadAssertionsAreDisabled() || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created")
NS_ASSUME_NONNULL_END
-3
View File
@@ -748,9 +748,6 @@ extern NSInteger const ASDefaultDrawingPriority;
@end
@interface ASDisplayNode (ASLayoutElementAsciiArtProtocol) <ASLayoutElementAsciiArtProtocol>
@end
@interface ASDisplayNode (ASLayout)
/** @name Managing dimensions */
+52 -17
View File
@@ -41,18 +41,14 @@
#import <AsyncDisplayKit/ASLayoutElementStylePrivate.h>
#import <AsyncDisplayKit/ASLayoutSpec.h>
#import <AsyncDisplayKit/ASLayoutSpecPrivate.h>
#import <AsyncDisplayKit/ASLog.h>
#import <AsyncDisplayKit/ASRunLoopQueue.h>
#import <AsyncDisplayKit/ASSignpost.h>
#import <AsyncDisplayKit/ASTraitCollection.h>
#import <AsyncDisplayKit/ASWeakProxy.h>
#import <AsyncDisplayKit/ASResponderChainEnumerator.h>
#import <AsyncDisplayKit/ASTipsController.h>
#if ASDisplayNodeLoggingEnabled
#define LOG(...) NSLog(__VA_ARGS__)
#else
#define LOG(...)
#endif
// Conditionally time these scopes to our debug ivars (only exist in debug/profile builds)
#if TIME_DISPLAYNODE_OPS
#define TIME_SCOPED(outVar) ASDN::ScopeTimer t(outVar)
@@ -452,10 +448,10 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
for (Ivar ivar : ivars) {
id value = object_getIvar(self, ivar);
if (ASClassRequiresMainThreadDeallocation(object_getClass(value))) {
LOG(@"Trampolining ivar '%s' value %@ for main deallocation.", ivar_getName(ivar), value);
as_log_debug(ASMainThreadDeallocationLog(), "%@: Trampolining ivar '%s' value %@ for main deallocation.", self, ivar_getName(ivar), value);
ASPerformMainThreadDeallocation(value);
} else {
LOG(@"Not trampolining ivar '%s' value %@.", ivar_getName(ivar), value);
as_log_debug(ASMainThreadDeallocationLog(), "%@: Not trampolining ivar '%s' value %@.", self, ivar_getName(ivar), value);
}
}
}
@@ -512,16 +508,16 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
// If it's `id` we have to include it just in case.
resultIvars[resultCount] = ivar;
resultCount += 1;
LOG(@"Marking ivar '%s' for possible main deallocation due to type id", ivar_getName(ivar));
as_log_debug(ASMainThreadDeallocationLog(), "%@: Marking ivar '%s' for possible main deallocation due to type id", self, ivar_getName(ivar));
} else {
// If it's an ivar with a static type, check the type.
Class c = ASGetClassFromType(type);
if (ASClassRequiresMainThreadDeallocation(c)) {
resultIvars[resultCount] = ivar;
resultCount += 1;
LOG(@"Marking ivar '%s' for main deallocation due to class %@", ivar_getName(ivar), c);
as_log_debug(ASMainThreadDeallocationLog(), "%@: Marking ivar '%s' for main deallocation due to class %@", self, ivar_getName(ivar), c);
} else {
LOG(@"Skipping ivar '%s' for main deallocation.", ivar_getName(ivar));
as_log_debug(ASMainThreadDeallocationLog(), "%@: Skipping ivar '%s' for main deallocation.", self, ivar_getName(ivar));
}
}
}
@@ -629,6 +625,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
ASDisplayNodeAssertMainThread();
ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
ASDisplayNodeLogEvent(self, @"didLoad");
as_log_verbose(ASNodeLog(), "didLoad %@", self);
TIME_SCOPED(_debugTimeForDidLoad);
[self didLoad];
@@ -914,7 +911,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
// Performing layout on a zero-bounds view often results in frame calculations
// with negative sizes after applying margins, which will cause
// measureWithSizeRange: on subnodes to assert.
LOG(@"Warning: No size given for node before node was trying to layout itself: %@. Please provide a frame for the node.", self);
as_log_debug(OS_LOG_DISABLED, "Warning: No size given for node before node was trying to layout itself: %@. Please provide a frame for the node.", self);
return;
}
@@ -962,6 +959,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
// This is fast enough to do it unconditionally.
auto key = ASPthreadStaticKey(NULL);
BOOL isRootCall = (pthread_getspecific(key) == NULL);
as_activity_scope_verbose(as_activity_create("Calculate node layout", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
as_log_verbose(ASLayoutLog(), "Calculating layout for %@ sizeRange %@", self, NSStringFromASSizeRange(constrainedSize));
if (isRootCall) {
pthread_setspecific(key, kCFBooleanTrue);
ASSignpostStart(ASSignpostCalculateLayout);
@@ -970,6 +969,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
ASSizeRange styleAndParentSize = ASLayoutElementSizeResolve(self.style.size, parentSize);
const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, styleAndParentSize);
ASLayout *result = [self calculateLayoutThatFits:resolvedRange];
as_log_verbose(ASLayoutLog(), "Calculated layout %@", result);
if (isRootCall) {
pthread_setspecific(key, NULL);
@@ -1024,6 +1024,12 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
// Get layout element from the node
id<ASLayoutElement> layoutElement = [self _locked_layoutElementThatFits:constrainedSize];
#if ASEnableVerboseLogging
for (NSString *asciiLine in [[layoutElement asciiArtString] componentsSeparatedByString:@"\n"]) {
as_log_verbose(ASLayoutLog(), "%@", asciiLine);
}
#endif
// Certain properties are necessary to set on an element of type ASLayoutSpec
if (layoutElement.layoutElementType == ASLayoutElementTypeLayoutSpec) {
@@ -1304,6 +1310,7 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
}];
});
as_log_verbose(ASRenderLog(), "%s %@", sel_getName(_cmd), node);
[renderQueue enqueue:node];
}
@@ -1906,6 +1913,7 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
- (void)_insertSubnode:(ASDisplayNode *)subnode atSubnodeIndex:(NSInteger)subnodeIndex sublayerIndex:(NSInteger)sublayerIndex andRemoveSubnode:(ASDisplayNode *)oldSubnode
{
ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
as_log_verbose(ASNodeLog(), "Insert subnode %@ at index %zd of %@ and remove subnode %@", subnode, subnodeIndex, self, oldSubnode);
if (subnode == nil || subnode == self) {
ASDisplayNodeFailAssert(@"Cannot insert a nil subnode or self as subnode");
@@ -2337,6 +2345,11 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
- (void)_removeFromSupernode:(ASDisplayNode *)supernode view:(UIView *)view layer:(CALayer *)layer
{
// TODO: Should we simply return early if supernode is nil? This currently gets called for every insertSubnode.
if (supernode != nil) {
as_log_verbose(ASNodeLog(), "Remove %@ from supernode %@", self, supernode);
}
// Clear supernode's reference to us before removing the view from the hierarchy, as _ASDisplayView
// will trigger us to clear our _supernode pointer in willMoveToSuperview:nil.
// This may result in removing the last strong reference, triggering deallocation after this method.
@@ -2688,7 +2701,7 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
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));
ASDisplayNodeLogEvent(self, @"%s %@", sel_getName(_cmd), NSStringFromASInterfaceState(interfaceState));
ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode *node) {
node.interfaceState &= (~interfaceState);
});
@@ -2696,6 +2709,8 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
- (void)recursivelySetInterfaceState:(ASInterfaceState)newInterfaceState
{
as_activity_scope(as_activity_create("Recursively set interface state", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
// Instead of each node in the recursion assuming it needs to schedule itself for display,
// setInterfaceState: skips this when handling range-managed nodes (our whole subtree has this set).
// If our range manager intends for us to be displayed right now, and didn't before, get started!
@@ -2819,7 +2834,14 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
}
}
ASDisplayNodeLogEvent(self, @"interfaceStateDidChange: %@, old: %@", NSStringFromASInterfaceState(newState), NSStringFromASInterfaceState(oldState));
// Log this change, unless it's just the node going from {} -> {Measure} because that change happens
// for all cell nodes and it isn't currently meaningful.
BOOL isJustEnteringMeasure = (oldState == ASInterfaceStateNone && newState == ASInterfaceStateMeasureLayout);
if (!isJustEnteringMeasure) {
as_log_verbose(ASNodeLog(), "%s %@ %@", sel_getName(_cmd), NSStringFromASInterfaceStateChange(oldState, newState), self);
}
ASDisplayNodeLogEvent(self, @"interfaceStateDidChange: %@", NSStringFromASInterfaceStateChange(oldState, newState));
[self interfaceStateDidChange:newState fromState:oldState];
}
@@ -3172,9 +3194,19 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
- (NSMutableArray<NSDictionary *> *)propertiesForDescription
{
NSMutableArray<NSDictionary *> *result = [NSMutableArray array];
if (self.debugName.length > 0) {
[result addObject:@{ @"debugName" : ASStringWithQuotesIfMultiword(self.debugName) }];
ASPushMainThreadAssertionsDisabled();
NSString *debugName = self.debugName;
if (debugName.length > 0) {
[result addObject:@{ (id)kCFNull : ASStringWithQuotesIfMultiword(debugName) }];
}
NSString *axId = self.accessibilityIdentifier;
if (axId.length > 0) {
[result addObject:@{ (id)kCFNull : ASStringWithQuotesIfMultiword(axId) }];
}
ASPopMainThreadAssertionsDisabled();
return result;
}
@@ -3185,7 +3217,10 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
if (self.debugName.length > 0) {
[result addObject:@{ @"debugName" : ASStringWithQuotesIfMultiword(self.debugName)}];
}
if (self.accessibilityIdentifier.length > 0) {
[result addObject:@{ @"axId": ASStringWithQuotesIfMultiword(self.accessibilityIdentifier) }];
}
CGRect windowFrame = [self _frameInWindow];
if (CGRectIsNull(windowFrame) == NO) {
[result addObject:@{ @"frameInWindow" : [NSValue valueWithCGRect:windowFrame] }];
+26
View File
@@ -27,8 +27,10 @@
* for the nodes will be set to `MYButtonNode.titleNode` and `MYButtonNode.countNode`.
*/
#if DEBUG
#define ASSetDebugName(node, format, ...) node.debugName = [NSString stringWithFormat:format, __VA_ARGS__]
#define ASSetDebugNames(...) _ASSetDebugNames(self.class, @"" # __VA_ARGS__, __VA_ARGS__, nil)
#else
#define ASSetDebugName(node, name)
#define ASSetDebugNames(...)
#endif
@@ -78,6 +80,30 @@ __unused static NSString * _Nonnull NSStringFromASInterfaceState(ASInterfaceStat
return [NSString stringWithFormat:@"{ %@ }", [states componentsJoinedByString:@" | "]];
}
#define INTERFACE_STATE_DELTA(Name) ({ \
if ((oldState & ASInterfaceState##Name) != (newState & ASInterfaceState##Name)) { \
[changes appendFormat:@"%c%s ", (newState & ASInterfaceState##Name ? '+' : '-'), #Name]; \
} \
})
/// e.g. { +Visible, -Preload } (although that should never actually happen.)
/// NOTE: Changes to MeasureLayout state don't really mean anything so we omit them for now.
__unused static NSString * _Nonnull NSStringFromASInterfaceStateChange(ASInterfaceState oldState, ASInterfaceState newState)
{
if (oldState == newState) {
return @"{ }";
}
NSMutableString *changes = [NSMutableString stringWithString:@"{ "];
INTERFACE_STATE_DELTA(Preload);
INTERFACE_STATE_DELTA(Display);
INTERFACE_STATE_DELTA(Visible);
[changes appendString:@"}"];
return changes;
}
#undef INTERFACE_STATE_DELTA
NS_ASSUME_NONNULL_BEGIN
ASDISPLAYNODE_EXTERN_C_BEGIN
+16 -10
View File
@@ -24,6 +24,7 @@
#import <AsyncDisplayKit/ASPhotosFrameworkImageRequest.h>
#import <AsyncDisplayKit/ASEqualityHelpers.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASLog.h>
#if AS_PIN_REMOTE_IMAGE
#import <AsyncDisplayKit/ASPINRemoteImageDownloader.h>
@@ -450,7 +451,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
// Grab the best possible image we can load right now.
id bestImmediatelyAvailableImageIdentifier = nil;
UIImage *bestImmediatelyAvailableImage = [self _bestImmediatelyAvailableImageFromDataSource:&bestImmediatelyAvailableImageIdentifier];
ASMultiplexImageNodeLogDebug(@"[%p] Best immediately available image identifier is %@", self, bestImmediatelyAvailableImageIdentifier);
as_log_verbose(ASImageLoadingLog(), "%@ Best immediately available image identifier is %@", self, bestImmediatelyAvailableImageIdentifier);
// Load it. This kicks off cache fetching/downloading, as appropriate.
[self _finishedLoadingImage:bestImmediatelyAvailableImage forIdentifier:bestImmediatelyAvailableImageIdentifier error:nil];
@@ -580,6 +581,8 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
return;
}
as_activity_scope(as_activity_create("Load next image for multiplex image node", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
as_log_verbose(ASImageLoadingLog(), "Loading image for %@ ident: %@", self, nextImageIdentifier);
self.loadingImageIdentifier = nextImageIdentifier;
__weak __typeof__(self) weakSelf = self;
@@ -595,13 +598,11 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
[strongSelf _finishedLoadingImage:image forIdentifier:imageIdentifier error:error];
};
ASMultiplexImageNodeLogDebug(@"[%p] Loading next image, ident: %@", self, nextImageIdentifier);
// Ask our data-source if it's got this image.
if (_dataSourceFlags.image) {
UIImage *image = [_dataSource multiplexImageNode:self imageForImageIdentifier:nextImageIdentifier];
if (image) {
ASMultiplexImageNodeLogDebug(@"[%p] Acquired next image (%@) from data-source", self, nextImageIdentifier);
as_log_verbose(ASImageLoadingLog(), "Acquired image from data source for %@ ident: %@", self, nextImageIdentifier);
finishedLoadingBlock(image, nextImageIdentifier, nil);
return;
}
@@ -610,7 +611,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
NSURL *nextImageURL = (_dataSourceFlags.URL) ? [_dataSource multiplexImageNode:self URLForImageIdentifier:nextImageIdentifier] : nil;
// If we fail to get a URL for the image, we have no source and can't proceed.
if (!nextImageURL) {
ASMultiplexImageNodeLogError(@"[%p] Could not acquire URL for next image (%@). Bailing.", self, nextImageIdentifier);
as_log_error(ASImageLoadingLog(), "Could not acquire URL %@ ident: (%@)", self, nextImageIdentifier);
finishedLoadingBlock(nil, nil, [NSError errorWithDomain:ASMultiplexImageNodeErrorDomain code:ASMultiplexImageNodeErrorCodeNoSourceForImage userInfo:nil]);
return;
}
@@ -620,14 +621,14 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
if ([[nextImageURL scheme] isEqualToString:kAssetsLibraryURLScheme]) {
// Load the asset.
[self _loadALAssetWithIdentifier:nextImageIdentifier URL:nextImageURL completion:^(UIImage *downloadedImage, NSError *error) {
ASMultiplexImageNodeCLogDebug(@"[%p] Acquired next image (%@) from asset library", weakSelf, nextImageIdentifier);
as_log_verbose(ASImageLoadingLog(), "Acquired image from assets library for %@ %@", weakSelf, nextImageIdentifier);
finishedLoadingBlock(downloadedImage, nextImageIdentifier, error);
}];
}
// Likewise, if it's a iOS 8 Photo asset, we need to fetch it accordingly.
else if (ASPhotosFrameworkImageRequest *request = [ASPhotosFrameworkImageRequest requestWithURL:nextImageURL]) {
[self _loadPHAssetWithRequest:request identifier:nextImageIdentifier completion:^(UIImage *image, NSError *error) {
ASMultiplexImageNodeCLogDebug(@"[%p] Acquired next image (%@) from Photos Framework", weakSelf, nextImageIdentifier);
as_log_verbose(ASImageLoadingLog(), "Acquired image from Photos for %@ %@", weakSelf, nextImageIdentifier);
finishedLoadingBlock(image, nextImageIdentifier, error);
}];
}
@@ -642,7 +643,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
// If we had a cache-hit, we're done.
if (imageFromCache) {
ASMultiplexImageNodeCLogDebug(@"[%p] Acquired next image (%@) from cache", strongSelf, nextImageIdentifier);
as_log_verbose(ASImageLoadingLog(), "Acquired image from cache for %@ id: %@ img: %@", strongSelf, nextImageIdentifier, imageFromCache);
finishedLoadingBlock(imageFromCache, nextImageIdentifier, nil);
return;
}
@@ -655,7 +656,12 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
// Otherwise, we've got to download it.
[strongSelf _downloadImageWithIdentifier:nextImageIdentifier URL:nextImageURL completion:^(UIImage *downloadedImage, NSError *error) {
ASMultiplexImageNodeCLogDebug(@"[%p] Acquired next image (%@) from download", strongSelf, nextImageIdentifier);
__typeof__(self) strongSelf = weakSelf;
if (downloadedImage) {
as_log_verbose(ASImageLoadingLog(), "Acquired image from download for %@ id: %@ img: %@", strongSelf, nextImageIdentifier, downloadedImage);
} else {
as_log_error(ASImageLoadingLog(), "Error downloading image for %@ id: %@ err: %@", strongSelf, nextImageIdentifier, error);
}
finishedLoadingBlock(downloadedImage, nextImageIdentifier, error);
}];
}];
@@ -851,7 +857,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
// We explicitly perform this check because our datasource often doesn't give back immediately available images, even though we might have downloaded one already.
// Because we seed this call with bestImmediatelyAvailableImageFromDataSource, we must be careful not to trample an existing image.
if (image || imageIdentifierCount == 0) {
ASMultiplexImageNodeLogDebug(@"[%p] loaded -> displaying (%@, %@)", self, imageIdentifier, image);
as_log_verbose(ASImageLoadingLog(), "[%p] loaded -> displaying (%@, %@)", self, imageIdentifier, image);
id previousIdentifier = self.loadedImageIdentifier;
UIImage *previousImage = self.image;
+16
View File
@@ -16,6 +16,8 @@
//
#import <AsyncDisplayKit/ASNavigationController.h>
#import <AsyncDisplayKit/ASLog.h>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
@implementation ASNavigationController
{
@@ -65,39 +67,53 @@ ASVisibilityDepthImplementation;
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
{
as_activity_scope(as_activity_create("Pop multiple from ASNavigationController", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
NSArray *viewControllers = [super popToViewController:viewController animated:animated];
as_log_info(ASNodeLog(), "Popped %@ to %@, removing %@", self, viewController, ASGetDescriptionValueString(viewControllers));
[self visibilityDepthDidChange];
return viewControllers;
}
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated
{
as_activity_scope(as_activity_create("Pop to root of ASNavigationController", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
NSArray *viewControllers = [super popToRootViewControllerAnimated:animated];
as_log_info(ASNodeLog(), "Popped view controllers %@ from %@", ASGetDescriptionValueString(viewControllers), self);
[self visibilityDepthDidChange];
return viewControllers;
}
- (void)setViewControllers:(NSArray *)viewControllers
{
// NOTE: As of now this method calls through to setViewControllers:animated: so no need to log/activity here.
[super setViewControllers:viewControllers];
[self visibilityDepthDidChange];
}
- (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated
{
as_activity_scope(as_activity_create("Set view controllers of ASNavigationController", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
as_log_info(ASNodeLog(), "Set view controllers of %@ to %@ animated: %d", self, ASGetDescriptionValueString(viewControllers), animated);
[super setViewControllers:viewControllers animated:animated];
[self visibilityDepthDidChange];
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
as_activity_scope(as_activity_create("Push view controller on ASNavigationController", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
as_log_info(ASNodeLog(), "Pushing %@ onto %@", viewController, self);
[super pushViewController:viewController animated:animated];
[self visibilityDepthDidChange];
}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
as_activity_scope(as_activity_create("Pop view controller from ASNavigationController", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
UIViewController *viewController = [super popViewControllerAnimated:animated];
as_log_info(ASNodeLog(), "Popped %@ from %@", viewController, self);
[self visibilityDepthDidChange];
return viewController;
}
+14 -2
View File
@@ -26,6 +26,7 @@
#import <AsyncDisplayKit/ASImageNode+Private.h>
#import <AsyncDisplayKit/ASImageNode+AnimatedImagePrivate.h>
#import <AsyncDisplayKit/ASImageContainerProtocolCategories.h>
#import <AsyncDisplayKit/ASLog.h>
#if AS_PIN_REMOTE_IMAGE
#import <AsyncDisplayKit/ASPINRemoteImageDownloader.h>
@@ -417,6 +418,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
return;
}
as_log_verbose(ASImageLoadingLog(), "Received progress image for %@ q: %.2g id: %@", self, progress, progressImage);
[self _locked_setCurrentImageQuality:progress];
[self _locked__setImage:progressImage];
}
@@ -443,12 +445,14 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
// Unbind from the previous download.
if (oldDownloadIDForProgressBlock != nil) {
as_log_verbose(ASImageLoadingLog(), "Disabled progress images for %@ id: %@", self, oldDownloadIDForProgressBlock);
[_downloader setProgressImageBlock:nil callbackQueue:dispatch_get_main_queue() withDownloadIdentifier:oldDownloadIDForProgressBlock];
}
// Bind to the current download.
if (newDownloadIDForProgressBlock != nil) {
__weak __typeof(self) weakSelf = self;
as_log_verbose(ASImageLoadingLog(), "Enabled progress images for %@ id: %@", self, newDownloadIDForProgressBlock);
[_downloader setProgressImageBlock:^(UIImage * _Nonnull progressImage, CGFloat progress, id _Nullable downloadIdentifier) {
[weakSelf handleProgressImage:progressImage progress:progress downloadIdentifier:downloadIdentifier];
} callbackQueue:dispatch_get_main_queue() withDownloadIdentifier:newDownloadIDForProgressBlock];
@@ -507,6 +511,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
if (_cacheFlags.cacheSupportsClearing) {
if (_URL != nil) {
as_log_verbose(ASImageLoadingLog(), "Clearing cached image for %@ url: %@", self, _URL);
[_cache clearFetchedImageFromCacheWithURL:_URL];
}
}
@@ -526,8 +531,10 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
if (_downloadIdentifier) {
if (storeResume && _downloaderFlags.downloaderImplementsCancelWithResume) {
as_log_verbose(ASImageLoadingLog(), "Canceling image download w resume for %@ id: %@", self, _downloadIdentifier);
[_downloader cancelImageDownloadWithResumePossibilityForIdentifier:_downloadIdentifier];
} else {
as_log_verbose(ASImageLoadingLog(), "Canceling image download no resume for %@ id: %@", self, _downloadIdentifier);
[_downloader cancelImageDownloadForIdentifier:_downloadIdentifier];
}
}
@@ -550,15 +557,17 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
ASDN::MutexLocker l(__instanceLock__);
url = _URL;
}
downloadIdentifier = [_downloader downloadImageWithURL:url
callbackQueue:dispatch_get_main_queue()
downloadProgress:NULL
completion:^(id <ASImageContainerProtocol> _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) {
as_log_verbose(ASImageLoadingLog(), "Downloaded image for %@ img: %@ url: %@", self, [imageContainer asdk_image], url);
if (finished != NULL) {
finished(imageContainer, error, downloadIdentifier);
}
}];
as_log_verbose(ASImageLoadingLog(), "Downloading image for %@ url: %@", self, url);
{
ASDN::MutexLocker l(__instanceLock__);
@@ -574,6 +583,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
if (cancelAndReattempt) {
if (downloadIdentifier != nil) {
as_log_verbose(ASImageLoadingLog(), "Canceling image download no resume for %@ id: %@", self, downloadIdentifier);
[_downloader cancelImageDownloadForIdentifier:downloadIdentifier];
}
[self _downloadImageWithCompletion:finished];
@@ -654,7 +664,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
});
} else {
__weak __typeof__(self) weakSelf = self;
void (^finished)(id <ASImageContainerProtocol>, NSError *, id downloadIdentifier) = ^(id <ASImageContainerProtocol>imageContainer, NSError *error, id downloadIdentifier) {
auto finished = ^(id <ASImageContainerProtocol>imageContainer, NSError *error, id downloadIdentifier) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf == nil) {
@@ -702,6 +712,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
_cacheUUID = cacheUUID;
__instanceLock__.unlock();
as_log_verbose(ASImageLoadingLog(), "Decaching image for %@ url: %@", self, URL);
[_cache cachedImageWithURL:URL
callbackQueue:dispatch_get_main_queue()
completion:^(id <ASImageContainerProtocol> imageContainer) {
@@ -717,6 +728,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
if ([imageContainer asdk_image] == nil && _downloader != nil) {
[self _downloadImageWithCompletion:finished];
} else {
as_log_verbose(ASImageLoadingLog(), "Decached image for %@ img: %@ url: %@", self, [imageContainer asdk_image], URL);
finished(imageContainer, nil, nil);
}
}];
+25 -10
View File
@@ -16,9 +16,11 @@
//
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASLog.h>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
#import <AsyncDisplayKit/ASRunLoopQueue.h>
#import <AsyncDisplayKit/ASThread.h>
#import <AsyncDisplayKit/ASLog.h>
#import <AsyncDisplayKit/ASSignpost.h>
#import <QuartzCore/QuartzCore.h>
#import <cstdlib>
#import <deque>
@@ -190,7 +192,10 @@ static void runLoopSourceCallback(void *info) {
CFRunLoopObserverRef _runLoopObserver;
NSPointerArray *_internalQueue; // Use NSPointerArray so we can decide __strong or __weak per-instance.
ASDN::RecursiveMutex _internalQueueLock;
// In order to not pollute the top-level activities, each queue has 1 root activity.
os_activity_t _rootActivity;
#if ASRunLoopQueueLoggingEnabled
NSTimer *_runloopQueueLoggingTimer;
#endif
@@ -272,6 +277,15 @@ typedef enum {
_queueConsumer = handlerBlock;
_batchSize = 1;
_ensureExclusiveMembership = YES;
// We don't want to pollute the top-level app activities with run loop batches, so we create one top-level
// activity per queue, and each batch activity joins that one instead.
_rootActivity = as_activity_create("Process run loop queue items", OS_ACTIVITY_NONE, OS_ACTIVITY_FLAG_DEFAULT);
{
// Log a message identifying this queue into the queue's root activity.
as_activity_scope_verbose(_rootActivity);
as_log_verbose(ASRenderLog(), "Created run loop queue: %@", self);
}
// Self is guaranteed to outlive the observer. Without the high cost of a weak pointer,
// __unsafe_unretained allows us to avoid flagging the memory cycle detector.
@@ -375,16 +389,17 @@ typedef enum {
}
// itemsToProcess will be empty if _queueConsumer == nil so no need to check again.
if (itemsToProcess.empty() == false) {
#if ASRunLoopQueueLoggingEnabled
NSLog(@"<%@> - Starting processing of: %ld", self, itemsToProcess.size());
#endif
auto count = itemsToProcess.size();
if (count > 0) {
as_activity_scope_verbose(as_activity_create("Process run loop queue batch", _rootActivity, OS_ACTIVITY_FLAG_DEFAULT));
auto itemsEnd = itemsToProcess.cend();
for (auto iterator = itemsToProcess.begin(); iterator < itemsEnd; iterator++) {
_queueConsumer(*iterator, isQueueDrained && iterator == itemsEnd - 1);
#if ASRunLoopQueueLoggingEnabled
NSLog(@"<%@> - Finished processing 1 item", self);
#endif
__unsafe_unretained id value = *iterator;
_queueConsumer(value, isQueueDrained && iterator == itemsEnd - 1);
as_log_verbose(ASRenderLog(), "processed %@", value);
}
if (count > 1) {
as_log_verbose(ASRenderLog(), "processed %lu items", (unsigned long)count);
}
}
+7
View File
@@ -16,6 +16,7 @@
//
#import <AsyncDisplayKit/ASTabBarController.h>
#import <AsyncDisplayKit/ASLog.h>
@implementation ASTabBarController
{
@@ -72,12 +73,18 @@ ASVisibilityDepthImplementation;
- (void)setSelectedIndex:(NSUInteger)selectedIndex
{
as_activity_scope(as_activity_create("Set selected index of ASTabBarController", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
as_log_info(ASNodeLog(), "Selected tab %tu of %@", selectedIndex, self);
[super setSelectedIndex:selectedIndex];
[self visibilityDepthDidChange];
}
- (void)setSelectedViewController:(__kindof UIViewController *)selectedViewController
{
as_activity_scope(as_activity_create("Set selected view controller of ASTabBarController", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
as_log_info(ASNodeLog(), "Selected view controller %@ of %@", selectedViewController, self);
[super setSelectedViewController:selectedViewController];
[self visibilityDepthDidChange];
}
+1 -1
View File
@@ -250,7 +250,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
NSMutableArray *result = [super propertiesForDescription];
NSString *plainString = [self _plainStringForDescription];
if (plainString.length > 0) {
[result insertObject:@{ @"text" : ASStringWithQuotesIfMultiword(plainString) } atIndex:0];
[result addObject:@{ (id)kCFNull : ASStringWithQuotesIfMultiword(plainString) }];
}
return result;
}
+10 -4
View File
@@ -19,12 +19,11 @@
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASLog.h>
#import <AsyncDisplayKit/ASTraitCollection.h>
#import <AsyncDisplayKit/ASRangeControllerUpdateRangeProtocol+Beta.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
#define AS_LOG_VISIBILITY_CHANGES 0
@implementation ASViewController
{
BOOL _ensureDisplayed;
@@ -169,7 +168,11 @@ ASVisibilityDidMoveToParentViewController;
- (void)viewWillAppear:(BOOL)animated
{
as_activity_scope(as_activity_create("ASViewController will appear", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
as_log_debug(ASNodeLog(), "View controller %@ will appear", self);
[super viewWillAppear:animated];
_ensureDisplayed = YES;
// A layout pass is forced this early to get nodes like ASCollectionNode, ASTableNode etc.
@@ -191,7 +194,7 @@ ASVisibilityDepthImplementation;
- (void)visibilityDepthDidChange
{
ASLayoutRangeMode rangeMode = ASLayoutRangeModeForVisibilityDepth(self.visibilityDepth);
#if AS_LOG_VISIBILITY_CHANGES
#if ASEnableVerboseLogging
NSString *rangeModeString;
switch (rangeMode) {
case ASLayoutRangeModeMinimum:
@@ -213,7 +216,7 @@ ASVisibilityDepthImplementation;
default:
break;
}
NSLog(@"Updating visibility of:%@ to: %@ (visibility depth: %d)", self, rangeModeString, self.visibilityDepth);
as_log_verbose(ASNodeLog(), "Updating visibility of %@ to: %@ (visibility depth: %zd)", self, rangeModeString, self.visibilityDepth);
#endif
[self updateCurrentRangeModeWithModeIfPossible:rangeMode];
}
@@ -284,6 +287,8 @@ ASVisibilityDepthImplementation;
ASPrimitiveTraitCollection oldTraitCollection = self.node.primitiveTraitCollection;
if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(traitCollection, oldTraitCollection) == NO) {
as_activity_scope_verbose(as_activity_create("Propagate ASViewController trait collection", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
as_log_debug(ASNodeLog(), "Propagating new traits for %@: %@", self, NSStringFromASPrimitiveTraitCollection(traitCollection));
self.node.primitiveTraitCollection = traitCollection;
NSArray<id<ASLayoutElement>> *children = [self.node sublayoutElements];
@@ -295,6 +300,7 @@ ASVisibilityDepthImplementation;
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// Once we've propagated all the traits, layout this node.
// Remeasure the node with the latest constrained size – old constrained size may be incorrect.
as_activity_scope_verbose(as_activity_create("Layout ASViewController node with new traits", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
[_node layoutThatFits:[self nodeConstrainedSize]];
#pragma clang diagnostic pop
}
-4
View File
@@ -15,10 +15,6 @@
// http://www.apache.org/licenses/LICENSE-2.0
//
#pragma once
#import <AsyncDisplayKit/ASLog.h>
// The C++ compiler mangles C function names. extern "C" { /* your C functions */ } prevents this.
// You should wrap all C function prototypes declared in headers with ASDISPLAYNODE_EXTERN_C_BEGIN/END, even if
// they are included only from .m (Objective-C) files. It's common for .m files to start using C++
+87 -79
View File
@@ -16,93 +16,101 @@
//
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <Foundation/Foundation.h>
#import <os/log.h>
#import <os/activity.h>
/// The signposts we use. Signposts are grouped by color. The SystemTrace.tracetemplate file
/// should be kept up-to-date with these values.
typedef NS_ENUM(uint32_t, ASSignpostName) {
// Collection/Table (Blue)
ASSignpostDataControllerBatch = 300, // Alloc/layout nodes before collection update.
ASSignpostRangeControllerUpdate, // Ranges update pass.
ASSignpostCollectionUpdate, // Entire update process, from -endUpdates to [super perform…]
// Rendering (Green)
ASSignpostLayerDisplay = 325, // Client display callout.
ASSignpostRunLoopQueueBatch, // One batch of ASRunLoopQueue.
// Layout (Purple)
ASSignpostCalculateLayout = 350, // Start of calculateLayoutThatFits to end. Max 1 per thread.
// Misc (Orange)
ASSignpostDeallocQueueDrain = 375, // One chunk of dealloc queue work. arg0 is count.
ASSignpostCATransactionLayout, // The CA transaction commit layout phase.
ASSignpostCATransactionCommit // The CA transaction commit post-layout phase.
};
typedef NS_ENUM(uintptr_t, ASSignpostColor) {
ASSignpostColorBlue,
ASSignpostColorGreen,
ASSignpostColorPurple,
ASSignpostColorOrange,
ASSignpostColorRed,
ASSignpostColorDefault
};
static inline ASSignpostColor ASSignpostGetColor(ASSignpostName name, ASSignpostColor colorPref) {
if (colorPref == ASSignpostColorDefault) {
return (ASSignpostColor)((name / 25) % 4);
} else {
return colorPref;
}
}
#define ASMultiplexImageNodeLogDebug(...)
#define ASMultiplexImageNodeCLogDebug(...)
#define ASMultiplexImageNodeLogError(...)
#define ASMultiplexImageNodeCLogError(...)
#define AS_KDEBUG_ENABLE defined(PROFILE) && __has_include(<sys/kdebug_signpost.h>)
#if AS_KDEBUG_ENABLE
#import <sys/kdebug_signpost.h>
// These definitions are required to build the backward-compatible kdebug trace
// on the iOS 10 SDK. The kdebug_trace function crashes if run on iOS 9 and earlier.
// It's valuable to support trace signposts on iOS 9, because A5 devices don't support iOS 10.
#ifndef DBG_MACH_CHUD
#define DBG_MACH_CHUD 0x0A
#define DBG_FUNC_NONE 0
#define DBG_FUNC_START 1
#define DBG_FUNC_END 2
#define DBG_APPS 33
#define SYS_kdebug_trace 180
#define KDBG_CODE(Class, SubClass, code) (((Class & 0xff) << 24) | ((SubClass & 0xff) << 16) | ((code & 0x3fff) << 2))
#define APPSDBG_CODE(SubClass,code) KDBG_CODE(DBG_APPS, SubClass, code)
#ifndef ASEnableLogs
#define ASEnableLogs 1
#endif
// Currently we'll reserve arg3.
#define ASSignpost(name, identifier, arg2, color) \
AS_AT_LEAST_IOS10 ? kdebug_signpost(name, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color)) \
: syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, name) | DBG_FUNC_NONE, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color));
#ifndef ASEnableVerboseLogging
#define ASEnableVerboseLogging 0
#endif
#define ASSignpostStartCustom(name, identifier, arg2) \
AS_AT_LEAST_IOS10 ? kdebug_signpost_start(name, (uintptr_t)identifier, (uintptr_t)arg2, 0, 0) \
: syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, name) | DBG_FUNC_START, (uintptr_t)identifier, (uintptr_t)arg2, 0, 0);
#define ASSignpostStart(name) ASSignpostStartCustom(name, self, 0)
ASDISPLAYNODE_EXTERN_C_BEGIN
#define ASSignpostEndCustom(name, identifier, arg2, color) \
AS_AT_LEAST_IOS10 ? kdebug_signpost_end(name, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color)) \
: syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, name) | DBG_FUNC_END, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color));
#define ASSignpostEnd(name) ASSignpostEndCustom(name, self, 0, ASSignpostColorDefault)
/// Log for general node events e.g. interfaceState, didLoad.
#define ASNodeLogEnabled 1
os_log_t ASNodeLog();
/// Log for layout-specific events e.g. calculateLayout.
#define ASLayoutLogEnabled 1
os_log_t ASLayoutLog();
/// Log for display-specific events e.g. display queue batches.
#define ASRenderLogEnabled 1
os_log_t ASRenderLog();
/// Log for collection events e.g. reloadData, performBatchUpdates.
#define ASCollectionLogEnabled 1
os_log_t ASCollectionLog();
/// Log for ASNetworkImageNode and ASMultiplexImageNode events.
#define ASImageLoadingLogEnabled 1
os_log_t ASImageLoadingLog();
/// Specialized log for our main thread deallocation trampoline.
#define ASMainThreadDeallocationLogEnabled 0
os_log_t ASMainThreadDeallocationLog();
ASDISPLAYNODE_EXTERN_C_END
/**
* The activity tracing system changed a lot between iOS 9 and 10.
* In iOS 10, the system was merged with logging and became much more powerful
* and adopted a new API.
*
* The legacy API is visible, but its functionality is extremely limited and the API is so different
* that we don't bother with it. For example, activities described by os_activity_start/end are not
* reflected in the log whereas activities described by the newer
* os_activity_scope are. So unfortunately we must use these iOS 10
* APIs to get meaningful logging data.
*/
#if OS_LOG_TARGET_HAS_10_12_FEATURES
#define OS_ACTIVITY_NULLABLE nullable
#define AS_ACTIVITY_CURRENT OS_ACTIVITY_CURRENT
#define as_activity_scope(activity) os_activity_scope(activity)
#define as_activity_apply(activity, block) os_activity_apply(activity, block)
#define as_activity_create(description, parent_activity, flags) os_activity_create(description, parent_activity, flags)
#define as_activity_scope_enter(activity, statePtr) os_activity_scope_enter(activity, statePtr)
#define as_activity_scope_leave(statePtr) os_activity_scope_leave(statePtr)
#define as_activity_get_identifier(activity, outParentID) os_activity_get_identifier(activity, outParentID)
#if ASEnableVerboseLogging
#define as_activity_scope_verbose(activity) as_activity_scope(activity)
#else
#define as_activity_scope_verbose(activity)
#endif
#else
#define ASSignpost(name, identifier, arg2, color)
#define ASSignpostStartCustom(name, identifier, arg2)
#define ASSignpostStart(name)
#define ASSignpostEndCustom(name, identifier, arg2, color)
#define ASSignpostEnd(name)
#define OS_ACTIVITY_NULLABLE
#define AS_ACTIVITY_CURRENT OS_ACTIVITY_NULL
#define as_activity_scope(activity)
#define as_activity_scope_verbose(activity)
#define as_activity_apply(activity, block)
#define as_activity_create(description, parent_activity, flags) OS_ACTIVITY_NULL
#define as_activity_scope_enter(activity, statePtr)
#define as_activity_scope_leave(statePtr)
#define as_activity_get_identifier(activity, outParentID) (os_activity_id_t)0
#endif // OS_LOG_TARGET_HAS_10_12_FEATURES
/**
* The logging macros are not guarded by deployment-target checks like the activity macros are, but they are
* only available on iOS >= 9 at runtime, so just make them conditional.
*/
#define as_log_create(subsystem, category) (AS_AT_LEAST_IOS9 ? os_log_create(subsystem, category) : (os_log_t)0)
#define as_log_debug(log, format, ...) (AS_AT_LEAST_IOS9 ? os_log_debug(log, format, ##__VA_ARGS__) : (void)0)
#define as_log_info(log, format, ...) (AS_AT_LEAST_IOS9 ? os_log_info(log, format, ##__VA_ARGS__) : (void)0)
#define as_log_error(log, format, ...) (AS_AT_LEAST_IOS9 ? os_log_error(log, format, ##__VA_ARGS__) : (void)0)
#define as_log_fault(log, format, ...) (AS_AT_LEAST_IOS9 ? os_log_fault(log, format, ##__VA_ARGS__) : (void)0)
#if ASEnableVerboseLogging
#define as_log_verbose(log, format, ...) as_log_debug(log, format, ##__VA_ARGS__)
#else
#define as_log_verbose(log, format, ...)
#endif
+79
View File
@@ -0,0 +1,79 @@
//
// ASLog.m
// Texture
//
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASLog.h>
os_log_t ASNodeLog() {
static os_log_t log;
#if ASEnableLogs && ASNodeLogEnabled
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
log = as_log_create("org.TextureGroup.Texture", "Node");
});
#endif
return log;
}
os_log_t ASLayoutLog() {
static os_log_t log;
#if ASEnableLogs && ASLayoutLogEnabled
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
log = as_log_create("org.TextureGroup.Texture", "Layout");
});
#endif
return log;
}
os_log_t ASCollectionLog() {
static os_log_t log;
#if ASEnableLogs && ASCollectionLogEnabled
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
log = as_log_create("org.TextureGroup.Texture", "Collection");
});
#endif
return log;
}
os_log_t ASRenderLog() {
static os_log_t log;
#if ASEnableLogs && ASRenderLogEnabled
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
log = as_log_create("org.TextureGroup.Texture", "Render");
});
#endif
return log;
}
os_log_t ASImageLoadingLog() {
static os_log_t log;
#if ASEnableLogs && ASImageLoadingLogEnabled
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
log = as_log_create("org.TextureGroup.Texture", "ImageLoading");
});
#endif
return log;
}
os_log_t ASMainThreadDeallocationLog() {
static os_log_t log;
#if ASEnableLogs && ASMainThreadDeallocationLogEnabled
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
log = as_log_create("org.TextureGroup.Texture", "MainDealloc");
});
#endif
return log;
}
+94
View File
@@ -0,0 +1,94 @@
//
// ASSignpost.h
// Texture
//
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
/// The signposts we use. Signposts are grouped by color. The SystemTrace.tracetemplate file
/// should be kept up-to-date with these values.
typedef NS_ENUM(uint32_t, ASSignpostName) {
// Collection/Table (Blue)
ASSignpostDataControllerBatch = 300, // Alloc/layout nodes before collection update.
ASSignpostRangeControllerUpdate, // Ranges update pass.
ASSignpostCollectionUpdate, // Entire update process, from -endUpdates to [super perform…]
// Rendering (Green)
ASSignpostLayerDisplay = 325, // Client display callout.
ASSignpostRunLoopQueueBatch, // One batch of ASRunLoopQueue.
// Layout (Purple)
ASSignpostCalculateLayout = 350, // Start of calculateLayoutThatFits to end. Max 1 per thread.
// Misc (Orange)
ASSignpostDeallocQueueDrain = 375, // One chunk of dealloc queue work. arg0 is count.
ASSignpostCATransactionLayout, // The CA transaction commit layout phase.
ASSignpostCATransactionCommit // The CA transaction commit post-layout phase.
};
typedef NS_ENUM(uintptr_t, ASSignpostColor) {
ASSignpostColorBlue,
ASSignpostColorGreen,
ASSignpostColorPurple,
ASSignpostColorOrange,
ASSignpostColorRed,
ASSignpostColorDefault
};
static inline ASSignpostColor ASSignpostGetColor(ASSignpostName name, ASSignpostColor colorPref) {
if (colorPref == ASSignpostColorDefault) {
return (ASSignpostColor)((name / 25) % 4);
} else {
return colorPref;
}
}
#define AS_KDEBUG_ENABLE defined(PROFILE) && __has_include(<sys/kdebug_signpost.h>)
#if AS_KDEBUG_ENABLE
#import <sys/kdebug_signpost.h>
// These definitions are required to build the backward-compatible kdebug trace
// on the iOS 10 SDK. The kdebug_trace function crashes if run on iOS 9 and earlier.
// It's valuable to support trace signposts on iOS 9, because A5 devices don't support iOS 10.
#ifndef DBG_MACH_CHUD
#define DBG_MACH_CHUD 0x0A
#define DBG_FUNC_NONE 0
#define DBG_FUNC_START 1
#define DBG_FUNC_END 2
#define DBG_APPS 33
#define SYS_kdebug_trace 180
#define KDBG_CODE(Class, SubClass, code) (((Class & 0xff) << 24) | ((SubClass & 0xff) << 16) | ((code & 0x3fff) << 2))
#define APPSDBG_CODE(SubClass,code) KDBG_CODE(DBG_APPS, SubClass, code)
#endif
// Currently we'll reserve arg3.
#define ASSignpost(name, identifier, arg2, color) \
AS_AT_LEAST_IOS10 ? kdebug_signpost(name, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color)) \
: syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, name) | DBG_FUNC_NONE, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color));
#define ASSignpostStartCustom(name, identifier, arg2) \
AS_AT_LEAST_IOS10 ? kdebug_signpost_start(name, (uintptr_t)identifier, (uintptr_t)arg2, 0, 0) \
: syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, name) | DBG_FUNC_START, (uintptr_t)identifier, (uintptr_t)arg2, 0, 0);
#define ASSignpostStart(name) ASSignpostStartCustom(name, self, 0)
#define ASSignpostEndCustom(name, identifier, arg2, color) \
AS_AT_LEAST_IOS10 ? kdebug_signpost_end(name, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color)) \
: syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, name) | DBG_FUNC_END, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color));
#define ASSignpostEnd(name) ASSignpostEndCustom(name, self, 0, ASSignpostColorDefault)
#else
#define ASSignpost(name, identifier, arg2, color)
#define ASSignpostStartCustom(name, identifier, arg2)
#define ASSignpostStart(name)
#define ASSignpostEndCustom(name, identifier, arg2, color)
#define ASSignpostEnd(name)
#endif
+2
View File
@@ -17,6 +17,7 @@
#import <AsyncDisplayKit/ASBatchContext.h>
#import <AsyncDisplayKit/ASLog.h>
#import <AsyncDisplayKit/ASThread.h>
typedef NS_ENUM(NSInteger, ASBatchContextState) {
@@ -63,6 +64,7 @@ typedef NS_ENUM(NSInteger, ASBatchContextState) {
- (void)completeBatchFetching:(BOOL)didComplete
{
if (didComplete) {
as_log_debug(ASCollectionLog(), "Completed batch fetch with context %@", self);
ASDN::MutexLocker l(__instanceLock__);
_state = ASBatchContextStateCompleted;
}
+45 -20
View File
@@ -23,8 +23,11 @@
#import <AsyncDisplayKit/ASCollectionElement.h>
#import <AsyncDisplayKit/ASCollectionLayoutContext.h>
#import <AsyncDisplayKit/ASDispatch.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import <AsyncDisplayKit/ASElementMap.h>
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASLog.h>
#import <AsyncDisplayKit/ASSignpost.h>
#import <AsyncDisplayKit/ASMainSerialQueue.h>
#import <AsyncDisplayKit/ASMutableElementMap.h>
#import <AsyncDisplayKit/ASRangeManagingNode.h>
@@ -167,7 +170,11 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray<ASCollectionElement *> *
for (NSUInteger i = 0; i < count; i += batchSize) {
NSRange batchedRange = NSMakeRange(i, MIN(count - i, batchSize));
NSArray<ASCollectionElement *> *batchedElements = [elements subarrayWithRange:batchedRange];
NSArray<ASCellNode *> *nodes = [self _allocateNodesFromElements:batchedElements andLayout:shouldLayout];
NSArray<ASCellNode *> *nodes;
{
as_activity_scope(as_activity_create("Data controller batch", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
nodes = [self _allocateNodesFromElements:batchedElements andLayout:shouldLayout];
}
batchCompletionHandler(batchedElements, nodes);
}
@@ -491,7 +498,14 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray<ASCollectionElement *> *
ASDisplayNodeAssertMainThread();
if (changeSet.includesReloadData) {
_initialReloadDataHasBeenCalled = YES;
if (_initialReloadDataHasBeenCalled) {
as_log_debug(ASCollectionLog(), "reloadData %@", ASViewToDisplayNode(ASDynamicCast(self.dataSource, UIView)));
} else {
as_log_debug(ASCollectionLog(), "Initial reloadData %@", ASViewToDisplayNode(ASDynamicCast(self.dataSource, UIView)));
_initialReloadDataHasBeenCalled = YES;
}
} else {
as_log_debug(ASCollectionLog(), "performBatchUpdates %@ %@", ASViewToDisplayNode(ASDynamicCast(self.dataSource, UIView)), changeSet);
}
dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER);
@@ -500,6 +514,7 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray<ASCollectionElement *> *
// See ASUICollectionViewTests.testThatIssuingAnUpdateBeforeInitialReloadIsUnacceptable
// for the issue that UICollectionView has that we're choosing to workaround.
if (!_initialReloadDataHasBeenCalled) {
as_log_debug(ASCollectionLog(), "%@ Skipped update because load hasn't happened.", ASObjectDescriptionMakeTiny(_dataSource));
[changeSet executeCompletionHandlerWithFinished:YES];
return;
}
@@ -540,27 +555,36 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray<ASCollectionElement *> *
}
// Mutable copy of current data.
ASElementMap *previousMap = _pendingMap;
ASMutableElementMap *mutableMap = [previousMap mutableCopy];
BOOL canDelegateLayout = (_layoutDelegate != nil);
BOOL canDelegateLayout;
ASElementMap *newMap;
id layoutContext;
{
as_activity_scope(as_activity_create("Latch new data for collection update", changeSet.rootActivity, OS_ACTIVITY_FLAG_DEFAULT));
ASElementMap *previousMap = _pendingMap;
ASMutableElementMap *mutableMap = [previousMap mutableCopy];
// Step 1: Update the mutable copies to match the data source's state
[self _updateSectionContextsInMap:mutableMap changeSet:changeSet];
ASPrimitiveTraitCollection existingTraitCollection = [self.node primitiveTraitCollection];
[self _updateElementsInMap:mutableMap changeSet:changeSet traitCollection:existingTraitCollection shouldFetchSizeRanges:(! canDelegateLayout) previousMap:previousMap];
// Step 2: Clone the new data
ASElementMap *newMap = [mutableMap copy];
self.pendingMap = newMap;
// Step 3: Ask layout delegate for contexts
id layoutContext = nil;
if (canDelegateLayout) {
layoutContext = [_layoutDelegate layoutContextWithElements:newMap];
canDelegateLayout = (_layoutDelegate != nil);
// Step 1: Update the mutable copies to match the data source's state
[self _updateSectionContextsInMap:mutableMap changeSet:changeSet];
ASPrimitiveTraitCollection existingTraitCollection = [self.node primitiveTraitCollection];
[self _updateElementsInMap:mutableMap changeSet:changeSet traitCollection:existingTraitCollection shouldFetchSizeRanges:(! canDelegateLayout) previousMap:previousMap];
// Step 2: Clone the new data
newMap = [mutableMap copy];
self.pendingMap = newMap;
// Step 3: Ask layout delegate for contexts
if (canDelegateLayout) {
layoutContext = [_layoutDelegate layoutContextWithElements:newMap];
}
}
as_log_debug(ASCollectionLog(), "New content: %@", newMap.smallDescription);
dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{
__block __unused os_activity_scope_state_s preparationScope = {}; // unused if deployment target < iOS10
as_activity_scope_enter(as_activity_create("Prepare nodes for collection update", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT), &preparationScope);
// Step 4: Allocate and layout elements if can't delegate
NSArray<ASCollectionElement *> *elementsToProcess;
if (canDelegateLayout) {
@@ -581,8 +605,9 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray<ASCollectionElement *> *
if (canDelegateLayout) {
[_layoutDelegate prepareLayoutWithContext:layoutContext];
}
[_mainSerialQueue performBlockOnMainThread:^{
as_activity_scope_leave(&preparationScope);
[_delegate dataController:self willUpdateWithChangeSet:changeSet];
// Step 5: Deploy the new data as "completed" and inform delegate
+5
View File
@@ -96,6 +96,11 @@ AS_SUBCLASSING_RESTRICTED
*/
- (nullable ASCollectionElement *)elementForLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes;
/**
* A very terse description e.g. { itemCounts = [ <S0: 1> <S1: 16> ] }
*/
@property (atomic, readonly) NSString *smallDescription;
#pragma mark - Initialization -- Only Useful to ASDataController
+12
View File
@@ -183,6 +183,18 @@
return [_elementToIndexPathMap countByEnumeratingWithState:state objects:buffer count:len];
}
- (NSString *)smallDescription
{
NSMutableArray *sectionDescriptions = [NSMutableArray array];
NSUInteger i = 0;
for (NSArray *section in _sectionsOfItems) {
[sectionDescriptions addObject:[NSString stringWithFormat:@"<S%tu: %tu>", i, section.count]];
i++;
}
return ASObjectDescriptionMakeWithoutObject(@[ @{ @"itemCounts": sectionDescriptions }]);
}
#pragma mark - ASDescriptionProvider
- (NSString *)description
+11 -2
View File
@@ -47,6 +47,9 @@ NSString *ASGetDescriptionValueString(id object)
[strings addObject:[NSString stringWithFormat:@"%lu", (unsigned long)[indexPath indexAtPosition:i]]];
}
return [NSString stringWithFormat:@"(%@)", [strings componentsJoinedByString:@", "]];
} else if ([object respondsToSelector:@selector(componentsJoinedByString:)]) {
// e.g. "[ <MYObject: 0x00000000> <MYObject: 0xFFFFFFFF> ]"
return [NSString stringWithFormat:@"[ %@ ]", [object componentsJoinedByString:@" "]];
}
return [object description];
}
@@ -56,7 +59,13 @@ NSString *_ASObjectDescriptionMakePropertyList(NSArray<NSDictionary *> * _Nullab
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)]];
NSString *str;
if (key == (id)kCFNull) {
str = ASGetDescriptionValueString(obj);
} else {
str = [NSString stringWithFormat:@"%@ = %@", key, ASGetDescriptionValueString(obj)];
}
[components addObject:str];
}];
}
return [components componentsJoinedByString:@"; "];
@@ -73,7 +82,7 @@ NSString *ASObjectDescriptionMake(__autoreleasing id object, NSArray<NSDictionar
return @"(null)";
}
NSMutableString *str = [NSMutableString stringWithFormat:@"<%@: %p", [object class], object];
NSMutableString *str = [NSMutableString stringWithFormat:@"<%s: %p", object_getClassName(object), object];
NSString *propList = _ASObjectDescriptionMakePropertyList(propertyGroups);
if (propList.length > 0) {
+3
View File
@@ -25,6 +25,7 @@
#import <AsyncDisplayKit/ASDisplayNodeInternal.h> // Required for interfaceState and hierarchyState setter methods.
#import <AsyncDisplayKit/ASElementMap.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASSignpost.h>
#import <AsyncDisplayKit/ASTwoDimensionalArrayUtils.h>
#import <AsyncDisplayKit/ASWeakSet.h>
@@ -203,6 +204,8 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
- (void)_updateVisibleNodeIndexPaths
{
as_activity_scope_verbose(as_activity_create("Update range controller", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
as_log_verbose(ASCollectionLog(), "Updating ranges for %@", ASViewToDisplayNode(ASDynamicCast(self.delegate, UIView)));
ASDisplayNodeAssert(_layoutController, @"An ASLayoutController is required by ASRangeController");
if (!_layoutController || !_dataSource) {
return;
+1 -1
View File
@@ -198,7 +198,7 @@
NSMutableString *description = [[super description] mutableCopy];
ASDisplayNode *node = self.asyncdisplaykit_node;
if (node != nil) {
NSString *classString = [NSString stringWithFormat:@"%@-", [node class]];
NSString *classString = [NSString stringWithFormat:@"%s-", object_getClassName(node)];
[description replaceOccurrencesOfString:@"_ASDisplay" withString:classString options:kNilOptions range:NSMakeRange(0, description.length)];
NSUInteger insertionIndex = [description rangeOfString:@">"].location;
if (insertionIndex != NSNotFound) {
+1 -1
View File
@@ -58,7 +58,7 @@
ASDisplayNode *node = _asyncdisplaykit_node;
if (node != nil) {
NSString *classString = [NSString stringWithFormat:@"%@-", [node class]];
NSString *classString = [NSString stringWithFormat:@"%s-", object_getClassName(node)];
[description replaceOccurrencesOfString:@"_ASDisplay" withString:classString options:kNilOptions range:NSMakeRange(0, description.length)];
NSUInteger semicolon = [description rangeOfString:@";"].location;
if (semicolon != NSNotFound) {
+11 -3
View File
@@ -107,9 +107,17 @@ ASSizeRange ASSizeRangeIntersect(ASSizeRange sizeRange, ASSizeRange otherSizeRan
NSString *NSStringFromASSizeRange(ASSizeRange sizeRange)
{
return [NSString stringWithFormat:@"<ASSizeRange: min=%@, max=%@>",
NSStringFromCGSize(sizeRange.min),
NSStringFromCGSize(sizeRange.max)];
// 17 field length copied from iOS 10.3 impl of NSStringFromCGSize.
if (CGSizeEqualToSize(sizeRange.min, sizeRange.max)) {
return [NSString stringWithFormat:@"{{%.*g, %.*g}}",
17, sizeRange.min.width,
17, sizeRange.min.height];
}
return [NSString stringWithFormat:@"{{%.*g, %.*g}, {%.*g, %.*g}}",
17, sizeRange.min.width,
17, sizeRange.min.height,
17, sizeRange.max.width,
17, sizeRange.max.height];
}
#if YOGA
+2 -2
View File
@@ -66,9 +66,9 @@ ASDISPLAYNODE_EXTERN_C_END
@property (nonatomic, assign, readonly) CGSize size;
/**
* Position in parent. Default to CGPointNull.
* Position in parent. Default to ASPointNull.
*
* @discussion When being used as a sublayout, this property must not equal CGPointNull.
* @discussion When being used as a sublayout, this property must not equal ASPointNull.
*/
@property (nonatomic, assign, readonly) CGPoint position;
+9 -2
View File
@@ -287,9 +287,16 @@ static std::atomic_bool static_retainsSublayoutLayoutElements = ATOMIC_VAR_INIT(
- (NSMutableArray <NSDictionary *> *)propertiesForDescription
{
NSMutableArray *result = [NSMutableArray array];
[result addObject:@{ @"layoutElement" : (self.layoutElement ?: (id)kCFNull) }];
[result addObject:@{ @"position" : [NSValue valueWithCGPoint:self.position] }];
[result addObject:@{ @"size" : [NSValue valueWithCGSize:self.size] }];
if (auto layoutElement = self.layoutElement) {
[result addObject:@{ @"layoutElement" : layoutElement }];
}
auto pos = self.position;
if (!ASPointIsNull(pos)) {
[result addObject:@{ @"position" : [NSValue valueWithCGPoint:pos] }];
}
return result;
}
+2 -1
View File
@@ -21,6 +21,7 @@
#import <AsyncDisplayKit/ASStackLayoutElement.h>
#import <AsyncDisplayKit/ASAbsoluteLayoutElement.h>
#import <AsyncDisplayKit/ASTraitCollection.h>
#import <AsyncDisplayKit/ASAsciiArtBoxCreator.h>
@class ASLayout;
@class ASLayoutSpec;
@@ -60,7 +61,7 @@ typedef NS_ENUM(NSUInteger, ASLayoutElementType) {
* access to the options via convenience properties. If you are creating custom layout spec, then you can
* extend the backing layout options class to accommodate any new layout options.
*/
@protocol ASLayoutElement <ASLayoutElementExtensibility, ASTraitEnvironment>
@protocol ASLayoutElement <ASLayoutElementExtensibility, ASTraitEnvironment, ASLayoutElementAsciiArtProtocol>
#pragma mark - Getter
+2 -2
View File
@@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
* A layout spec is an immutable object that describes a layout, loosely inspired by React.
*/
@interface ASLayoutSpec : NSObject <ASLayoutElement, ASLayoutElementStylability, NSFastEnumeration>
@interface ASLayoutSpec : NSObject <ASLayoutElement, ASLayoutElementStylability, NSFastEnumeration, ASDescriptionProvider>
/**
* Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a
@@ -95,7 +95,7 @@ NS_ASSUME_NONNULL_BEGIN
@end
@interface ASLayoutSpec (Debugging) <ASLayoutElementAsciiArtProtocol, ASDebugNameProvider>
@interface ASLayoutSpec (Debugging) <ASDebugNameProvider>
/**
* Used by other layout specs to create ascii art debug strings
*/
+36 -16
View File
@@ -173,6 +173,24 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
ASLayoutElementStyleExtensibilityForwarding
#pragma mark - ASDescriptionProvider
- (NSMutableArray<NSDictionary *> *)propertiesForDescription
{
auto result = [NSMutableArray<NSDictionary *> array];
if (NSArray *children = self.children) {
// Use tiny descriptions because these trees can get nested very deep.
auto tinyDescriptions = ASArrayByFlatMapping(children, id object, ASObjectDescriptionMakeTiny(object));
[result addObject:@{ @"children": tinyDescriptions }];
}
return result;
}
- (NSString *)description
{
return ASObjectDescriptionMake(self, [self propertiesForDescription]);
}
#pragma mark - Framework Private
#if AS_DEDUPE_LAYOUT_SPEC_TREE
@@ -236,6 +254,23 @@ ASLayoutElementStyleExtensibilityForwarding
}
}
#pragma mark - ASLayoutElementAsciiArtProtocol
- (NSString *)asciiArtString
{
NSArray *children = self.children.count < 2 && self.child ? @[self.child] : self.children;
return [ASLayoutSpec asciiArtStringForChildren:children parentName:[self asciiArtName]];
}
- (NSString *)asciiArtName
{
NSMutableString *result = [NSMutableString stringWithCString:object_getClassName(self) encoding:NSASCIIStringEncoding];
if (_debugName) {
[result appendFormat:@" (%@)", _debugName];
}
return result;
}
@end
#pragma mark - ASWrapperLayoutSpec
@@ -295,7 +330,7 @@ ASLayoutElementStyleExtensibilityForwarding
@implementation ASLayoutSpec (Debugging)
#pragma mark - ASLayoutElementAsciiArtProtocol
#pragma mark - ASCII Art Helpers
+ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction
{
@@ -317,21 +352,6 @@ ASLayoutElementStyleExtensibilityForwarding
return [self asciiArtStringForChildren:children parentName:parentName direction:ASStackLayoutDirectionHorizontal];
}
- (NSString *)asciiArtString
{
NSArray *children = self.children.count < 2 && self.child ? @[self.child] : self.children;
return [ASLayoutSpec asciiArtStringForChildren:children parentName:[self asciiArtName]];
}
- (NSString *)asciiArtName
{
NSString *string = NSStringFromClass([self class]);
if (_debugName) {
string = [string stringByAppendingString:[NSString stringWithFormat:@" (debugName = %@)",_debugName]];
}
return string;
}
@end
#pragma mark - ASLayoutSpec (Deprecated)
+20
View File
@@ -25,6 +25,7 @@
#import <AsyncDisplayKit/ASLayoutElement.h>
#import <AsyncDisplayKit/ASLayoutElementStylePrivate.h>
#import <AsyncDisplayKit/ASLayoutSpecUtilities.h>
#import <AsyncDisplayKit/ASLog.h>
#import <AsyncDisplayKit/ASStackPositionedLayout.h>
#import <AsyncDisplayKit/ASStackUnpositionedLayout.h>
@@ -134,6 +135,8 @@
return [ASLayout layoutWithLayoutElement:self size:constrainedSize.min];
}
as_activity_scope_verbose(as_activity_create("Calculate stack layout", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
as_log_verbose(ASLayoutLog(), "Stack layout %@", self);
// Accessing the style and size property is pretty costly we create layout spec children we use to figure
// out the layout for each child
const auto stackChildren = AS::map(children, [&](const id<ASLayoutElement> child) -> ASStackLayoutSpecChild {
@@ -177,6 +180,23 @@
}
}
- (NSMutableArray<NSDictionary *> *)propertiesForDescription
{
auto result = [super propertiesForDescription];
// Add our direction
switch (self.direction) {
case ASStackLayoutDirectionVertical:
[result insertObject:@{ (id)kCFNull: @"vertical" } atIndex:0];
break;
case ASStackLayoutDirectionHorizontal:
[result insertObject:@{ (id)kCFNull: @"horizontal" } atIndex:0];
break;
}
return result;
}
@end
@implementation ASStackLayoutSpec (Debugging)
+6
View File
@@ -22,6 +22,7 @@
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h> // Required for _insertSubnode... / _removeFromSupernode.
#import <AsyncDisplayKit/ASLog.h>
#import <queue>
#import <memory>
@@ -106,6 +107,8 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
ASDN::MutexSharedLocker l(__instanceLock__);
[self calculateSubnodeOperationsIfNeeded];
// Create an activity even if no subnodes affected.
as_activity_scope(as_activity_create("Apply subnode insertions", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
if (_insertedSubnodes.count == 0) {
return;
}
@@ -121,6 +124,7 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
- (void)applySubnodeRemovals
{
as_activity_scope(as_activity_create("Apply subnode removals", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
ASDN::MutexSharedLocker l(__instanceLock__);
[self calculateSubnodeOperationsIfNeeded];
@@ -144,6 +148,8 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
return;
}
// Create an activity even if no subnodes affected.
as_activity_scope(as_activity_create("Calculate subnode operations", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
ASLayout *previousLayout = _previousLayout->layout;
ASLayout *pendingLayout = _pendingLayout->layout;
+3 -3
View File
@@ -25,7 +25,7 @@ static NSUInteger ASRectSize(const void *ptr)
@implementation NSMapTable (ASRectTableMethods)
+ (instancetype)rectTableWithKeyPointerFunctions:(NSPointerFunctions *)keyFuncs
+ (NSMapTable *)rectTableWithKeyPointerFunctions:(NSPointerFunctions *)keyFuncs
{
static NSPointerFunctions *cgRectFuncs;
static dispatch_once_t onceToken;
@@ -37,7 +37,7 @@ static NSUInteger ASRectSize(const void *ptr)
return [[NSMapTable alloc] initWithKeyPointerFunctions:keyFuncs valuePointerFunctions:cgRectFuncs capacity:0];
}
+ (instancetype)rectTableForStrongObjectPointers
+ (NSMapTable *)rectTableForStrongObjectPointers
{
static NSPointerFunctions *strongObjectPointerFuncs;
static dispatch_once_t onceToken;
@@ -47,7 +47,7 @@ static NSUInteger ASRectSize(const void *ptr)
return [self rectTableWithKeyPointerFunctions:strongObjectPointerFuncs];
}
+ (instancetype)rectTableForWeakObjectPointers
+ (NSMapTable *)rectTableForWeakObjectPointers
{
static NSPointerFunctions *weakObjectPointerFuncs;
static dispatch_once_t onceToken;
+7
View File
@@ -18,6 +18,7 @@
#import <Foundation/Foundation.h>
#import <vector>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
#import <AsyncDisplayKit/ASLog.h>
NS_ASSUME_NONNULL_BEGIN
@@ -119,6 +120,12 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType);
/// Indicates whether the change set is empty, that is it includes neither reload data nor per item or section changes.
@property (nonatomic, readonly) BOOL isEmpty;
/// The top-level activity for this update.
@property (nonatomic, OS_ACTIVITY_NULLABLE) os_activity_t rootActivity;
/// The activity for submitting this update i.e. between -beginUpdates and -endUpdates.
@property (nonatomic, OS_ACTIVITY_NULLABLE) os_activity_t submitActivity;
- (instancetype)initWithOldData:(std::vector<NSInteger>)oldItemCounts NS_DESIGNATED_INITIALIZER;
/**
+4 -2
View File
@@ -601,7 +601,7 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType)
- (NSString *)description
{
return ASObjectDescriptionMake(self, [self propertiesForDescription]);
return ASObjectDescriptionMakeWithoutObject([self propertiesForDescription]);
}
- (NSString *)debugDescription
@@ -612,7 +612,9 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType)
- (NSMutableArray<NSDictionary *> *)propertiesForDescription
{
NSMutableArray<NSDictionary *> *result = [NSMutableArray array];
[result addObject:@{ @"includesReloadData" : @(_includesReloadData) }];
if (_includesReloadData) {
[result addObject:@{ @"reloadData" : @"YES" }];
}
if (_reloadSectionChanges.count > 0) {
[result addObject:@{ @"reloadSections" : [_ASHierarchySectionChange smallDescriptionForSectionChanges:_reloadSectionChanges] }];
}
-2
View File
@@ -1880,10 +1880,8 @@ static bool stringContainsPointer(NSString *description, id p) {
node.debugName = @"big troll eater name";
XCTAssertTrue([node.description containsString:node.debugName], @"debugName didn't end up in description");
XCTAssertTrue([node.description containsString:@"debugName"], @"Node description should contain `debugName`.");
[node layer];
XCTAssertTrue([node.description containsString:node.debugName], @"debugName didn't end up in description");
XCTAssertTrue([node.description containsString:@"debugName"], @"Node description should contain `debugName`.");
}
- (void)testNameInDescriptionLayer
@@ -18,6 +18,7 @@
#import "ItemNode.h"
#import "ItemStyles.h"
#import "PlaceholderNetworkImageNode.h"
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
const CGFloat kFixedLabelsAreaHeight = 96.0;
const CGFloat kDesignWidth = 320.0;
@@ -45,6 +46,7 @@ const CGFloat kSoldOutGBHeight = 50.0;
@end
@implementation ItemNode
@dynamic viewModel;
- (instancetype)initWithViewModel:(ItemViewModel *)viewModel
{
@@ -55,6 +57,8 @@ const CGFloat kSoldOutGBHeight = 50.0;
[self updateLabels];
[self updateBackgroundColor];
ASSetDebugName(self, @"Item #%zd", viewModel.identifier);
self.accessibilityIdentifier = viewModel.titleText;
}
return self;
}
@@ -1,20 +1,18 @@
//
// ItemViewModel.h
// Sample
//
// Created by Samuel Stow on 12/29/15.
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import <Foundation/Foundation.h>
@@ -22,8 +20,9 @@
@interface ItemViewModel : NSObject
+ (instancetype)randomItem;
+ (ItemViewModel *)randomItem;
@property (nonatomic, readonly) NSInteger identifier;
@property (nonatomic, copy) NSString *titleText;
@property (nonatomic, copy) NSString *firstInfoText;
@property (nonatomic, copy) NSString *secondInfoText;
@@ -1,23 +1,22 @@
//
// ItemViewModel.m
// Sample
//
// Created by Samuel Stow on 12/29/15.
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import "ItemViewModel.h"
#import <stdatomic.h>
NSArray *titles;
NSArray *firstInfos;
@@ -32,28 +31,29 @@ NSArray *badges;
@implementation ItemViewModel
+ (instancetype)randomItem {
+ (ItemViewModel *)randomItem {
return [[ItemViewModel alloc] init];
}
- (instancetype)init {
self = [super init];
if (self) {
_titleText = [self randomObjectFromArray:titles];
_firstInfoText = [self randomObjectFromArray:firstInfos];
_secondInfoText = [NSString stringWithFormat:@"%zd+ bought", [self randomNumberInRange:5 to:6000]];
_originalPriceText = [NSString stringWithFormat:@"$%zd", [self randomNumberInRange:40 to:90]];
_finalPriceText = [NSString stringWithFormat:@"$%zd", [self randomNumberInRange:5 to:30]];
BOOL isSoldOut = arc4random() % 5 == 0;
_soldOutText = isSoldOut ? @"SOLD OUT" : nil;
_distanceLabelText = [NSString stringWithFormat:@"%zd mi", [self randomNumberInRange:1 to:20]];
BOOL isBadged = arc4random() % 2 == 0;
if (isBadged) {
_badgeText = [self randomObjectFromArray:badges];
}
_catNumber = [self randomNumberInRange:1 to:10];
_labelNumber = [self randomNumberInRange:1 to:10000];
static _Atomic(NSInteger) nextID = ATOMIC_VAR_INIT(1);
_identifier = atomic_fetch_add(&nextID, 1);
_titleText = [self randomObjectFromArray:titles];
_firstInfoText = [self randomObjectFromArray:firstInfos];
_secondInfoText = [NSString stringWithFormat:@"%zd+ bought", [self randomNumberInRange:5 to:6000]];
_originalPriceText = [NSString stringWithFormat:@"$%zd", [self randomNumberInRange:40 to:90]];
_finalPriceText = [NSString stringWithFormat:@"$%zd", [self randomNumberInRange:5 to:30]];
BOOL isSoldOut = arc4random() % 5 == 0;
_soldOutText = isSoldOut ? @"SOLD OUT" : nil;
_distanceLabelText = [NSString stringWithFormat:@"%zd mi", [self randomNumberInRange:1 to:20]];
BOOL isBadged = arc4random() % 2 == 0;
if (isBadged) {
_badgeText = [self randomObjectFromArray:badges];
}
_catNumber = [self randomNumberInRange:1 to:10];
_labelNumber = [self randomNumberInRange:1 to:10000];
}
return self;
}
@@ -1,11 +1,11 @@
//
// ViewController.m
// Sample
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
@@ -56,6 +56,7 @@ static const CGFloat kHorizontalSectionPadding = 10.0f;
_collectionNode.dataSource = self;
_collectionNode.delegate = self;
_collectionNode.backgroundColor = [UIColor grayColor];
_collectionNode.accessibilityIdentifier = @"Cat deals list";
ASRangeTuningParameters preloadTuning;
preloadTuning.leadingBufferScreenfuls = 2;
@@ -96,12 +97,10 @@ static const CGFloat kHorizontalSectionPadding = 10.0f;
ViewController *strongSelf = weakSelf;
if (strongSelf != nil)
{
NSLog(@"ViewController is not nil");
[strongSelf appendMoreItems:kBatchSize completion:completion];
NSLog(@"ViewController finished updating collectionView");
}
else {
NSLog(@"ViewController is nil - won't update collectionView");
NSLog(@"ViewController is nil - won't update collection");
}
};
@@ -113,13 +112,11 @@ static const CGFloat kHorizontalSectionPadding = 10.0f;
- (void)appendMoreItems:(NSInteger)numberOfNewItems completion:(void (^)(BOOL))completion {
NSArray *newData = [self getMoreData:numberOfNewItems];
dispatch_async(dispatch_get_main_queue(), ^{
[_collectionNode performBatchAnimated:YES updates:^{
[_data addObjectsFromArray:newData];
NSArray *addedIndexPaths = [self indexPathsForObjects:newData];
[_collectionNode insertItemsAtIndexPaths:addedIndexPaths];
} completion:completion];
});
[_collectionNode performBatchAnimated:YES updates:^{
[_data addObjectsFromArray:newData];
NSArray *addedIndexPaths = [self indexPathsForObjects:newData];
[_collectionNode insertItemsAtIndexPaths:addedIndexPaths];
} completion:completion];
}
- (NSArray *)getMoreData:(NSInteger)count {
@@ -196,7 +193,6 @@ static const CGFloat kHorizontalSectionPadding = 10.0f;
- (void)collectionNode:(ASCollectionNode *)collectionNode willBeginBatchFetchWithContext:(ASBatchContext *)context
{
NSLog(@"fetch additional content");
[self fetchMoreCatsWithCompletion:^(BOOL finished){
[context completeBatchFetching:YES];
}];