“EXC_BAD_ACCESS”,“ - [CFString retain]:在表格中滚动时发送到解除分配实例的消息”

时间:2010-12-10 18:31:18

标签: iphone xcode uitableview exc-bad-access

应用说明:我是新的iPhone开发人员。我正在使用一个应用程序,它使用两个表视图,然后是详细视图。第一级表视图显示几个类别,当选择类别时,将显示与该类别相关的主题列表。选择主题后,将显示与该主题相关的详细视图。

当我在模拟器中运行应用程序时,一切似乎都运行良好(但只是简单!):

  • 第一级表视图:这个类别的视图加载正常并且没有任何问题。
  • 主题列表视图:此视图似乎也可以正常工作,但只是短暂的。它加载了所有正确的主题,我甚至可以在整个主题列表中快速向上和向下滚动 - 但这只能在滚动时运行几秒钟(之后应用程序崩溃)。我还可以选择任何主题并成功加载详细视图。
  • 详细信息视图:工作正常,没问题。

我正在为应用程序使用两个nib:MainWindow.xib显示First Level和Topic View表。 TopicDetail.xib用于显示主题的详细信息。

DATA:第一级视图从包含字符串数组的plist获取数据。主题列表视图从词典列表中获取其数据。每个字典由五个字符串组成。其中一个字符串包含与主题相关的类别的“标签”。选择“第一级视图”中的类别时,“主题视图”将选择与所选类别匹配的“标签”主题,并将其显示在“主题视图表”上。

问题:我的问题在于第二个“主题视图”。我可以通过几种方式导致崩溃:

  • 如果我快速向上和向下滚动主题列表,几秒钟内一切正常,则应用程序崩溃。

  • 如果我从屏幕上滚动一个单元格,我可以将同一单元格滚动回屏幕而不会出现问题。如果我重复此操作(第二次从屏幕上滚动相同的单元格然后尝试将其滚动回屏幕),当将单元格滚动回屏幕时应用程序崩溃。

错误消息:返回的初始错误消息为 EXC_BAD_ACCESS 。当我启用NSZombieEnabled时,我收到错误消息“ - [CFString retain]:消息发送到解除分配的实例

基于这些错误消息和我的研究,我相信我有一个内存分配问题,在已经发布的对象上调用“retain”,但我无法弄清楚这是怎么发生的以及如何解决它!任何帮助将不胜感激 - 谢谢你提前!!

CODE:

第一级视图Controller.h:

#import 
@class TopicListController;

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

First Level View 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 个答案:

答案 0 :(得分:10)

您的问题出在tableView:cellForRowAtIndexPath:。您不应该发布以下内容:

[rowString release];
[rowDetailString release];

当你得到如下字符串时:

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

它们作为自动释放的对象返回。你应该只发布你拥有的东西,即你从一个以new开头的方法中分配,保留或获得的东西。一篇解释这一点的好文章是objective-c memory management for lazy people。但引用重要部分:

  
      
  • 如果你分配它,你就拥有它。
  •   
  • 如果你复制它就拥有它。
  •   
  • 如果你是新手,你就拥有它。 (New只是alloc / init的快捷方式)。
  •