fix: Optimize class chain lookup - #1194
Conversation
There was a problem hiding this comment.
Pull request overview
This PR optimizes class chain lookup by avoiding repeated live XCUIElement resolution for intermediate class-chain segments, instead walking a single upfront snapshot in-memory and resolving live elements only for the final matches.
Changes:
- Replaces incremental
XCUIElementQuerybuilding with snapshot-based traversal for class chain segment evaluation. - Applies type/predicate filtering and position selection on snapshot candidates, then resolves final
XCUIElementinstances via existing snapshot→UID lookup. - Explicitly excludes the root snapshot from descendant matches to align behavior with
descendantsMatchingType:semantics.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
| if (item.predicates) { | ||
| for (FBAbstractPredicateItem *predicate in item.predicates) { | ||
| if ([predicate isKindOfClass:FBSelfPredicateItem.class]) { | ||
| query = [query matchingPredicate:predicate.value]; | ||
| } else if ([predicate isKindOfClass:FBDescendantPredicateItem.class]) { | ||
| query = [query containingPredicate:predicate.value]; | ||
| for (FBAbstractPredicateItem *predicateItem in item.predicates) { | ||
| if ([predicateItem isKindOfClass:FBSelfPredicateItem.class]) { | ||
| typeMatches = [[typeMatches filteredArrayUsingPredicate:predicateItem.value] mutableCopy]; | ||
| } else if ([predicateItem isKindOfClass:FBDescendantPredicateItem.class]) { |
There was a problem hiding this comment.
Good catch — fixed in 924a231 by de-duplicating typeMatches (via pointer identity) whenever more than one root is being walked, before predicates/position are applied.
| if (nil != chainItem.position) { | ||
| // It is necessary to resolve the query if intermediate element index is not zero or one, | ||
| // It is necessary to resolve the position if intermediate element index is not zero or one, | ||
| // because predicates don't support search by indexes |
There was a problem hiding this comment.
Fair point, the old wording didn't match reality (the parser never allows position 0, and this branch runs for any explicit position including 1). Reworded in 924a231 to explain why an indexed segment has to collapse to a single root immediately instead of staying folded into candidates.
De-duplicate overlapping-root matches before positional selection, and reword a stale comment about when intermediate positions get resolved. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Similarly to #1193 there is a major performance impact here when comparing to the current master. In the deep nested application I mentioned in #1193 an object lookup using the following selector: is significantly slower - lookup when built on master takes on an iPhone 12 Pro ~650ms on average, whereas with these changes lookup takes roughly twice that time (~1200ms on average), tested directly via POST commands to WDA. |
Summary