@@ -351,33 +351,41 @@ - (void)updateIndirectObjects {
351351 }
352352 QSAction *aObj = [aSelector objectValue ];
353353 id actionProvider = [aObj provider ];
354- NSArray *indirects = nil ;
355- if (actionProvider && [actionProvider respondsToSelector: @selector (validIndirectObjectsForAction:directObject: )]) {
356- QSObject *directObject = [[dSelector objectValue ] resolvedObject ];
357- indirects = [actionProvider validIndirectObjectsForAction: [aObj identifier ] directObject: directObject];
358- }
359- // If the validIndirectObjectsForAction... method hasn't been implemented, attempt to get valid indirects from the action's 'indirectTypes'
360- if (!indirects) {
361- if ([aObj indirectTypes ]) {
362- // Perform on background thread to not block main thread (avoids potential deadlock)
363- QSGCDAsync (^{
364- NSMutableArray *indirectsForAllTypes = [[NSMutableArray alloc ] initWithCapacity: 0 ];
365- [[aObj indirectTypes ] enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock: ^(NSString *eachType, NSUInteger idx, BOOL *stop) {
366- [indirectsForAllTypes addObjectsFromArray: [QSLib scoredArrayForType: eachType]];
367- }];
368- NSArray *finalIndirects = ([indirectsForAllTypes count ] > 0 ) ? [indirectsForAllTypes copy ] : nil ;
369-
370- // Update UI on main thread
371- QSGCDMainAsync (^{
372- [self updateControl: self ->iSelector withArray: finalIndirects];
373- [self ->iSelector setSearchMode: (finalIndirects ? SearchFilter : SearchFilterAll)];
374- });
375- });
376- return ;
354+
355+ // Capture UI state on the main thread before dispatching
356+ QSObject *directObject = [[dSelector objectValue ] resolvedObject ];
357+ NSString *actionIdentifier = [aObj identifier ];
358+ NSArray *indirectTypes = [aObj indirectTypes ];
359+ BOOL hasProvider = (actionProvider && [actionProvider respondsToSelector: @selector (validIndirectObjectsForAction:directObject: )]);
360+
361+ // Perform on background thread to avoid blocking main thread and prevent deadlock
362+ // when action providers use concurrent enumeration that may dispatch_sync back to main
363+ QSGCDAsync (^{
364+ NSArray *indirects = nil ;
365+
366+ if (hasProvider) {
367+ indirects = [actionProvider validIndirectObjectsForAction: actionIdentifier directObject: directObject];
377368 }
378- }
379- [self updateControl: iSelector withArray: indirects];
380- [iSelector setSearchMode: (indirects?SearchFilter: SearchFilterAll)];
369+
370+ // If the validIndirectObjectsForAction... method hasn't been implemented or returned nil,
371+ // attempt to get valid indirects from the action's 'indirectTypes'
372+ if (!indirects && indirectTypes) {
373+ NSMutableArray *indirectsForAllTypes = [[NSMutableArray alloc ] initWithCapacity: 0 ];
374+ [indirectTypes enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock: ^(NSString *eachType, NSUInteger idx, BOOL *stop) {
375+ [indirectsForAllTypes addObjectsFromArray: [QSLib scoredArrayForType: eachType]];
376+ }];
377+ if ([indirectsForAllTypes count ]) {
378+ indirects = [indirectsForAllTypes copy ];
379+ }
380+ }
381+
382+ NSArray *finalIndirects = indirects;
383+ // Update UI on main thread
384+ QSGCDMainAsync (^{
385+ [self updateControl: self ->iSelector withArray: finalIndirects];
386+ [self ->iSelector setSearchMode: (finalIndirects ? SearchFilter : SearchFilterAll)];
387+ });
388+ });
381389}
382390
383391- (void )updateViewLocations {
0 commit comments