Lay some foundation for our new pending state controller

This commit is contained in:
Adlai Holler 2016-01-07 22:40:42 -08:00
parent d899f12f70
commit 99b674c346
11 changed files with 677 additions and 268 deletions

View File

@ -455,6 +455,16 @@
B350625F1B0111800018CF92 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 058D09AF195D04C000B7D73C /* Foundation.framework */; };
C78F7E2A1BF7808300CDEAFC /* ASTableNode.m in Sources */ = {isa = PBXBuildFile; fileRef = B0F880591BEAEC7500D17647 /* ASTableNode.m */; };
C78F7E2B1BF7809800CDEAFC /* ASTableNode.h in Headers */ = {isa = PBXBuildFile; fileRef = B0F880581BEAEC7500D17647 /* ASTableNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
CC3B20831C3F76D600798563 /* ASPendingStateController.h in Headers */ = {isa = PBXBuildFile; fileRef = CC3B20811C3F76D600798563 /* ASPendingStateController.h */; };
CC3B20841C3F76D600798563 /* ASPendingStateController.h in Headers */ = {isa = PBXBuildFile; fileRef = CC3B20811C3F76D600798563 /* ASPendingStateController.h */; };
CC3B20851C3F76D600798563 /* ASPendingStateController.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC3B20821C3F76D600798563 /* ASPendingStateController.mm */; };
CC3B20861C3F76D600798563 /* ASPendingStateController.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC3B20821C3F76D600798563 /* ASPendingStateController.mm */; };
CC3B20891C3F7A5400798563 /* ASWeakSet.h in Headers */ = {isa = PBXBuildFile; fileRef = CC3B20871C3F7A5400798563 /* ASWeakSet.h */; };
CC3B208A1C3F7A5400798563 /* ASWeakSet.h in Headers */ = {isa = PBXBuildFile; fileRef = CC3B20871C3F7A5400798563 /* ASWeakSet.h */; };
CC3B208B1C3F7A5400798563 /* ASWeakSet.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3B20881C3F7A5400798563 /* ASWeakSet.m */; };
CC3B208C1C3F7A5400798563 /* ASWeakSet.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3B20881C3F7A5400798563 /* ASWeakSet.m */; };
CC3B208E1C3F7D0A00798563 /* ASWeakSetTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3B208D1C3F7D0A00798563 /* ASWeakSetTests.m */; };
CC3B20901C3F892D00798563 /* ASPendingStateControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3B208F1C3F892D00798563 /* ASPendingStateControllerTests.m */; };
CC7FD9DE1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */; settings = {ATTRIBUTES = (Public, ); }; };
CC7FD9DF1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7FD9DD1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m */; };
CC7FD9E11BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7FD9E01BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m */; };
@ -786,6 +796,12 @@
B30BF6511C5964B0004FCD53 /* ASLayoutManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutManager.m; path = TextKit/ASLayoutManager.m; sourceTree = "<group>"; };
B35061DA1B010EDF0018CF92 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B35061DD1B010EDF0018CF92 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../AsyncDisplayKit-iOS/Info.plist"; sourceTree = "<group>"; };
CC3B20811C3F76D600798563 /* ASPendingStateController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPendingStateController.h; sourceTree = "<group>"; };
CC3B20821C3F76D600798563 /* ASPendingStateController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASPendingStateController.mm; sourceTree = "<group>"; };
CC3B20871C3F7A5400798563 /* ASWeakSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASWeakSet.h; sourceTree = "<group>"; };
CC3B20881C3F7A5400798563 /* ASWeakSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASWeakSet.m; sourceTree = "<group>"; };
CC3B208D1C3F7D0A00798563 /* ASWeakSetTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASWeakSetTests.m; sourceTree = "<group>"; };
CC3B208F1C3F892D00798563 /* ASPendingStateControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPendingStateControllerTests.m; sourceTree = "<group>"; };
CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPhotosFrameworkImageRequest.h; sourceTree = "<group>"; };
CC7FD9DD1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPhotosFrameworkImageRequest.m; sourceTree = "<group>"; };
CC7FD9E01BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPhotosFrameworkImageRequestTests.m; sourceTree = "<group>"; };
@ -1002,6 +1018,8 @@
children = (
DBC453211C5FD97200B16017 /* ASDisplayNodeImplicitHierarchyTests.m */,
DBC452DD1C5C6A6A00B16017 /* ArrayDiffingTests.m */,
CC3B208F1C3F892D00798563 /* ASPendingStateControllerTests.m */,
CC3B208D1C3F7D0A00798563 /* ASWeakSetTests.m */,
057D02C01AC0A66700C7AC3C /* AsyncDisplayKitTestHost */,
056D21501ABCEDA1001107EF /* ASSnapshotTestCase.h */,
05EA6FE61AC0966E00E35788 /* ASSnapshotTestCase.mm */,
@ -1123,6 +1141,10 @@
children = (
DB55C25F1C6408D6004EDCF5 /* _ASTransitionContext.h */,
DB55C2601C6408D6004EDCF5 /* _ASTransitionContext.m */,
CC3B20811C3F76D600798563 /* ASPendingStateController.h */,
CC3B20821C3F76D600798563 /* ASPendingStateController.mm */,
CC3B20871C3F7A5400798563 /* ASWeakSet.h */,
CC3B20881C3F7A5400798563 /* ASWeakSet.m */,
AC026B6D1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h */,
AC026B6E1BD57DBF00BBC17E /* _ASHierarchyChangeSet.m */,
9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */,
@ -1413,6 +1435,7 @@
257754B51BEE44CD00737CA5 /* ASTextKitTruncating.h in Headers */,
ACF6ED4F1B17847A00DA7C62 /* ASStackPositionedLayout.h in Headers */,
257754A71BEE44CD00737CA5 /* ASTextKitAttributes.h in Headers */,
CC3B20891C3F7A5400798563 /* ASWeakSet.h in Headers */,
ACF6ED511B17847A00DA7C62 /* ASStackUnpositionedLayout.h in Headers */,
9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */,
ACF6ED311B17843500DA7C62 /* ASStaticLayoutSpec.h in Headers */,
@ -1421,6 +1444,7 @@
257754C11BEE458E00737CA5 /* ASTextKitHelpers.h in Headers */,
B30BF6521C5964B0004FCD53 /* ASLayoutManager.h in Headers */,
0574D5E219C110940097DC25 /* ASTableViewProtocols.h in Headers */,
CC3B20831C3F76D600798563 /* ASPendingStateController.h in Headers */,
058D0A51195D05CB00B7D73C /* ASTextNode.h in Headers */,
058D0A81195D05F900B7D73C /* ASThread.h in Headers */,
ACC945A91BA9E7A0005E1FB8 /* ASViewController.h in Headers */,
@ -1475,6 +1499,7 @@
B35061F51B010EFD0018CF92 /* ASCollectionView.h in Headers */,
254C6B791BF94DF4003EC431 /* ASTextKitEntityAttribute.h in Headers */,
509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */,
CC3B20841C3F76D600798563 /* ASPendingStateController.h in Headers */,
B35061F71B010EFD0018CF92 /* ASCollectionViewProtocols.h in Headers */,
DE6EA3231C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */,
B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */,
@ -1517,6 +1542,7 @@
34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */,
B350625C1B010F070018CF92 /* ASLog.h in Headers */,
0442850E1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */,
CC3B208A1C3F7A5400798563 /* ASWeakSet.h in Headers */,
DE8BEAC21C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */,
B35062041B010EFD0018CF92 /* ASMultiplexImageNode.h in Headers */,
DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */,
@ -1611,6 +1637,7 @@
058D09B9195D04C000B7D73C /* Frameworks */,
058D09BA195D04C000B7D73C /* Resources */,
3B9D88CDF51B429C8409E4B6 /* Copy Pods Resources */,
78B4649EC16385BFAFD84D49 /* Embed Pods Frameworks */,
);
buildRules = (
);
@ -1740,6 +1767,21 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
78B4649EC16385BFAFD84D49 /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -1799,6 +1841,7 @@
257754C41BEE458E00737CA5 /* ASTextNodeWordKerner.m in Sources */,
058D0A1A195D050800B7D73C /* ASHighlightOverlayLayer.mm in Sources */,
058D0A2B195D050800B7D73C /* ASImageNode+CGExtras.m in Sources */,
CC3B208B1C3F7A5400798563 /* ASWeakSet.m in Sources */,
058D0A16195D050800B7D73C /* ASImageNode.mm in Sources */,
430E7C911B4C23F100697A4C /* ASIndexPath.m in Sources */,
ACF6ED231B17843500DA7C62 /* ASInsetLayoutSpec.mm in Sources */,
@ -1815,6 +1858,7 @@
058D0A1B195D050800B7D73C /* ASMutableAttributedStringBuilder.m in Sources */,
055B9FA91A1C154B00035D6D /* ASNetworkImageNode.mm in Sources */,
AEB7B01B1C5962EA00662EF4 /* ASDefaultPlayButton.m in Sources */,
CC3B20851C3F76D600798563 /* ASPendingStateController.mm in Sources */,
ACF6ED2C1B17843500DA7C62 /* ASOverlayLayoutSpec.mm in Sources */,
0442850F1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.mm in Sources */,
257754921BED28F300737CA5 /* ASEqualityHashHelpers.mm in Sources */,
@ -1861,6 +1905,7 @@
ACF6ED5C1B178DC700DA7C62 /* ASCenterLayoutSpecSnapshotTests.mm in Sources */,
9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */,
2911485C1A77147A005D0878 /* ASControlNodeTests.m in Sources */,
CC3B208E1C3F7D0A00798563 /* ASWeakSetTests.m in Sources */,
ACF6ED5D1B178DC700DA7C62 /* ASDimensionTests.mm in Sources */,
058D0A38195D057000B7D73C /* ASDisplayLayerTests.m in Sources */,
2538B6F31BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m in Sources */,
@ -1883,6 +1928,7 @@
AEEC47E41C21D3D200EC1693 /* ASVideoNodeTests.m in Sources */,
254C6B521BF8FE6D003EC431 /* ASTextKitTruncationTests.mm in Sources */,
058D0A3D195D057000B7D73C /* ASTextKitCoreTextAdditionsTests.m in Sources */,
CC3B20901C3F892D00798563 /* ASPendingStateControllerTests.m in Sources */,
058D0A40195D057000B7D73C /* ASTextNodeTests.m in Sources */,
DBC453221C5FD97200B16017 /* ASDisplayNodeImplicitHierarchyTests.m in Sources */,
058D0A41195D057000B7D73C /* ASTextNodeWordKernerTests.mm in Sources */,
@ -1936,6 +1982,7 @@
B35061FF1B010EFD0018CF92 /* ASDisplayNodeExtras.mm in Sources */,
B35062011B010EFD0018CF92 /* ASEditableTextNode.mm in Sources */,
254C6B881BF94F8A003EC431 /* ASTextKitRenderer.mm in Sources */,
CC3B208C1C3F7A5400798563 /* ASWeakSet.m in Sources */,
B350621C1B010EFD0018CF92 /* ASFlowLayoutController.mm in Sources */,
B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */,
B35062541B010EFD0018CF92 /* ASImageNode+CGExtras.m in Sources */,
@ -1950,6 +1997,7 @@
DECBD6EA1BE56E1900CF4905 /* ASButtonNode.mm in Sources */,
254C6B841BF94F8A003EC431 /* ASTextNodeWordKerner.m in Sources */,
34EFC76B1B701CEB00AD841F /* ASLayoutSpec.mm in Sources */,
CC3B20861C3F76D600798563 /* ASPendingStateController.mm in Sources */,
254C6B8C1BF94F8A003EC431 /* ASTextKitTailTruncater.mm in Sources */,
B35062051B010EFD0018CF92 /* ASMultiplexImageNode.mm in Sources */,
B35062251B010EFD0018CF92 /* ASMutableAttributedStringBuilder.m in Sources */,

View File

@ -981,6 +981,17 @@ static inline void filterNodesInLayoutAtIndexesWithIntersectingNodes(
_contentsScaleForDisplay = contentsScaleForDisplay;
}
- (void)applyPendingViewState
{
ASDisplayNodeAssertMainThread();
if (self.layerBacked) {
[_pendingViewState applyToLayer:self.layer];
} else {
[_pendingViewState applyToView:self.view];
}
[_pendingViewState clearChanges];
}
- (void)displayImmediately
{
ASDisplayNodeAssertMainThread();
@ -2366,13 +2377,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
// for the view/layer are still valid.
ASDN::MutexLocker l(_propertyLock);
if (_flags.layerBacked) {
[_pendingViewState applyToLayer:_layer];
} else {
[_pendingViewState applyToView:_view];
}
_pendingViewState = nil;
[self applyPendingViewState];
// TODO: move this into real pending state
if (_flags.displaySuspended) {

View File

@ -178,6 +178,8 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
@property (nonatomic, assign) CGFloat contentsScaleForDisplay;
- (void)applyPendingViewState;
/**
* // TODO: NOT YET IMPLEMENTED
*

View File

@ -0,0 +1,45 @@
//
// ASPendingStateController.h
// AsyncDisplayKit
//
// Created by Adlai Holler on 1/7/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import <Foundation/Foundation.h>
@class ASDisplayNode;
NS_ASSUME_NONNULL_BEGIN
/**
A singleton that is responsible for applying changes to
UIView/CALayer properties of display nodes when they
have been set on background threads.
This controller will enqueue run-loop events to flush changes
but if you need
*/
@interface ASPendingStateController : NSObject
+ (ASPendingStateController *)sharedInstance;
@property (nonatomic, readonly) BOOL hasChanges;
/**
Flush all pending states for nodes now. Any UIView/CALayer properties
that have been set in the background will be applied to their
corresponding views/layers before this method returns.
You must call this method on the main thread.
*/
- (void)flush;
/**
Register this node as having pending state that needs
*/
- (void)registerNode:(ASDisplayNode *)node;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,99 @@
//
// ASPendingStateController.m
// AsyncDisplayKit
//
// Created by Adlai Holler on 1/7/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import "ASPendingStateController.h"
#import "ASThread.h"
#import "ASWeakSet.h"
#import "ASDisplayNode.h"
@interface ASPendingStateController()
{
ASDN::Mutex _lock;
struct ASPendingStateControllerFlags {
unsigned pendingFlush:1;
} _flags;
}
@property (nonatomic, strong, readonly) ASWeakSet<ASDisplayNode *> *dirtyNodes;
@end
@implementation ASPendingStateController
#pragma mark Lifecycle & Singleton
- (instancetype)init
{
self = [super init];
if (self) {
_dirtyNodes = [ASWeakSet new];
}
return self;
}
+ (ASPendingStateController *)sharedInstance
{
static dispatch_once_t onceToken;
static ASPendingStateController *controller;
dispatch_once(&onceToken, ^{
controller = [ASPendingStateController new];
});
return controller;
}
#pragma mark External API
- (void)flush
{
ASDisplayNodeAssertMainThread();
[self flushNow];
}
- (void)registerNode:(ASDisplayNode *)node
{
ASDN::MutexLocker l(_lock);
[_dirtyNodes addObject:node];
[self scheduleFlushIfNeeded];
}
#pragma mark Private Methods
/**
This method is assumed to be called with the lock held.
*/
- (void)scheduleFlushIfNeeded
{
if (_flags.pendingFlush) {
return;
}
_flags.pendingFlush = YES;
[self performSelectorOnMainThread:@selector(flushNow) withObject:nil waitUntilDone:NO modes:@[ NSRunLoopCommonModes ]];
}
- (void)flushNow
{
ASDN::MutexLocker l(_lock);
for (__unused ASDisplayNode *node in _dirtyNodes) {
// TODO: apply pending state.
}
[_dirtyNodes removeAllObjects];
_flags.pendingFlush = NO;
}
@end
@implementation ASPendingStateController (Testing)
- (BOOL)test_isFlushScheduled
{
return _flags.pendingFlush;
}
@end

View File

@ -0,0 +1,40 @@
//
// ASWeakSet.h
// AsyncDisplayKit
//
// Created by Adlai Holler on 1/7/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ASWeakSet<__covariant ObjectType> : NSObject<NSFastEnumeration>
/// Returns YES if the receiver is empty, NO otherwise.
@property (nonatomic, readonly, getter=isEmpty) BOOL empty;
/// Returns YES if `object` is in the receiver, NO otherwise.
- (BOOL)containsObject:(ObjectType)object;
/// Insets `object` into the set.
- (void)addObject:(ObjectType)object;
/// Removes object from the set.
- (void)removeObject:(ObjectType)object;
/// Removes all objects from the set.
- (void)removeAllObjects;
/**
How many objects are contained in this set.
NOTE: This method is O(N). Consider using the `empty`
property.
*/
@property (nonatomic, readonly) NSUInteger count;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,68 @@
//
// ASWeakSet.m
// AsyncDisplayKit
//
// Created by Adlai Holler on 1/7/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import "ASWeakSet.h"
@interface ASWeakSet<__covariant ObjectType> ()
@property (nonatomic, strong, readonly) NSMapTable<NSNull *, NSNull *> *mapTable;
@end
@implementation ASWeakSet
- (instancetype)init
{
self = [super init];
if (self) {
_mapTable = [NSMapTable weakToStrongObjectsMapTable];
}
return self;
}
- (void)addObject:(id)object
{
[_mapTable setObject:[NSNull null] forKey:object];
}
- (void)removeObject:(id)object
{
[_mapTable removeObjectForKey:object];
}
- (void)removeAllObjects
{
[_mapTable removeAllObjects];
}
- (BOOL)containsObject:(id)object
{
return [_mapTable objectForKey:object] != nil;
}
- (BOOL)isEmpty
{
for (__unused id object in _mapTable) {
return NO;
}
return YES;
}
- (NSUInteger)count
{
NSInteger count = 0;
for (__unused id object in _mapTable) {
count += 1;
}
return count;
}
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id _Nonnull *)buffer count:(NSUInteger)len
{
return [_mapTable countByEnumeratingWithState:state objects:buffer count:len];
}
@end

View File

@ -30,4 +30,8 @@
+ (_ASPendingState *)pendingViewStateFromLayer:(CALayer *)layer;
+ (_ASPendingState *)pendingViewStateFromView:(UIView *)view;
@property (nonatomic, readonly) BOOL hasChanges;
- (void)clearChanges;
@end

View File

@ -12,6 +12,57 @@
#import "_ASAsyncTransactionContainer.h"
#import "ASAssert.h"
typedef struct {
// Properties
int needsDisplay:1;
int needsLayout:1;
// Flags indicating that a given property should be applied to the view at creation
int setClipsToBounds:1;
int setOpaque:1;
int setNeedsDisplayOnBoundsChange:1;
int setAutoresizesSubviews:1;
int setAutoresizingMask:1;
int setFrame:1;
int setBounds:1;
int setBackgroundColor:1;
int setTintColor:1;
int setContents:1;
int setHidden:1;
int setAlpha:1;
int setCornerRadius:1;
int setContentMode:1;
int setNeedsDisplay:1;
int setAnchorPoint:1;
int setPosition:1;
int setZPosition:1;
int setContentsScale:1;
int setTransform:1;
int setSublayerTransform:1;
int setUserInteractionEnabled:1;
int setExclusiveTouch:1;
int setShadowColor:1;
int setShadowOpacity:1;
int setShadowOffset:1;
int setShadowRadius:1;
int setBorderWidth:1;
int setBorderColor:1;
int setAsyncTransactionContainer:1;
int setAllowsEdgeAntialiasing:1;
int setEdgeAntialiasingMask:1;
int setIsAccessibilityElement:1;
int setAccessibilityLabel:1;
int setAccessibilityHint:1;
int setAccessibilityValue:1;
int setAccessibilityTraits:1;
int setAccessibilityFrame:1;
int setAccessibilityLanguage:1;
int setAccessibilityElementsHidden:1;
int setAccessibilityViewIsModal:1;
int setShouldGroupAccessibilityChildren:1;
int setAccessibilityIdentifier:1;
} ASPendingStateFlags;
@implementation _ASPendingState
{
@package //Expose all ivars for ASDisplayNode to bypass getters for efficiency
@ -50,56 +101,7 @@
BOOL shouldGroupAccessibilityChildren;
NSString *accessibilityIdentifier;
struct {
// Properties
int needsDisplay:1;
int needsLayout:1;
// Flags indicating that a given property should be applied to the view at creation
int setClipsToBounds:1;
int setOpaque:1;
int setNeedsDisplayOnBoundsChange:1;
int setAutoresizesSubviews:1;
int setAutoresizingMask:1;
int setFrame:1;
int setBounds:1;
int setBackgroundColor:1;
int setTintColor:1;
int setContents:1;
int setHidden:1;
int setAlpha:1;
int setCornerRadius:1;
int setContentMode:1;
int setNeedsDisplay:1;
int setAnchorPoint:1;
int setPosition:1;
int setZPosition:1;
int setContentsScale:1;
int setTransform:1;
int setSublayerTransform:1;
int setUserInteractionEnabled:1;
int setExclusiveTouch:1;
int setShadowColor:1;
int setShadowOpacity:1;
int setShadowOffset:1;
int setShadowRadius:1;
int setBorderWidth:1;
int setBorderColor:1;
int setAsyncTransactionContainer:1;
int setAllowsEdgeAntialiasing:1;
int setEdgeAntialiasingMask:1;
int setIsAccessibilityElement:1;
int setAccessibilityLabel:1;
int setAccessibilityHint:1;
int setAccessibilityValue:1;
int setAccessibilityTraits:1;
int setAccessibilityFrame:1;
int setAccessibilityLanguage:1;
int setAccessibilityElementsHidden:1;
int setAccessibilityViewIsModal:1;
int setShouldGroupAccessibilityChildren:1;
int setAccessibilityIdentifier:1;
} _flags;
ASPendingStateFlags _flags;
}
@ -199,12 +201,6 @@ static UIColor *defaultTintColor = nil;
return self;
}
- (CALayer *)layer
{
ASDisplayNodeAssert(NO, @"One shouldn't call node.layer when the view isn't loaded, but we're returning nil to not crash if someone is still doing this");
return nil;
}
- (void)setNeedsDisplay
{
_flags.needsDisplay = YES;
@ -560,91 +556,92 @@ static UIColor *defaultTintColor = nil;
- (void)applyToLayer:(CALayer *)layer
{
if (_flags.setAnchorPoint)
ASPendingStateFlags flags = _flags;
if (flags.setAnchorPoint)
layer.anchorPoint = anchorPoint;
if (_flags.setPosition)
if (flags.setPosition)
layer.position = position;
if (_flags.setZPosition)
if (flags.setZPosition)
layer.zPosition = zPosition;
if (_flags.setBounds)
if (flags.setBounds)
layer.bounds = bounds;
if (_flags.setContentsScale)
if (flags.setContentsScale)
layer.contentsScale = contentsScale;
if (_flags.setTransform)
if (flags.setTransform)
layer.transform = transform;
if (_flags.setSublayerTransform)
if (flags.setSublayerTransform)
layer.sublayerTransform = sublayerTransform;
if (_flags.setContents)
if (flags.setContents)
layer.contents = contents;
if (_flags.setClipsToBounds)
if (flags.setClipsToBounds)
layer.masksToBounds = clipsToBounds;
if (_flags.setBackgroundColor)
if (flags.setBackgroundColor)
layer.backgroundColor = backgroundColor;
if (_flags.setOpaque)
if (flags.setOpaque)
layer.opaque = opaque;
if (_flags.setHidden)
if (flags.setHidden)
layer.hidden = isHidden;
if (_flags.setAlpha)
if (flags.setAlpha)
layer.opacity = alpha;
if (_flags.setCornerRadius)
if (flags.setCornerRadius)
layer.cornerRadius = cornerRadius;
if (_flags.setContentMode)
if (flags.setContentMode)
layer.contentsGravity = ASDisplayNodeCAContentsGravityFromUIContentMode(contentMode);
if (_flags.setShadowColor)
if (flags.setShadowColor)
layer.shadowColor = shadowColor;
if (_flags.setShadowOpacity)
if (flags.setShadowOpacity)
layer.shadowOpacity = shadowOpacity;
if (_flags.setShadowOffset)
if (flags.setShadowOffset)
layer.shadowOffset = shadowOffset;
if (_flags.setShadowRadius)
if (flags.setShadowRadius)
layer.shadowRadius = shadowRadius;
if (_flags.setBorderWidth)
if (flags.setBorderWidth)
layer.borderWidth = borderWidth;
if (_flags.setBorderColor)
if (flags.setBorderColor)
layer.borderColor = borderColor;
if (_flags.setNeedsDisplayOnBoundsChange)
if (flags.setNeedsDisplayOnBoundsChange)
layer.needsDisplayOnBoundsChange = needsDisplayOnBoundsChange;
if (_flags.setAllowsEdgeAntialiasing)
if (flags.setAllowsEdgeAntialiasing)
layer.allowsEdgeAntialiasing = allowsEdgeAntialiasing;
if (_flags.setEdgeAntialiasingMask)
if (flags.setEdgeAntialiasingMask)
layer.edgeAntialiasingMask = edgeAntialiasingMask;
if (_flags.needsDisplay)
if (flags.needsDisplay)
[layer setNeedsDisplay];
if (_flags.needsLayout)
if (flags.needsLayout)
[layer setNeedsLayout];
if (_flags.setAsyncTransactionContainer)
if (flags.setAsyncTransactionContainer)
layer.asyncdisplaykit_asyncTransactionContainer = asyncTransactionContainer;
if (_flags.setOpaque)
if (flags.setOpaque)
ASDisplayNodeAssert(layer.opaque == opaque, @"Didn't set opaque as desired");
if (_flags.setFrame)
if (flags.setFrame)
ASDisplayNodeAssert(NO, @"Frame property should only be used for synchronously wrapped nodes. See setFrame: in ASDisplayNode+UIViewBridge");
}
@ -660,143 +657,144 @@ static UIColor *defaultTintColor = nil;
CALayer *layer = view.layer;
if (_flags.setAnchorPoint)
ASPendingStateFlags flags = _flags;
if (flags.setAnchorPoint)
layer.anchorPoint = anchorPoint;
if (_flags.setPosition)
if (flags.setPosition)
layer.position = position;
if (_flags.setZPosition)
if (flags.setZPosition)
layer.zPosition = zPosition;
// This should only be used for synchronous views wrapped by nodes.
if (_flags.setFrame && !(_flags.setBounds && _flags.setPosition)) {
if (flags.setFrame && !(flags.setBounds && flags.setPosition)) {
view.frame = frame;
}
if (_flags.setBounds)
if (flags.setBounds)
view.bounds = bounds;
if (_flags.setContentsScale)
if (flags.setContentsScale)
layer.contentsScale = contentsScale;
if (_flags.setTransform)
if (flags.setTransform)
layer.transform = transform;
if (_flags.setSublayerTransform)
if (flags.setSublayerTransform)
layer.sublayerTransform = sublayerTransform;
if (_flags.setContents)
if (flags.setContents)
layer.contents = contents;
if (_flags.setClipsToBounds)
if (flags.setClipsToBounds)
view.clipsToBounds = clipsToBounds;
if (_flags.setBackgroundColor)
if (flags.setBackgroundColor)
layer.backgroundColor = backgroundColor;
if (_flags.setTintColor)
if (flags.setTintColor)
view.tintColor = self.tintColor;
if (_flags.setOpaque)
if (flags.setOpaque)
view.layer.opaque = opaque;
if (_flags.setHidden)
if (flags.setHidden)
view.hidden = isHidden;
if (_flags.setAlpha)
if (flags.setAlpha)
view.alpha = alpha;
if (_flags.setCornerRadius)
if (flags.setCornerRadius)
layer.cornerRadius = cornerRadius;
if (_flags.setContentMode)
if (flags.setContentMode)
view.contentMode = contentMode;
if (_flags.setUserInteractionEnabled)
if (flags.setUserInteractionEnabled)
view.userInteractionEnabled = userInteractionEnabled;
#if TARGET_OS_IOS
if (_flags.setExclusiveTouch)
if (flags.setExclusiveTouch)
view.exclusiveTouch = exclusiveTouch;
#endif
if (_flags.setShadowColor)
if (flags.setShadowColor)
layer.shadowColor = shadowColor;
if (_flags.setShadowOpacity)
if (flags.setShadowOpacity)
layer.shadowOpacity = shadowOpacity;
if (_flags.setShadowOffset)
if (flags.setShadowOffset)
layer.shadowOffset = shadowOffset;
if (_flags.setShadowRadius)
if (flags.setShadowRadius)
layer.shadowRadius = shadowRadius;
if (_flags.setBorderWidth)
if (flags.setBorderWidth)
layer.borderWidth = borderWidth;
if (_flags.setBorderColor)
if (flags.setBorderColor)
layer.borderColor = borderColor;
if (_flags.setAutoresizingMask)
if (flags.setAutoresizingMask)
view.autoresizingMask = autoresizingMask;
if (_flags.setAutoresizesSubviews)
if (flags.setAutoresizesSubviews)
view.autoresizesSubviews = autoresizesSubviews;
if (_flags.setNeedsDisplayOnBoundsChange)
if (flags.setNeedsDisplayOnBoundsChange)
layer.needsDisplayOnBoundsChange = needsDisplayOnBoundsChange;
if (_flags.setAllowsEdgeAntialiasing)
if (flags.setAllowsEdgeAntialiasing)
layer.allowsEdgeAntialiasing = allowsEdgeAntialiasing;
if (_flags.setEdgeAntialiasingMask)
if (flags.setEdgeAntialiasingMask)
layer.edgeAntialiasingMask = edgeAntialiasingMask;
if (_flags.needsDisplay)
if (flags.needsDisplay)
[view setNeedsDisplay];
if (_flags.needsLayout)
if (flags.needsLayout)
[view setNeedsLayout];
if (_flags.setAsyncTransactionContainer)
if (flags.setAsyncTransactionContainer)
view.asyncdisplaykit_asyncTransactionContainer = asyncTransactionContainer;
if (_flags.setOpaque)
if (flags.setOpaque)
ASDisplayNodeAssert(view.layer.opaque == opaque, @"Didn't set opaque as desired");
if (_flags.setIsAccessibilityElement)
if (flags.setIsAccessibilityElement)
view.isAccessibilityElement = isAccessibilityElement;
if (_flags.setAccessibilityLabel)
if (flags.setAccessibilityLabel)
view.accessibilityLabel = accessibilityLabel;
if (_flags.setAccessibilityHint)
if (flags.setAccessibilityHint)
view.accessibilityHint = accessibilityHint;
if (_flags.setAccessibilityValue)
if (flags.setAccessibilityValue)
view.accessibilityValue = accessibilityValue;
if (_flags.setAccessibilityTraits)
if (flags.setAccessibilityTraits)
view.accessibilityTraits = accessibilityTraits;
if (_flags.setAccessibilityFrame)
if (flags.setAccessibilityFrame)
view.accessibilityFrame = accessibilityFrame;
if (_flags.setAccessibilityLanguage)
if (flags.setAccessibilityLanguage)
view.accessibilityLanguage = accessibilityLanguage;
if (_flags.setAccessibilityElementsHidden)
if (flags.setAccessibilityElementsHidden)
view.accessibilityElementsHidden = accessibilityElementsHidden;
if (_flags.setAccessibilityViewIsModal)
if (flags.setAccessibilityViewIsModal)
view.accessibilityViewIsModal = accessibilityViewIsModal;
if (_flags.setShouldGroupAccessibilityChildren)
if (flags.setShouldGroupAccessibilityChildren)
view.shouldGroupAccessibilityChildren = shouldGroupAccessibilityChildren;
if (_flags.setAccessibilityIdentifier)
if (flags.setAccessibilityIdentifier)
view.accessibilityIdentifier = accessibilityIdentifier;
}
@ -806,81 +804,31 @@ static UIColor *defaultTintColor = nil;
if (!layer) {
return nil;
}
_ASPendingState *pendingState = [[_ASPendingState alloc] init];
pendingState.anchorPoint = layer.anchorPoint;
(pendingState->_flags).setAnchorPoint = YES;
pendingState.position = layer.position;
(pendingState->_flags).setPosition = YES;
pendingState.zPosition = layer.zPosition;
(pendingState->_flags).setZPosition = YES;
pendingState.bounds = layer.bounds;
(pendingState->_flags).setBounds = YES;
pendingState.contentsScale = layer.contentsScale;
(pendingState->_flags).setContentsScale = YES;
pendingState.transform = layer.transform;
(pendingState->_flags).setTransform = YES;
pendingState.sublayerTransform = layer.sublayerTransform;
(pendingState->_flags).setSublayerTransform = YES;
pendingState.contents = layer.contents;
(pendingState->_flags).setContents = YES;
pendingState.clipsToBounds = layer.masksToBounds;
(pendingState->_flags).setClipsToBounds = YES;
pendingState.backgroundColor = layer.backgroundColor;
(pendingState->_flags).setBackgroundColor = YES;
pendingState.opaque = layer.opaque;
(pendingState->_flags).setOpaque = YES;
pendingState.hidden = layer.hidden;
(pendingState->_flags).setHidden = YES;
pendingState.alpha = layer.opacity;
(pendingState->_flags).setAlpha = YES;
pendingState.cornerRadius = layer.cornerRadius;
(pendingState->_flags).setCornerRadius = YES;
pendingState.contentMode = ASDisplayNodeUIContentModeFromCAContentsGravity(layer.contentsGravity);
(pendingState->_flags).setContentMode = YES;
pendingState.shadowColor = layer.shadowColor;
(pendingState->_flags).setShadowColor = YES;
pendingState.shadowOpacity = layer.shadowOpacity;
(pendingState->_flags).setShadowOpacity = YES;
pendingState.shadowOffset = layer.shadowOffset;
(pendingState->_flags).setShadowOffset = YES;
pendingState.shadowRadius = layer.shadowRadius;
(pendingState->_flags).setShadowRadius = YES;
pendingState.borderWidth = layer.borderWidth;
(pendingState->_flags).setBorderWidth = YES;
pendingState.borderColor = layer.borderColor;
(pendingState->_flags).setBorderColor = YES;
pendingState.needsDisplayOnBoundsChange = layer.needsDisplayOnBoundsChange;
(pendingState->_flags).setNeedsDisplayOnBoundsChange = YES;
pendingState.allowsEdgeAntialiasing = layer.allowsEdgeAntialiasing;
(pendingState->_flags).setAllowsEdgeAntialiasing = YES;
pendingState.edgeAntialiasingMask = layer.edgeAntialiasingMask;
(pendingState->_flags).setEdgeAntialiasingMask = YES;
return pendingState;
}
@ -890,134 +838,110 @@ static UIColor *defaultTintColor = nil;
if (!view) {
return nil;
}
_ASPendingState *pendingState = [[_ASPendingState alloc] init];
CALayer *layer = view.layer;
pendingState.anchorPoint = layer.anchorPoint;
(pendingState->_flags).setAnchorPoint = YES;
pendingState.position = layer.position;
(pendingState->_flags).setPosition = YES;
pendingState.zPosition = layer.zPosition;
(pendingState->_flags).setZPosition = YES;
pendingState.bounds = view.bounds;
(pendingState->_flags).setBounds = YES;
pendingState.contentsScale = layer.contentsScale;
(pendingState->_flags).setContentsScale = YES;
pendingState.transform = layer.transform;
(pendingState->_flags).setTransform = YES;
pendingState.sublayerTransform = layer.sublayerTransform;
(pendingState->_flags).setSublayerTransform = YES;
pendingState.contents = layer.contents;
(pendingState->_flags).setContents = YES;
pendingState.clipsToBounds = view.clipsToBounds;
(pendingState->_flags).setClipsToBounds = YES;
pendingState.backgroundColor = layer.backgroundColor;
(pendingState->_flags).setBackgroundColor = YES;
pendingState.tintColor = view.tintColor;
(pendingState->_flags).setTintColor = YES;
pendingState.opaque = layer.opaque;
(pendingState->_flags).setOpaque = YES;
pendingState.hidden = view.hidden;
(pendingState->_flags).setHidden = YES;
pendingState.alpha = view.alpha;
(pendingState->_flags).setAlpha = YES;
pendingState.cornerRadius = layer.cornerRadius;
(pendingState->_flags).setCornerRadius = YES;
pendingState.contentMode = view.contentMode;
(pendingState->_flags).setContentMode = YES;
pendingState.userInteractionEnabled = view.userInteractionEnabled;
(pendingState->_flags).setUserInteractionEnabled = YES;
#if TARGET_OS_IOS
pendingState.exclusiveTouch = view.exclusiveTouch;
(pendingState->_flags).setExclusiveTouch = YES;
#endif
pendingState.shadowColor = layer.shadowColor;
(pendingState->_flags).setShadowColor = YES;
pendingState.shadowOpacity = layer.shadowOpacity;
(pendingState->_flags).setShadowOpacity = YES;
pendingState.shadowOffset = layer.shadowOffset;
(pendingState->_flags).setShadowOffset = YES;
pendingState.shadowRadius = layer.shadowRadius;
(pendingState->_flags).setShadowRadius = YES;
pendingState.borderWidth = layer.borderWidth;
(pendingState->_flags).setBorderWidth = YES;
pendingState.borderColor = layer.borderColor;
(pendingState->_flags).setBorderColor = YES;
pendingState.autoresizingMask = view.autoresizingMask;
(pendingState->_flags).setAutoresizingMask = YES;
pendingState.autoresizesSubviews = view.autoresizesSubviews;
(pendingState->_flags).setAutoresizesSubviews = YES;
pendingState.needsDisplayOnBoundsChange = layer.needsDisplayOnBoundsChange;
(pendingState->_flags).setNeedsDisplayOnBoundsChange = YES;
pendingState.allowsEdgeAntialiasing = layer.allowsEdgeAntialiasing;
(pendingState->_flags).setAllowsEdgeAntialiasing = YES;
pendingState.edgeAntialiasingMask = layer.edgeAntialiasingMask;
(pendingState->_flags).setEdgeAntialiasingMask = YES;
pendingState.isAccessibilityElement = view.isAccessibilityElement;
(pendingState->_flags).setIsAccessibilityElement = YES;
pendingState.accessibilityLabel = view.accessibilityLabel;
(pendingState->_flags).setAccessibilityLabel = YES;
pendingState.accessibilityHint = view.accessibilityHint;
(pendingState->_flags).setAccessibilityHint = YES;
pendingState.accessibilityValue = view.accessibilityValue;
(pendingState->_flags).setAccessibilityValue = YES;
pendingState.accessibilityTraits = view.accessibilityTraits;
(pendingState->_flags).setAccessibilityTraits = YES;
pendingState.accessibilityFrame = view.accessibilityFrame;
(pendingState->_flags).setAccessibilityFrame = YES;
pendingState.accessibilityLanguage = view.accessibilityLanguage;
(pendingState->_flags).setAccessibilityLanguage = YES;
pendingState.accessibilityElementsHidden = view.accessibilityElementsHidden;
(pendingState->_flags).setAccessibilityElementsHidden = YES;
pendingState.accessibilityViewIsModal = view.accessibilityViewIsModal;
(pendingState->_flags).setAccessibilityViewIsModal = YES;
pendingState.shouldGroupAccessibilityChildren = view.shouldGroupAccessibilityChildren;
(pendingState->_flags).setShouldGroupAccessibilityChildren = YES;
pendingState.accessibilityIdentifier = view.accessibilityIdentifier;
(pendingState->_flags).setAccessibilityIdentifier = YES;
return pendingState;
}
- (void)clearChanges
{
_flags = (ASPendingStateFlags){ 0 };
}
- (BOOL)hasChanges
{
ASPendingStateFlags flags = _flags;
return (flags.setAnchorPoint
|| flags.setPosition
|| flags.setZPosition
|| flags.setFrame
|| flags.setBounds
|| flags.setPosition
|| flags.setContentsScale
|| flags.setTransform
|| flags.setSublayerTransform
|| flags.setContents
|| flags.setClipsToBounds
|| flags.setBackgroundColor
|| flags.setTintColor
|| flags.setHidden
|| flags.setAlpha
|| flags.setCornerRadius
|| flags.setContentMode
|| flags.setUserInteractionEnabled
|| flags.setExclusiveTouch
|| flags.setShadowOpacity
|| flags.setShadowOffset
|| flags.setShadowRadius
|| flags.setShadowColor
|| flags.setBorderWidth
|| flags.setBorderColor
|| flags.setAutoresizingMask
|| flags.setAutoresizesSubviews
|| flags.setNeedsDisplayOnBoundsChange
|| flags.setAllowsEdgeAntialiasing
|| flags.setEdgeAntialiasingMask
|| flags.needsDisplay
|| flags.needsLayout
|| flags.setAsyncTransactionContainer
|| flags.setOpaque
|| flags.setIsAccessibilityElement
|| flags.setAccessibilityLabel
|| flags.setAccessibilityHint
|| flags.setAccessibilityValue
|| flags.setAccessibilityTraits
|| flags.setAccessibilityFrame
|| flags.setAccessibilityLanguage
|| flags.setAccessibilityElementsHidden
|| flags.setAccessibilityViewIsModal
|| flags.setShouldGroupAccessibilityChildren
|| flags.setAccessibilityIdentifier);
}
- (void)dealloc
{
CGColorRelease(backgroundColor);

View File

@ -0,0 +1,40 @@
//
// ASPendingStateControllerTests.m
// AsyncDisplayKit
//
// Created by Adlai Holler on 1/7/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "ASPendingStateController.h"
#import "ASDisplayNode.h"
@interface ASPendingStateController (Testing)
- (BOOL)test_isFlushScheduled;
@end
@interface ASPendingStateControllerTests : XCTestCase
@end
@implementation ASPendingStateControllerTests
- (void)testTheresASharedInstance
{
XCTAssertNotNil([ASPendingStateController sharedInstance]);
}
- (void)testThatRegisteringANodeCausesAtFlushAtRunLoopEnd
{
ASPendingStateController *ctrl = [ASPendingStateController sharedInstance];
ASDisplayNode *node = [ASDisplayNode new];
XCTAssertFalse(ctrl.test_isFlushScheduled);
[ctrl registerNode:node];
XCTAssertTrue(ctrl.test_isFlushScheduled);
NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:1];
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeout];
XCTAssertFalse(ctrl.test_isFlushScheduled);
}
@end

View File

@ -0,0 +1,134 @@
//
// ASWeakSetTests.m
// AsyncDisplayKit
//
// Created by Adlai Holler on 1/7/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "ASWeakSet.h"
@interface ASWeakSetTests : XCTestCase
@end
@implementation ASWeakSetTests
- (void)testAddingACoupleRetainedObjects
{
ASWeakSet <NSString *> *weakSet = [ASWeakSet new];
NSString *hello = @"hello";
NSString *world = @"hello";
[weakSet addObject:hello];
[weakSet addObject:world];
XCTAssert([weakSet containsObject:hello]);
XCTAssert([weakSet containsObject:world]);
XCTAssert(![weakSet containsObject:@"apple"]);
}
- (void)testThatCountIncorporatesDeallocatedObjects
{
ASWeakSet *weakSet = [ASWeakSet new];
XCTAssertEqual(weakSet.count, 0);
NSObject *a = [NSObject new];
NSObject *b = [NSObject new];
[weakSet addObject:a];
[weakSet addObject:b];
XCTAssertEqual(weakSet.count, 2);
@autoreleasepool {
NSObject *doomedObject = [NSObject new];
[weakSet addObject:doomedObject];
XCTAssertEqual(weakSet.count, 3);
}
XCTAssertEqual(weakSet.count, 2);
}
- (void)testThatIsEmptyIncorporatesDeallocatedObjects
{
ASWeakSet *weakSet = [ASWeakSet new];
XCTAssertTrue(weakSet.isEmpty);
@autoreleasepool {
NSObject *doomedObject = [NSObject new];
[weakSet addObject:doomedObject];
XCTAssertFalse(weakSet.isEmpty);
}
XCTAssertTrue(weakSet.isEmpty);
}
- (void)testThatContainsObjectWorks
{
ASWeakSet *weakSet = [ASWeakSet new];
NSObject *a = [NSObject new];
NSObject *b = [NSObject new];
[weakSet addObject:a];
XCTAssertTrue([weakSet containsObject:a]);
XCTAssertFalse([weakSet containsObject:b]);
}
- (void)testThatRemoveObjectWorks
{
ASWeakSet *weakSet = [ASWeakSet new];
NSObject *a = [NSObject new];
NSObject *b = [NSObject new];
[weakSet addObject:a];
[weakSet addObject:b];
XCTAssertTrue([weakSet containsObject:a]);
XCTAssertTrue([weakSet containsObject:b]);
XCTAssertEqual(weakSet.count, 2);
[weakSet removeObject:b];
XCTAssertTrue([weakSet containsObject:a]);
XCTAssertFalse([weakSet containsObject:b]);
XCTAssertEqual(weakSet.count, 1);
}
- (void)testThatFastEnumerationWorks
{
ASWeakSet *weakSet = [ASWeakSet new];
NSObject *a = [NSObject new];
NSObject *b = [NSObject new];
[weakSet addObject:a];
[weakSet addObject:b];
@autoreleasepool {
NSObject *doomedObject = [NSObject new];
[weakSet addObject:doomedObject];
XCTAssertEqual(weakSet.count, 3);
}
NSInteger i = 0;
NSMutableSet *awaitingObjects = [NSMutableSet setWithObjects:a, b, nil];
for (NSObject *object in weakSet) {
XCTAssertTrue([awaitingObjects containsObject:object]);
[awaitingObjects removeObject:object];
i += 1;
}
XCTAssertEqual(i, 2);
}
- (void)testThatRemoveAllObjectsWorks
{
ASWeakSet *weakSet = [ASWeakSet new];
NSObject *a = [NSObject new];
NSObject *b = [NSObject new];
[weakSet addObject:a];
[weakSet addObject:b];
XCTAssertEqual(weakSet.count, 2);
[weakSet removeAllObjects];
XCTAssertEqual(weakSet.count, 0);
NSInteger i = 0;
for (__unused NSObject *object in weakSet) {
i += 1;
}
XCTAssertEqual(i, 0);
}
@end