搜索结果为空时 UITableView 中的 UISearchController 崩溃

Posted

技术标签:

【中文标题】搜索结果为空时 UITableView 中的 UISearchController 崩溃【英文标题】:UISearchController in UITableView crashes when search results are empty 【发布时间】:2016-10-04 17:40:13 【问题描述】:

我在 UITableViewController 中有一个 UISearchController。每次显示表格视图时,我想用最后搜索的文本填充搜索栏。为此,我在 viewDidAppear 中添加了代码来检索最后一个搜索字符串并将 UISearchController 设置为活动状态。

只要我不输入任何搜索文本,一切正常。

输入搜索文本后,第一次搜索正常。但是,当我重新打开此视图并使用最后一个搜索词填充 UISearchController 时,如果新列表现在返回空结果(这可能是因为我在 tableview 中的列表是动态的),那么应用程序在 cellForRowAtIndexPath 中崩溃并尝试将一行访问到一个空的搜索结果数组中。

我在很多地方都放了NSLogs,发现了一个奇怪的东西。即使 UISearchController 处于活动状态,numberOfRowsInSection 中的相应条件也会返回 FALSE,从而返回原始项目列表中的项目数,而不是搜索到的项目列表(现在为 0)。

它调用 numOfRowsInSection,当搜索控制器处于活动状态时,而不是进入该函数的“if ([_searchController isActive])”子句,它错误地进入“else”子句,获取原始列表的计数(未搜索),即 0,然后调用 cellForRowAtIndexPath 且 indexPath.row = 0。奇怪的是,在 cellForRowAtIndexPath 中,它确实正确在“if (_searchController.active)”子句中.显然有一些竞争条件或者我遗漏了一些东西。

我很困惑为什么会这样。将不胜感激。

@interface TaalListViewController ()

@property (strong, nonatomic) NSArray *m_searchedItemList;
@property NSArray *m_itemList;
@property (retain, nonatomic) UISearchController *searchController;

@end

@implementation TaalListViewController

- (id)initWithStyle:(UITableViewStyle)style

    self = [super initWithStyle:style];
    if (self) 
        // Custom initialization
    
    return self;


- (void)viewDidLoad

    @try 
        [super viewDidLoad];

        // Uncomment the following line to preserve selection between presentations.
        // self.clearsSelectionOnViewWillAppear = NO;

        // This is a global function declared and returns the items list. This works fine.
        _m_itemList = [Globals getItemsList];

        // Search controller
        _m_searchedItemList = [_m_itemList mutableCopy];

        self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
        self.searchController.searchBar.delegate = self;
        self.searchController.searchResultsUpdater = self;
        [self.searchController.searchBar sizeToFit];
        self.searchController.dimsBackgroundDuringPresentation = NO;
        self.definesPresentationContext = YES;
        self.tableView.tableHeaderView = self.searchController.searchBar;
    
    @catch (NSException *exception) 
        NSLog(@"Caught exception in viewDidLoad");
        NSLog(@"CRASH: %@", exception);
        NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
    



- (void) viewDidAppear:(BOOL)animated

    [super viewDidAppear:animated];

    // Get the last searched text and automatically set it in the search bar
    NSString *str = [Globals getLastSearchedText];
    if ((str != nil) && (!(str.length == 0)))
    
        NSLog(@"Setting seach bar active automatically");
        self.searchController.searchBar.text = str;
        self.searchController.active = YES;
        // [self.searchController.searchBar becomeFirstResponder];
    


#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

    // Return the number of sections.
    return 1;


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

    @try 
        // Return the number of rows in the section.
        if ([self.searchController isActive])
        
            NSLog(@"Number of searched item rows = %lu", (unsigned long)_m_searchedItemList.count);
            return _m_searchedItemList.count;
        
        else
        
            NSLog(@"Number of regular rows = %lu", (unsigned long)_m_itemList.count);
            return _m_itemList.count;
        
    
    @catch (NSException *exception) 
        NSLog(@"Caught exception in numberOfRowsInSection");
        NSLog(@"CRASH: %@", exception);
        NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
    


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    @try 
        static NSString *CellIdentifier = @"ListTableCell";

        ListTableViewCell *cell = (ListTableViewCell *) [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

        // Configure the cell...
        if (cell == nil)
        
            cell = [[ListTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        

        DisplayListItem *item = nil;

        if (self.searchController.active)
        
            NSLog(@"Row %ld of search list", (long)indexPath.row);
            item = (DisplayListItem *) [_m_searchedItemList objectAtIndex:indexPath.row];
        
        else
        
            NSLog(@"Row %ld of regular list", (long)indexPath.row);
            item = (DisplayListItem *) [_m_itemList objectAtIndex:indexPath.row];
        

        // Set the cell here and return
        // ...
        // Omitted code
        // ...

        return cell;
    
    @catch (NSException *exception) 
        NSLog(@"Caught exception in cellForRowAtIndexPath");
        NSLog(@"CRASH: %@", exception);
        NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
    


#pragma mark - UISearchResultsUpdating

-(void)updateSearchResultsForSearchController:(UISearchController *)searchController 

    NSString *searchString = [self.searchController.searchBar text];

    NSLog(@"updateSearchResultsForSearchController called, searchString = %@", searchString);

    _m_searchedItemList = [_m_itemList mutableCopy];

    if (searchString.length != 0)
    
        [self filterContentForSearchText:searchString];
    

    [self.tableView reloadData];


#pragma mark - UISearchBarDelegate

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar 
    NSLog(@"searchBarSearchButtonClicked called");
    [searchBar resignFirstResponder];


- (void)filterContentForSearchText:(NSString*)searchText

    NSLog(@"filterContentForSearchText called");
    @try 

        NSMutableArray *subPredicates = [NSMutableArray arrayWithCapacity:searchText.count];
        for (NSString *searchStr in searchText)
        
            [subPredicates addObject:[NSPredicate predicateWithFormat:@"m_name contains[c] %@", searchStr]];
        
        NSPredicate *resultPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
        _m_searchedItemList = [_m_itemList filteredArrayUsingPredicate:resultPredicate];

        [Globals setLastSearchedText:searchText];

        NSLog(@"filterContentForSearchText finished, searched count = %lu", _m_searchedItemList.count);
    
    @catch (NSException *exception) 
        NSLog(@"Caught exception in filterContentForSearchText");
        NSLog(@"CRASH: %@", exception);
        NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
    


- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText

    [Globals setLastSearchedText:searchText];

@end

这是调试器中 NSLogs 的输出。

2016-10-04 22:45:46.364927 AppName[1387:639346] Setting seach bar active automatically
2016-10-04 22:45:46.365378 AppName[1387:639346] updateSearchResultsForSearchController called, searchString = Kay
2016-10-04 22:45:46.365470 AppName[1387:639346] filterContentForSearchText called
2016-10-04 22:45:46.366522 AppName[1387:639346] filterContentForSearchText finished, searched count = 0
2016-10-04 22:45:46.366825 AppName[1387:639346] Number of regular rows = 215
2016-10-04 22:45:46.367319 AppName[1387:639346] updateSearchResultsForSearchController called, searchString = Kay
2016-10-04 22:45:46.367407 AppName[1387:639346] filterContentForSearchText called
2016-10-04 22:45:46.368190 AppName[1387:639346] filterContentForSearchText finished, searched count = 0
2016-10-04 22:45:46.368264 AppName[1387:639346] Number of regular rows = 215
2016-10-04 22:45:46.371081 AppName[1387:639346] Row 0 of search list
2016-10-04 22:45:46.373274 AppName[1387:639346] Caught exception in cellForRowAtIndexPath
2016-10-04 22:45:46.373383 AppName[1387:639346] CRASH: *** -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray
2016-10-04 22:45:46.382029 AppName[1387:639346] Stack Trace: (
    0   CoreFoundation                      0x0000000192d981d8 <redacted> + 148
    1   libobjc.A.dylib                     0x00000001917d055c objc_exception_throw + 56
    2   CoreFoundation                      0x0000000192d033dc <redacted> + 0
    3   AppName                             0x00000001001269bc -[ListViewController tableView:cellForRowAtIndexPath:] + 512
    4   UIKit                               0x0000000198f243d4 <redacted> + 716
    5   UIKit                               0x0000000198f24604 <redacted> + 80
    6   UIKit                               0x0000000198f11bac <redacted> + 2304
    7   UIKit                               0x0000000198f29668 <redacted> + 116
    8   UIKit                               0x0000000198cc5b14 <redacted> + 176
    9   UIKit                               0x0000000198bde54c <redacted> + 1196
    10  QuartzCore                          0x00000001960a640c <redacted> + 148
    11  QuartzCore                          0x000000019609b0e8 <redacted> + 292
    12  QuartzCore
2016-10-04 22:45:46.382306 AppName[1387:639346] -[_UITableViewReorderingSupport _needsSetup]: unrecognized selector sent to instance 0x170290e00
2016-10-04 22:45:46.382714 AppName[1387:639346] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_UITableViewReorderingSupport _needsSetup]: unrecognized selector sent to instance 0x170290e00'
*** First throw call stack:
(0x192d981c0 0x1917d055c 0x192d9f278 0x192d9c278 0x192c9659c 0x198d22c6c 0x198f24440 0x198f24604 0x198f11bac 0x198f29668 0x198cc5b14 0x198bde54c 0x1960a640c 0x19609b0e8 0x19609afa8 0x196017c64 0x19603f0d0 0x19603faf0 0x192d457dc 0x192d4340c 0x192d4389c 0x192c72048 0x1946f5198 0x198c4c628 0x198c47360 0x10015aaa8 0x191c545b8)
libc++abi.dylib: terminating with uncaught exception of type NSException

【问题讨论】:

使用前初始化数组。 我已经初始化了数组: // 这是一个声明的全局函数并返回项目列表。这工作正常。 _m_itemList = [全局 getItemsList]; // 搜索控制器 _m_searchedItemList = [_m_itemList mutableCopy];此代码已在 viewDidLoad 中初始化。 看到这种行为,在滚动时离开搜索模式......在 numberOfRows 中,searchController isActive 是 YES,当执行 cellForRowAtIndexPath 时(因为滚动),它是 NO...... 【参考方案1】:
2016-10-04 22:45:46.373274 AppName[1387:639346] Caught exception in cellForRowAtIndexPath
2016-10-04 22:45:46.373383 AppName[1387:639346] CRASH: *** -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray

在那里说!在cellForRowAtIndexPath 中,您尝试访问空NSArray 的元素0

不要这样做,它不会崩溃!

【讨论】:

我明白这一点。问题是 - 它调用 numOfRowsInSection,而不是进入该函数的“if ([_searchController isActive])”子句,而是进入“else”子句,获取原始列表的计数(未搜索),然后调用具有错误 indexPath 的 cellForRowAtIndexPath。更奇怪的是,在 cellForRowAtIndexPath 中,它确实正确地进入了“if (_searchController.active)”子句。显然有一些比赛条件或者我遗漏了一些东西。

以上是关于搜索结果为空时 UITableView 中的 UISearchController 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

字段为空时为 UISearchBar 启用“搜索”按钮? (IOS 7)

点击空的 UITableView 退出搜索栏

当搜索字符串为空时,带有连接的搜索查询显示所有行

当过滤器为空时,它返回空数组

结果为空时LINQ返回啥

仅当查询不为空时,才从查询写入 BigQuery 中的表