Iphone EXC_错误访问-此代码如何导致泄漏?通过malloc_history&;NSzombied已启用

Iphone EXC_错误访问-此代码如何导致泄漏?通过malloc_history&;NSzombied已启用,iphone,cocoa-touch,memory-management,ios4,uiviewcontroller,Iphone,Cocoa Touch,Memory Management,Ios4,Uiviewcontroller,更新(在底部):添加的整个视图将显示:方法实现,以显示三个UIViewController如何分配/解除分配到UIScrollView中 我有一个根ViewController,其中包含一个UIScrollView。此UIScrollView本身包含3个不同的UIViewControllers(对于单独的UITableViews,设置为NSFetchedResultControllerDelegates) 当我们返回到视图时,我需要在某个点重新加载根ViewController的UIScroll

更新(在底部):添加的整个视图将显示:方法实现,以显示三个UIViewController如何分配/解除分配到UIScrollView中

我有一个根ViewController,其中包含一个
UIScrollView
。此
UIScrollView
本身包含3个不同的
UIViewController
s(对于单独的
UITableView
s,设置为
NSFetchedResultControllerDelegate
s)

当我们返回到视图时,我需要在某个点重新加载根ViewController的
UIScrollView
,因此我在以下步骤开始时执行此操作:

-(void) viewWillAppear:(BOOL)animated {
 [dataViewScroller.subviews 
                 makeObjectsPerformSelector:@selector(removeFromSuperview)];
 .......
 // and we proceed to re-load the 3 UIViewControllers into the UIScrollView.
 // From testing this all seems to work fine.
 // The **problem** is that that as soon as this method ends - I get an
 // EXC_BAD_ACCESS error described above.
 // If the above line is removed, everythign works fine.
}
以下是加载到
UIScrollView
中的每个
UIViewController的viewDidLoad方法,如下所示:

 - (void)viewDidLoad {
[super viewDidLoad];

self.view.backgroundColor = [UIColor clearColor];
self.expensesTableView.backgroundColor = [UIColor clearColor];

self.theTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
self.theTableView.rowHeight = 44;

UIView *containerView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 280, 44)] autorelease];
containerView.backgroundColor = [UIColor clearColor]; 

// Setting up and aligning the label in the center of our view.
UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 20)];   
headerLabel.text = NSLocalizedString(self.headerText, @"");
headerLabel.textColor = [UIColor blackColor];
headerLabel.font = [UIFont boldSystemFontOfSize:16];
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.center = containerView.center;
headerLabel.textAlignment = UITextAlignmentCenter;

[containerView addSubview:headerLabel];

// HOW IS THIS A LEAK? CAUSES EXC_BAD_ACCESS If Not commented out
//NSLog(@"Test - %@", headerLabel);
//[headerLabel release];

[self.view addSubview:containerView];

NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
    // Update to handle the error appropriately.
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    exit(-1);  // Fail
}
}

上面的代码注释掉了行
标题标签释放]
。有了这个,程序运行得很好!如果我启用了行
[headerLabel release]
,我在技术上应该启用该行,否则它就是泄漏,那么GDB会报告以下内容:

 MyAppTest[11110:207] *** -[CFString release]: message sent to deallocated instance 0x5d66b60
(gdb)

This is the output from malloc_history trace using the above memory address:

Identifier:      MyAppTest
Version:         ??? (???)
Code Type:       X86 (Native)
Parent Process:  gdb-i386-apple-darwin [10116]

Date/Time:       2011-03-15 03:14:47.855 -0400
OS Version:      Mac OS X 10.6.6 (10J567)
Report Version:  6

ALLOC 0x5996770-0x599678f [size=32]: thread_a037a540 |start | main | UIApplicationMain | -[UIApplication _run] | CFRunLoopRunInMode | CFRunLoopRunSpecific | __CFRunLoopRun | __CFRunLoopDoSource1 | __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ | PurpleEventCallback | _UIApplicationHandleEvent | -[UIApplication sendEvent:] | -[UIApplication handleEvent:withNewEvent:] | -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] | -[UIApplication _reportAppLaunchFinished] | CA::Transaction::commit() | CA::Context::commit_transaction(CA::Transaction*) | CALayerLayoutIfNeeded | -[CALayer layoutSublayers] | -[UILayoutContainerView layoutSubviews] | -[UINavigationController _startDeferredTransitionIfNeeded] | -[UINavigationController _startTransition:fromViewController:toViewController:] | -[AccountViewController viewWillAppear:] | -[NSManagedObjectContext executeFetchRequest:error:] | -[NSPersistentStoreCoordinator executeRequest:withContext:error:] | -[NSSQLCore executeRequest:withContext:error:] | -[NSSQLCore objectsForFetchRequest:inContext:] | -[NSSQLCore newRowsForFetchPlan:] | -[NSSQLCore _newRowsForFetchPlan:selectedBy:withArgument:] | -[NSSQLCore _prepareResultsFromResultSet:usingFetchPlan:withMatchingRows:] | CFStringCreateWithCString | __CFStringCreateImmutableFunnel3 | _CFRuntimeCreateInstance | malloc_zone_malloc 
把我的头发拔出来,因为在我释放
headerLabel
后,绝对没有任何消息或呼叫。有人有什么建议吗?请

----更新

- (void) viewWillAppear:(BOOL)animated {

[super viewWillAppear:animated];

[dataViewScroller.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];

[self.myTableViewControllers removeAllObjects];
self.myTableViewControllers = nil;

// All database modifcations should dynamically reload when the view reappears

/* RE-OBTAIN CATEGORIES */
NSManagedObjectContext *context = [myAppTestAppDelegate managedObjectContext];

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyObjectType" inManagedObjectContext:context];
[fetchRequest setEntity:entity];

NSError *errorInRequest = nil;
NSArray *results = [context executeFetchRequest:fetchRequest error:&errorInRequest];

if(errorInRequest)
{
    NSLog(@"Unresolved error %@, %@", errorInRequest, [errorInRequest userInfo]);
    abort();
}

[fetchRequest release];

kNumberOfPages = [results count];

dataViewPageControl.numberOfPages = kNumberOfPages;
    dataViewPageControl.currentPage = 0;
dataViewScroller.contentSize = CGSizeMake(dataViewScroller.frame.size.width * kNumberOfPages, dataViewScroller.frame.size.height);

// the view controllers are created lazily
// in the meantime, load the array with placeholders which will be replaced on demand
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < kNumberOfPages; i++) {
    [controllers addObject:[NSNull null]];
}

self.myTableViewControllers = controllers;
[controllers release];

int index = 0;

for (NSManagedObject *type in results) {
    [self loadScrollViewWithPage:index withIdentifier:[type valueForKey:@"name"]];
    index++;
}

}

- (void)loadScrollViewWithPage:(int)page withIdentifier:(NSString *)ident {

if (page < 0) return;
if (page >= kNumberOfPages) return;

MyTableListViewController *controller = [myTableViewControllers objectAtIndex:page];

if ((NSNull *)controller == [NSNull null]) {
    controller = [[MyTableListViewController alloc] initWithPageNumber:page withHeader:ident];
    controller.managedObjectContext = [myAppTestAppDelegate managedObjectContext];
    [myTableViewControllers replaceObjectAtIndex:page withObject:controller];
    [controller release];
}
if (nil == controller.view.superview) {
    CGRect frame = dataViewScroller.frame;
    frame.origin.x = frame.size.width * page;
    frame.origin.y = 0;
    controller.view.frame = frame;
    [dataViewScroller addSubview:controller.view];
}
}
-(void)视图将显示:(BOOL)动画{
[超级视图将显示:动画];
[dataViewScroller.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
[self.myTableViewControllers removeAllObjects];
self.myTableViewController=nil;
//当视图重新出现时,应动态重新加载所有数据库修改
/*重新获取类别*/
NSManagedObjectContext*上下文=[myAppTestAppDelegate managedObjectContext];
NSFetchRequest*fetchRequest=[[NSFetchRequest alloc]init];
NSEntityDescription*entity=[NSEntityDescription entityForName:@“MyObjectType”在ManagedObjectContext:context中];
[FetchRequestSetEntity:entity];
nError*errorInRequest=nil;
NSArray*结果=[context executeFetchRequest:fetchRequest错误:&errorInRequest];
如果(错误请求)
{
NSLog(@“未解决的错误%@,%@”,errorInRequest,[errorInRequest userInfo]);
中止();
}
[请求释放];
kNumberOfPages=[结果计数];
dataViewPageControl.numberOfPages=kNumberOfPages;
dataViewPageControl.currentPage=0;
dataViewScroller.contentSize=CGSizeMake(dataViewScroller.frame.size.width*kNumberOfPages,dataViewScroller.frame.size.height);
//视图控制器是惰性创建的
//同时,加载带有占位符的数组,占位符将根据需要替换
NSMutableArray*控制器=[[NSMutableArray alloc]init];
for(无符号i=0;i=kNumberOfPages)返回;
MyTableListViewController*控制器=[MyTableViewController对象索引:页];
如果((NSNull*)控制器==[NSNull]){
控制器=[[MyTableListViewController alloc]initWithPageNumber:page withHeader:Identit];
controller.managedObjectContext=[myAppTestAppDelegate managedObjectContext];
[myTableViewControllers replaceObjectAtIndex:page with Object:controller];
[控制器释放];
}
if(nil==controller.view.superview){
CGRect frame=dataViewScroller.frame;
frame.origin.x=frame.size.width*页;
frame.origin.y=0;
controller.view.frame=frame;
[dataViewScroller addSubview:controller.view];
}
}

如您所见,当根ViewController加载时,它们总是动态加载。这是因为用户可以转到帐户设置类型管理页面,并向其设置中添加其他类别。返回根视图时,必须在滚动视图中呈现新的/或删除的类别。如果他们最初有3个类别(即3个TableViewController),然后转到管理页面并创建了一个新的类别,则在返回UIScrollView时,应重新加载所有4个类别(4个动态创建的TableViewController)。这有意义吗?

尝试在将containerView添加到self.view后手动释放containerView,而不是使用自动释放?

您确定视图控制器没有过早释放吗

看起来
headerLabel
已(结束)释放,但这并非源于您的代码,因为它由
containerView
保留,后者由视图控制器保留,后者由
self.view
保留

因此,请尝试修改视图控制器的解除锁定方法,如下所示:

-(void)dealloc
{
     NSLog(@"View controller deallocing");
     // ... whatever you had here
     [super dealloc];
}
并检查控制台中的日志消息。

更新(基于评论):

Cocoa在幕后做了大量的视图魔术,所以我想我可以看到问题所在。
视图将出现
通知将自动发送到所有即将出现的视图控制器。这意味着它将被发送到您的根控制器,以及您刚刚发布其视图的所有三个ViewController!。简单地说,您正在删除视图继承权的一部分,而Cocoa正在向该继承权内的视图发送消息


为了解决这个问题,我推荐一些不那么激烈的东西。每次视图出现时,不需要完全销毁和重新创建这三个子视图。根viewController应该保留对它创建的三个viewController的引用,然后向它们发送一条消息,以便在时机成熟时刷新或重新显示它们的内容。

如果在方法结束后发生这种情况,则表明程序正在尝试
// Load the view nib and initialize the pageNumber ivar.
- (id)initWithPageNumber:(int)page withHeader:(NSString *)header withType:(NSManagedObject *)type {
if (self = [super initWithNibName:@"ExpenseListViewController" bundle:nil]) {

    pageNumber = page;
            // below WAS the offending line.
    // headerText = header;
            self.headerText = header;

}
return self;