@"Subclass %@ must at least provide a layoutSpecBlock or override at most one of the three layout methods: calculateLayoutThatFits, layoutSpecThatFits or calculateSizeThatFits", NSStringFromClass(self.class))
+ (void)initialize
{
[superinitialize];
if (self != [ASDisplayNode class]) {
// Subclasses should never override these. Use unused to prevent warnings
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 overwrite calculateLayoutThatFits:", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:)), @"Subclass %@ must not override layoutThatFits: method", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:parentSize:)), @"Subclass %@ must not override layoutThatFits:parentSize method", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearFetchedData)), @"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);
// 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];
_view = nil;
_subnodes = nil;
_layer = nil;
// TODO: Remove this? If supernode isn't already nil, this method isn't dealloc-safe anyway.
[self__setSupernode:nil];
_pendingViewState = nil;
_pendingDisplayNodes = nil;
}
#pragma mark - Core
- (void)__unloadNode
{
ASDisplayNodeAssertMainThread();
ASDisplayNodeAssert([selfisNodeLoaded], @"Implementation shouldn't call __unloadNode if not loaded: %@", self);
ASDisplayNodeAssert(_flags.synchronous == NO, @"Node created using -initWithViewBlock:/-initWithLayerBlock: cannot be unloaded. Node: %@", self);
// Passed constrainedSize is the the same as the node's current constrained size it's a noop
ASDisplayNodeAssertMainThread();
if ([selfshouldCalculateLayoutWithConstrainedSize:constrainedSize parentSize:constrainedSize.max] == NO) {
return;
}
{
ASDN::MutexLocker l(__instanceLock__);
ASDisplayNodeAssert(ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO, @"Can't start a transition when one of the supernodes is performing one.");
}
int32_t transitionID = [self_startNewTransition];
// Move all subnodes in a pending state
ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
ASDisplayNodeAssert([node _isTransitionInProgress] == NO, @"Can't start a transition when one of the subnodes is performing one.");