ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method.", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method.", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measure:)), @"Subclass %@ must not override measure: method", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange: method. Instead override calculateLayoutThatFits:", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:)), @"Subclass %@ must not override layoutThatFits: method. Instead override calculateLayoutThatFits:.", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:parentSize:)), @"Subclass %@ must not override layoutThatFits:parentSize method. Instead override calculateLayoutThatFits:.", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method.", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearPreloadedData)), @"Subclass %@ must not override recursivelyClearFetchedData method.", classString);
}
// Below we are pre-calculating values per-class and dynamically adding a method (_staticInitialize) to populate these values
// when each instance is constructed. These values don't change for each class, so there is significant performance benefit
// in doing it here. +initialize is guaranteed to be called before any instance method so it is safe to add this method here.
// Note that we take care to detect if the class overrides +respondsToSelector: or -respondsToSelector and take the slow path
// (recalculating for each instance) to make sure we are always correct.
ASDisplayNodeAssert(node.class == initializeSelf, @"Node class %@ does not have a matching _staticInitialize method; check to ensure [super initialize] is called within any custom +initialize implementations! Overridden methods will not be called unless they are also implemented by superclass %@", node.class, initializeSelf);
ASDisplayNodeAssert(oldSubnodes.count == subnodes.count, @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior.");
for (NSInteger i = 0; i < oldSubnodes.count; i++) {
ASDisplayNodeAssert(oldSubnodes[i] == subnodes[i], @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior.");
}
return layoutElement;
});
}
#endif
}
+ (void)load
{
// Ensure this value is cached on the main thread before needed in the background.
ASScreenScale();
}
+ (BOOL)layerBackedNodesEnabled
{
returnYES;
}
+ (Class)viewClass
{
return [_ASDisplayView class];
}
+ (Class)layerClass
{
return [_ASDisplayLayer class];
}
#pragma mark - Lifecycle
- (void)_staticInitialize
{
ASDisplayNodeAssert(NO, @"_staticInitialize must be overridden");
// Synchronous nodes may not be able to call the hierarchy notifications, so only enforce for regular nodes.
ASDisplayNodeAssert(_flags.synchronous || !ASInterfaceStateIncludesVisible(_interfaceState), @"Node should always be marked invisible before deallocating. Node: %@", self);
self.asyncLayer.asyncDelegate = nil;
_view.asyncdisplaykit_node = nil;
_layer.asyncdisplaykit_node = nil;
// Remove any subnodes so they lose their connection to the now deallocated parent. This can happen
// because subnodes do not retain their supernode, but subnodes can legitimately remain alive if another
// thing outside the view hierarchy system (e.g. async display, controller code, etc). keeps a retained
// reference to subnodes.
for (ASDisplayNode *subnode in _subnodes)
[subnode __setSupernode:nil];
// Trampoline any UIKit ivars' deallocation to main
if (ASDisplayNodeThreadIsMain() == NO) {
[self_scheduleIvarsForMainDeallocation];
}
_subnodes = nil;
// TODO: Remove this? If supernode isn't already nil, this method isn't dealloc-safe anyway.
@"Subclass %@ must at least provide a layoutSpecBlock or override at most one of the three layout methods: calculateLayoutThatFits:, layoutSpecThatFits:, or calculateSizeThatFits:", NSStringFromClass(self.class))
if (_calculatedDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, parentSize)) {
ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _calculatedDisplayNodeLayout->layout should not be nil! %@", self);
// Our calculated layout is suitable for this constrainedSize, so keep using it and
// invalidate any pending layout that has been generated in the past.
ASDisplayNodeAssertNotNil(_pendingDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _pendingDisplayNodeLayout->layout should not be nil! %@", self);