Iphone EXC_错误访问-此代码如何导致泄漏?通过malloc_history&;NSzombied已启用
更新(在底部):添加的整个视图将显示:方法实现,以显示三个UIViewController如何分配/解除分配到UIScrollView中 我有一个根ViewController,其中包含一个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
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;