«EXC_BAD_ACCESS», «- [CFString retain]: сообщение отправлено освобожденному экземпляру» при прокрутке в таблице - PullRequest
3 голосов
/ 10 декабря 2010

ОПИСАНИЕ ПРИЛОЖЕНИЯ : Я новый разработчик для iPhone.Я работаю над приложением, которое использовало два табличных представления, а затем подробное представление.В табличном представлении первого уровня представлено несколько категорий, при выборе категории отображается список тем, связанных с этой категорией.Когда тема выбрана, отображается подробный вид, связанный с этой темой.

Когда я запускаю приложение в симуляторе, кажется, что все работает нормально (но ненадолго!):

  • Представление таблицы первого уровня: это представление с помощьюкатегории загружаются нормально и работают без проблем.
  • Представление списка тем: Это представление также работает нормально, но ненадолго.Он загружает все правильные темы, и я даже могу быстро прокрутить весь список тем вверх и вниз - но это работает только в течение нескольких секунд во время прокрутки (после чего происходит сбой приложения).Я также могу выбрать любую из тем, и подробный вид загружается успешно.
  • Детальный просмотр: работает нормально, без проблем.

Я использую два кончика для приложения: MainWindow.xib отображает таблицы первого уровня и представления тем,TopicDetail.xib используется для отображения подробной информации по теме.

ДАННЫЕ: Представление первого уровня получает свои данные из plist, который состоит из массива строк.Представление списка тем получает свои данные из списка словарей.Каждый словарь состоит из пяти строк.Одна из этих строк содержит «Теги» категорий, которые относятся к теме.При выборе категории в представлении первого уровня в представлении тем выбираются темы с тегами, которые соответствуют выбранной категории, и отображаются в таблице представлений тем.

ПРОБЛЕМА: Моя проблема со вторым «Просмотр темы».Несколько способов, которыми я смог вызвать сбой:

  • Если я быстро прокручиваю список тем вверх и вниз, в течение пары секунд все работает нормально, тосбой приложения.

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

СООБЩЕНИЯ ОБ ОШИБКАХ: Исходное сообщение об ошибке, которое было возвращено, было EXC_BAD_ACCESS .Когда я включил NSZombieEnabled, я получил сообщение об ошибке " - [CFString retain]: сообщение отправлено на освобожденный экземпляр "

На основании этих сообщений об ошибках и моих исследований я считаю, что у меня есть выделение памятипроблема, когда вызывается «retain» для объекта, который был освобожден, но я не могу понять, где это происходит и как его решить!Любая помощь будет принята с благодарностью - заранее спасибо !!

КОД:

Первый уровень View Controller.h:

#import 
@class TopicListController;

@interface FirstLevelViewController : UITableViewController {
    NSArray *controllers;
    TopicListController *childController;
}
@property (nonatomic, retain) NSArray *controllers;
@end

ПервыйУровень просмотра Controller.m:


#import "FirstLevelViewController.h"
#import "TopicListController.h"
#import "TopicAppDelegate.h"

@implementation FirstLevelViewController
@synthesize controllers;

- (void)viewDidLoad
{
    self.title = @"Categories"; 
    NSString *path = [[NSBundle mainBundle] pathForResource:@"TopicCategoryList" ofType:@"plist"];  
    NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:path];   
    self.controllers = array;
    [array release];
    [super viewDidLoad];
}

- (void)viewDidUnload
{
    self.controllers = nil;
    [childController release];
    childController = nil;
    [super viewDidUnload];
}

- (void) dealloc
{
    [controllers release];
    [childController release];
    [super dealloc];
}

#pragma mark -
#pragma mark Table Data Source Methods

- (NSInteger)tableView:(UITableView *)tableview numberOfRowsInSection:(NSInteger)section
{
    return [self.controllers count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *FirstLevelCell = @"FirstLevelCell";    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:FirstLevelCell];

    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:FirstLevelCell] autorelease];
    }
    NSUInteger row = [indexPath row];
    cell.textLabel.text = [controllers objectAtIndex:row];
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    return cell;
}

#pragma mark -
#pragma mark Table View Delegate Methods

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (childController == nil) {
        childController = [[TopicListController alloc] init];
    }

    childController.title = @"Topics";
    NSUInteger row = [indexPath row];

    NSString *selectedCategory = [self.controllers objectAtIndex:row];
    childController.selectedCategory = selectedCategory;
    [self.navigationController pushViewController:childController animated:YES];
}                            

@end

Список тем Controller.h:

#import 
@class TopicDetailController;

@interface TopicListController : UITableViewController {

    NSArray *list;
    NSString *selectedCategory;
    TopicDetailController *childController;

}

@property (nonatomic, retain) NSArray *list;
@property (nonatomic, retain) NSString *selectedCategory;

@end

Список тем Controller.m

#import "TopicListController.h"
#import "TopicAppDelegate.h"
#import "TopicDetailController.h"
#import "NSArray-MutableDeepCopy.h"
#import "TopicConstants.h"

@implementation TopicListController

@synthesize list;
@synthesize selectedCategory;

- (void)viewWillAppear:(BOOL)animated
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"TopicContent" ofType:@"plist"];

    NSMutableArray *fullArray = [[NSMutableArray alloc] initWithContentsOfFile:path];   
    NSMutableArray *fullArrayCopy = [fullArray mutableDeepCopy];

    NSUInteger items = [fullArrayCopy count];
    NSUInteger item=0;

        for (item; item /*less than*/ items; item++) {
            if ([[[fullArrayCopy objectAtIndex:item] objectForKey:CATEGORIES_KEY] 
             rangeOfString:selectedCategory 
             options:NSCaseInsensitiveSearch].location == NSNotFound) 
        {
            [fullArrayCopy removeObjectAtIndex:item];
            item--;
            items--;
        }
    }

    self.list = fullArrayCopy;
    [fullArray release];
    [self.tableView reloadData];
    [super viewWillAppear:animated];
}

- (void)viewDidUnload
{
    self.list = nil;
    self.selectedCategory = nil;
    [childController release];
    childController = nil;
}

- (void)dealloc
{
    [list release];
    [selectedCategory release];
    [childController release];
    [super dealloc];
}

#pragma mark -
#pragma mark Table Data Source Methods

- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger) section
{
    return [list count];
}

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

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:DisclosureButtonCellIdentifier];

    if (cell==nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:DisclosureButtonCellIdentifier]
                autorelease];
    }

    NSUInteger row = [indexPath row];
    NSString *rowString = [[list objectAtIndex:row] objectForKey:TITLE_KEY];
    NSString *rowDetailString = [[list objectAtIndex:row] objectForKey:SUBTITLE_KEY];
    cell.textLabel.text = rowString;
    cell.detailTextLabel.text = rowDetailString;
    cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
    [rowString release];
    [rowDetailString release];
    return cell;
}

#pragma mark -
#pragma mark Table Delegate Methods

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (childController == nil) {
        childController = [[TopicDetailController alloc] initWithNibName:@"TopicDetail" bundle:nil];
    }


    NSUInteger row = [indexPath row];

    NSString *selectedTopicInformation = [[list objectAtIndex:row] objectForKey:INFORMATION_KEY];
    NSString *selectedTopicTitle = [[list objectAtIndex:row] objectForKey:TITLE_KEY];
    NSString *selectedTopicSubtitle = [[list objectAtIndex:row] objectForKey:SUBTITLE_KEY];
    NSString *selectedTopicTips = [[list objectAtIndex:row] objectForKey:TIPS_KEY];

    NSString *detailMessageInformation  = [[NSString alloc] initWithFormat:@"%@.", selectedTopicInformation];
    NSString *detailMessageTitle = [[NSString alloc] initWithFormat:@"%@", selectedTopicTitle];
    NSString *detailMessageSubtitle = [[NSString alloc] initWithFormat:@"%@", selectedTopicSubtitle];
    NSString *detailMessageTips = [[NSString alloc] initWithFormat:@"%@", selectedTopicTips];

    childController.messageInformation = detailMessageInformation;
    childController.messageTitle = detailMessageTitle;
    childController.messageSubTitle = detailMessageSubtitle;
    childController.messageTips = detailMessageTips;

    childController.title = @"Topic Detail";

    [detailMessageInformation release];
    [detailMessageTitle release];
    [detailMessageSubtitle release];
    [detailMessageTips release];
    [self.navigationController pushViewController:childController animated:YES];


}

- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
    if (childController == nil) {
        childController = [[TopicDetailController alloc] initWithNibName:@"TopicDetail" bundle:nil];
    }

    NSUInteger row = [indexPath row];

    NSString *selectedTopicInformation = [[list objectAtIndex:row] objectForKey:INFORMATION_KEY];
    NSString *selectedTopicTitle = [[list objectAtIndex:row] objectForKey:TITLE_KEY];
    NSString *selectedTopicSubtitle = [[list objectAtIndex:row] objectForKey:SUBTITLE_KEY];
    NSString *selectedTopicTips = [[list objectAtIndex:row] objectForKey:TIPS_KEY];

    NSString *detailMessageInformation  = [[NSString alloc] initWithFormat:@"%@.", selectedTopicInformation];
    NSString *detailMessageTitle = [[NSString alloc] initWithFormat:@"%@", selectedTopicTitle];
    NSString *detailMessageSubtitle = [[NSString alloc] initWithFormat:@"%@", selectedTopicSubtitle];
    NSString *detailMessageTips = [[NSString alloc] initWithFormat:@"%@", selectedTopicTips];

    childController.messageInformation = detailMessageInformation;
    childController.messageTitle = detailMessageTitle;
    childController.messageSubTitle = detailMessageSubtitle;
    childController.messageTips = detailMessageTips;

    childController.title = @"Topic Detail";

    [detailMessageInformation release];
    [detailMessageTitle release];
    [detailMessageSubtitle release];
    [detailMessageTips release];
    [self.navigationController pushViewController:childController animated:YES];
}

@end

1 Ответ

10 голосов
/ 10 декабря 2010

Ваша проблема в tableView:cellForRowAtIndexPath:.Вы не должны выпускать следующее:

[rowString release];
[rowDetailString release];

, когда вы получаете строку типа:

NSString *rowString = [[list objectAtIndex:row] objectForKey:TITLE_KEY];
NSString *rowDetailString = [[list objectAtIndex:row] objectForKey:SUBTITLE_KEY];

Они возвращаются как автоматически выпущенные объекты.Вы должны когда-либо выпускать только то, что у вас есть, то есть то, что вы выделили, сохранили или получили методом, начинающимся с new.Хорошая статья, объясняющая это, - управление памятью target-c для ленивых .Но процитируем важную часть:

  • Вы владеете им, если выделяете его.
  • Вы владеете им, если копируете его.
  • Вы владеете имесли ты новый.(Новый просто ярлык для alloc / init).
...