Каковы накладные расходы на файл plist с массивом словарей? - PullRequest
0 голосов
/ 12 марта 2012

Я пытаюсь загрузить некоторые данные из файла plist. Он содержит NSArray NSDictionaries с очень небольшим количеством данных (очень короткая строка и число). Проблема в том, что в нем около 8000 записей. Размер самого файла plist составляет около 900 кБ при хранении в формате XML и около 350 кБ при хранении в виде двоичного файла, но, очевидно, когда он загружается в память, он увеличивается до 510 МБ, что, по-видимому, и происходит при использовании анализатора распределения. Это в 50-60 раз больше размера диска.

Сначала я подумал, что, должно быть, произошла какая-то ошибка, но потом я понял, что очень мало знаю о накладных расходах памяти в Какао. Тем не мение. Я загружаю список следующим образом:

NSMutableArray *data = [[NSMutableArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"localita" ofType:@"plist"]];

затем я назначаю его свойству (неатомному, сильному) этого пользовательского контроллера представления, которое у меня есть

[cbVC setData:data];
[cbVC setFilteredData:data];

Раньше я переопределял setData, но я переключился на этот способ, чтобы увидеть, если это было проблемой - это не так. Итак, это интерфейс:

@protocol CityBrowserDelegate <NSObject>

- (void)cityPicked:(NSString *)cityName;

@end

@interface CityBrowserViewController : UIViewController <UITableViewDelegate, UISearchBarDelegate, UISearchDisplayDelegate>

- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope;
- (IBAction)selectionFinished:(id)sender;

@property (nonatomic, strong) NSMutableArray *data;
@property (nonatomic, strong) NSMutableArray *filteredData;

@property (nonatomic, strong) IBOutlet UITableView *tableView;

@property (nonatomic, strong) IBOutlet id<MyPopoverControllerDelegate, CityBrowserDelegate> delegate;

@end

и это реализация (я вычеркнул не относящиеся к делу части)

@implementation CityBrowserViewController

@synthesize data = _data;
@synthesize filteredData = _filteredData;
@synthesize tableView = _tableView;
@synthesize delegate = _delegate;

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [self.filteredData count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.filteredData count] == 0 ? 1 : [self.filteredData count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellID = @"BasicCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    if(!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
    }
    if([self.filteredData count] == 0) {
        [cell.textLabel setText:@"Nessun risultato"];
    } else {
        [cell.textLabel setText:[[self.filteredData objectAtIndex:indexPath.row] valueForKey:@"Name"]];
    }
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *name = [[self.filteredData objectAtIndex:indexPath.row] valueForKey:@"Name"];
    [self.delegate cityPicked:name];
}

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
    [self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];

    // Return YES to cause the search result table view to be reloaded.
    return YES;
}

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
    [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];

    // Return YES to cause the search result table view to be reloaded.
    return YES;
}

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
    _filteredData = _data;
}

- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
    [_filteredData removeAllObjects]; // First clear the filtered array.

    // YES, I KNOW, but it seems to be quite fast nonetheless...
    NSDictionary *city;
    for (city in _data) {
        NSString *cityName = [city valueForKey:@"Name"];
        NSComparisonResult result = [cityName compare:searchText options:NSCaseInsensitiveSearch range:NSMakeRange(0, [searchText length])];
        if (result == NSOrderedSame) {
            [_filteredData addObject:city];
        }
    }
}

- (void)selectionFinished:(id)sender {
    [self.delegate dismissPopover];
}

@end

Теперь: есть какая-нибудь подсказка о том, почему при загрузке этого списка мне выделяется 510 МБ памяти? И, очевидно, он не освобождается, когда я отменяю поиск и восстанавливаю исходные данные в searchBarCancelButtonClicked

1 Ответ

0 голосов
/ 12 марта 2012

Проблема заключалась в том, что я возвращал N разделов и N строк на раздел, учитывая N размера набора данных. Очевидно, не самая лучшая идея. Набор данных составляет всего около 8000 записей, и он не изменится в ближайшее время, поэтому сейчас я оставлю код как есть (с очевидным исправлением). В отдельной ветке я экспериментирую с Core Data.

...