Merge pull request #1484 from Adlai-Holler/AHNoDuplicateActions

[ASControlNode] Deduplicate Target-Action-Events Entries, Fix Exception When Removing Nil Target
This commit is contained in:
appleguy
2016-04-05 18:48:15 -07:00
2 changed files with 76 additions and 5 deletions

View File

@@ -277,16 +277,17 @@ static BOOL _enableHitTestDebug = NO;
}
// Have we seen this target before for this event?
NSMutableArray *targetActions = [eventDispatchTable objectForKey:target];
NSMutableSet *targetActions = [eventDispatchTable objectForKey:target];
if (!targetActions)
{
// Nope. Create an actions array for it.
targetActions = [[NSMutableArray alloc] initWithCapacity:kASControlNodeActionDispatchTableInitialCapacity]; // enough to handle common types without re-hashing the dictionary when adding entries.
// Nope. Create an action set for it.
targetActions = [[NSMutableSet alloc] initWithCapacity:kASControlNodeActionDispatchTableInitialCapacity]; // enough to handle common types without re-hashing the dictionary when adding entries.
[eventDispatchTable setObject:targetActions forKey:target];
}
// Add the action message.
// Note that bizarrely enough UIControl (at least according to the docs) supports duplicate target-action pairs for a particular control event, so we replicate that behavior.
// UIControl does not support duplicate target-action-events entries, so we replicate that behavior.
// See: https://github.com/facebook/AsyncDisplayKit/files/205466/DuplicateActionsTest.playground.zip
[targetActions addObject:NSStringFromSelector(action)];
});
@@ -371,7 +372,7 @@ static BOOL _enableHitTestDebug = NO;
if (!target)
{
// Look at every target, removing target-pairs that have action (or all of its actions).
for (id aTarget in eventDispatchTable)
for (id aTarget in [eventDispatchTable copy])
removeActionFromTarget(aTarget, action);
}
else

View File

@@ -106,6 +106,76 @@
XCTAssert(controller.hits == 1, @"Controller did not receive the action event");
}
- (void)testRemoveWithoutTargetRemovesTargetlessAction {
ASActionSenderEventController *controller = [[ASActionSenderEventController alloc] init];
ASControlNode *node = [[ASControlNode alloc] init];
[node addTarget:nil action:ACTION_SENDER_EVENT forControlEvents:EVENT];
[node removeTarget:nil action:ACTION_SENDER_EVENT forControlEvents:EVENT];
[controller.view addSubview:node.view];
[node sendActionsForControlEvents:EVENT withEvent:nil];
XCTAssertEqual(controller.hits, 0, @"Controller did not receive exactly zero action events");
}
- (void)testRemoveWithTarget {
ASActionSenderEventController *controller = [[ASActionSenderEventController alloc] init];
ASControlNode *node = [[ASControlNode alloc] init];
[node addTarget:controller action:ACTION_SENDER_EVENT forControlEvents:EVENT];
[node removeTarget:controller action:ACTION_SENDER_EVENT forControlEvents:EVENT];
[controller.view addSubview:node.view];
[node sendActionsForControlEvents:EVENT withEvent:nil];
XCTAssertEqual(controller.hits, 0, @"Controller did not receive exactly zero action events");
}
- (void)testRemoveWithTargetRemovesAction {
ASActionSenderEventController *controller = [[ASActionSenderEventController alloc] init];
ASControlNode *node = [[ASControlNode alloc] init];
[node addTarget:controller action:ACTION_SENDER_EVENT forControlEvents:EVENT];
[node removeTarget:controller action:ACTION_SENDER_EVENT forControlEvents:EVENT];
[controller.view addSubview:node.view];
[node sendActionsForControlEvents:EVENT withEvent:nil];
XCTAssertEqual(controller.hits, 0, @"Controller did not receive exactly zero action events");
}
- (void)testRemoveWithoutTargetRemovesTargetedAction {
ASActionSenderEventController *controller = [[ASActionSenderEventController alloc] init];
ASControlNode *node = [[ASControlNode alloc] init];
[node addTarget:controller action:ACTION_SENDER_EVENT forControlEvents:EVENT];
[node removeTarget:nil action:ACTION_SENDER_EVENT forControlEvents:EVENT];
[controller.view addSubview:node.view];
[node sendActionsForControlEvents:EVENT withEvent:nil];
XCTAssertEqual(controller.hits, 0, @"Controller did not receive exactly zero action events");
}
- (void)testDuplicateEntriesWithoutTarget {
ASActionSenderEventController *controller = [[ASActionSenderEventController alloc] init];
ASControlNode *node = [[ASControlNode alloc] init];
[node addTarget:nil action:ACTION_SENDER_EVENT forControlEvents:EVENT];
[node addTarget:nil action:ACTION_SENDER_EVENT forControlEvents:EVENT];
[controller.view addSubview:node.view];
[node sendActionsForControlEvents:EVENT withEvent:nil];
XCTAssertEqual(controller.hits, 1, @"Controller did not receive exactly one action event");
}
- (void)testDuplicateEntriesWithTarget {
ASActionSenderEventController *controller = [[ASActionSenderEventController alloc] init];
ASControlNode *node = [[ASControlNode alloc] init];
[node addTarget:controller action:ACTION_SENDER_EVENT forControlEvents:EVENT];
[node addTarget:controller action:ACTION_SENDER_EVENT forControlEvents:EVENT];
[controller.view addSubview:node.view];
[node sendActionsForControlEvents:EVENT withEvent:nil];
XCTAssertEqual(controller.hits, 1, @"Controller did not receive exactly one action event");
}
- (void)testDuplicateEntriesWithAndWithoutTarget {
ASActionSenderEventController *controller = [[ASActionSenderEventController alloc] init];
ASControlNode *node = [[ASControlNode alloc] init];
[node addTarget:controller action:ACTION_SENDER_EVENT forControlEvents:EVENT];
[node addTarget:nil action:ACTION_SENDER_EVENT forControlEvents:EVENT];
[controller.view addSubview:node.view];
[node sendActionsForControlEvents:EVENT withEvent:nil];
XCTAssertEqual(controller.hits, 2, @"Controller did not receive exactly two action events");
}
- (void)testDeeperHierarchyWithoutTarget {
ASActionController *controller = [[ASActionController alloc] init];
UIView *view = [[UIView alloc] init];