【问题标题】:Why does my app crash when UISearchBar's textDidChange is called?为什么调用 UISearchBar 的 textDidChange 时我的应用会崩溃?
【发布时间】:2021-09-23 06:01:27
【问题描述】:

我有一个 UITableView,它显示了音乐库中的所有歌曲,效果很好。但是,当我开始在搜索栏中输入以缩小搜索结果时,应用程序立即崩溃。就像我一按键盘上的一个字母,它就会崩溃。我尝试过修改我的textDidChange: 方法,但无论如何它总是崩溃。我不确定我做错了什么,有人可以帮忙吗?谢谢。

标题:


@interface PTTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate>
@property (strong,nonatomic)UITableView* tableView;
@property (strong,nonatomic)UISearchBar* searchBar;
@end

ViewController.m:


#import "PTTableViewController.h"

@implementation PTTableViewController

MPMediaQuery *songsQuery;
NSArray *songsArray;
NSMutableArray *filteredArray;
NSMutableArray *songTitlesArray;

-(void)viewDidLoad{
    [super viewDidLoad];
    
    self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0,0,[UIScreen mainScreen].bounds.size.width - 75,150) style:UITableViewStylePlain];
    self.tableView.dataSource = self;
    self.tableView.delegate = self;
    [self.view addSubview:_tableView];
    
    self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0,0,320,44)];
    self.searchBar.delegate = self;
    self.searchBar.placeholder = @"Search";
    self.tableView.tableHeaderView = self.searchBar;
    
    songsQuery = [MPMediaQuery songsQuery];
    songsArray = [songsQuery items];
    
    songTitlesArray = [[NSMutableArray alloc] init];
    for (MPMediaItem *item in songsArray) {
        [songTitlesArray addObject:[item valueForProperty:MPMediaItemPropertyTitle]];
    }
    filteredArray = [[NSMutableArray alloc] init];
    filteredArray = [songTitlesArray copy];

}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return filteredArray.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (cell == nil){
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }

    cell.textLabel.text = [filteredArray objectAtIndex:indexPath.row];

    return cell;
}

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
    NSLog(@"search changed");
    [self performSelectorInBackground:@selector(helper) withObject:nil];

}

-(void)helper{
    [filteredArray removeAllObjects];
    
    if ([self.searchBar.text isEqualToString:@""]){
        filteredArray = [songTitlesArray copy];
    } else {
        for (NSString *object in songTitlesArray){
            if ([object rangeOfString:self.searchBar.text].location == NSNotFound){
                NSLog(@"string not found");
            } else {
                NSLog(@"string found");
                [filteredArray addObject:object];
            }
        }
    } [self.tableView reloadData];
}
@end

【问题讨论】:

  • 那是因为你要从filteredArray中删除所有元素而不更新表格视图?
  • 什么是崩溃信息?
  • 我实际上是在越狱设备上的 SpringBoard 中运行这段代码,所以当“应用程序”崩溃时,我的意思是设备重新启动/软重启?奇怪的是,没有生成崩溃报告。
  • 你在后台调用助手,所以[self.tableView reloadData]也在后台调用。 UI 中的主线程应该如何处理这个问题?首先要尝试的是,将 reloadData 包装在 GCD 块中,并改为寻址主线程以进行重新加载。因为可能有一个没有条目的表,这不是一个错误,这是一个功能,但当你的线程搞砸时仍然毫无意义。
  • 它仍然在主线程上崩溃。我让它在后台运行的原因是因为如果在主线程上调用键盘也会滞后。

标签: ios objective-c uitableview crash uisearchbar


【解决方案1】:

首先...

你会因为你这样做而崩溃:

filteredArray = [songTitlesArray copy];

songTitlesArray非可变副本分配给filteredArray

当你尝试这样做时:

[filteredArray removeAllObjects];

你正试图改变一个不可变的对象。

您可以通过多种方式解决此(第一个)问题...

一种方法是将copy 的所有实例更改为mutableCopy

filteredArray = [songTitlesArray mutableCopy];

但是,使用您的确切代码,您将引入新的崩溃,因为您将在后台线程上执行 UI 操作。

下一步...您确定遇到“键盘延迟”吗?

您可能想要更改一些内容:

  • 使_filteredArray 不可变
  • 无需调用removeAllObjects,只需重新创建数组
  • 使用filteredArrayUsingPredicate 而不是循环遍历和比较每个字符串

试试这个 - 因为我没有你的 MPMediaQuery 我会生成 500 个字符串作为“歌曲标题”:

@interface PTTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate>
@property (strong,nonatomic)UITableView* tableView;
@property (strong,nonatomic)UISearchBar* searchBar;
@end

@implementation PTTableViewController

//MPMediaQuery *songsQuery;
NSArray *songsArray;
NSArray *filteredArray;
NSMutableArray *songTitlesArray;

-(void)viewDidLoad{
    [super viewDidLoad];
    
    self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0,0,[UIScreen mainScreen].bounds.size.width - 75,150) style:UITableViewStylePlain];
    self.tableView.dataSource = self;
    self.tableView.delegate = self;
    [self.view addSubview:_tableView];
    
    self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0,0,320,44)];
    self.searchBar.delegate = self;
    self.searchBar.placeholder = @"Search";
    self.tableView.tableHeaderView = self.searchBar;
    
    //  songsQuery = [MPMediaQuery songsQuery];
    //  songsArray = [songsQuery items];
    //
    //  songTitlesArray = [[NSMutableArray alloc] init];
    //  for (MPMediaItem *item in songsArray) {
    //      [songTitlesArray addObject:[item valueForProperty:MPMediaItemPropertyTitle]];
    //  }
    //  filteredArray = [[NSMutableArray alloc] init];
    //  filteredArray = [songTitlesArray copy];

    // I don't have your Query, so let's create a 500 element string array,
    //  using the row number + one of the following words in each entry

    NSString *samples = @"These are some random words for the song titles.";

    // So, we'll have:

    //  "Row: 0 These"
    //  "Row: 1 are"
    //  "Row: 2 some"
    //  "Row: 3 random"
    //  "Row: 4 words"
    //  "Row: 5 for"
    //  "Row: 6 the"
    //  "Row: 7 song"
    //  "Row: 8 titles."
    //  "Row: 9 These"
    //  "Row: 10 are"
    //  "Row: 11 some"
    //  "Row: 12 random"
    //  "Row: 13 words"
    //  "Row: 14 for"
    //  ... etc
    
    NSArray *a = [samples componentsSeparatedByString:@" "];
    
    songTitlesArray = [[NSMutableArray alloc] init];
    
    for (int i = 0; i < 500; i++) {
        NSString *w = a[i % [a count]];
        NSString *s = [NSString stringWithFormat:@"Row: %d %@", i, w];
        [songTitlesArray addObject:s];
    }
    
    filteredArray = [songTitlesArray copy];
    
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return filteredArray.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (cell == nil){
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }
    
    cell.textLabel.text = [filteredArray objectAtIndex:indexPath.row];
    
    return cell;
}

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
    [self helper];
}

-(void)helper{
    if ([self.searchBar.text isEqualToString:@""]){
        filteredArray = [songTitlesArray copy];
    } else {
        NSString *w = [NSString stringWithFormat:@"SELF contains[c] '%@'", self.searchBar.text];
        NSPredicate *sPredicate = [NSPredicate predicateWithFormat:w];
        filteredArray = [songTitlesArray filteredArrayUsingPredicate:sPredicate];
    }
    [self.tableView reloadData];
}
@end

附带说明一下,您已经很好地展示了尝试在越狱设备上开发的(许多)原​​因之一.

【讨论】:

  • 谢谢,将“copy”更改为“mutableCopy”修复了崩溃问题!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多