EXC_BAD_ACCESS - Как этот код вызывает утечку?Отслеживается с помощью malloc_history и NSZombieEnabled - PullRequest
1 голос
/ 15 марта 2011

ОБНОВЛЕНИЕ (внизу): Добавлен весь viewWillAppear: реализация метода, чтобы показать, как три UIViewController распределяются / освобождаются в UIScrollView.

У меня естькорневой ViewController, который содержит UIScrollView.Этот UIScrollView сам содержит 3 различных UIViewController с (для UITableView с устанавливается NSFetchedResultControllerDelegate с)

Мне нужно перезагрузить корневого 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.
}

Вот метод viewDidLoad для каждого из UIViewControllers, загруженных в UIScrollView, выглядит следующим образом:

 - (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] закомментирована.С этим программа работает нормально!Если я включаю линию [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 после того, как я отпустил it.У кого-нибудь есть предложения?пожалуйста ??

---- ОБНОВЛЕНИЕ

- (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];
}
}

Итак, как вы можете видеть, они всегда загружаются динамически при загрузке Root ViewController.Это связано с тем, что пользователь может перейти на страницу администрирования типа Настройки учетной записи и добавить дополнительные категории в свои настройки.При возврате к корневому представлению в представлении прокрутки должны отображаться категории NEW / OR DELETED.Если у них изначально было 3 Cateogories (т.е. 3 TableViewControllers), и они перешли на страницу администратора и создали новую, при возврате UIScrollView должен перезагрузить все 4 категории (4 динамически создаваемых TableViewControllers.) Имеет ли это смысл?

Ответы [ 5 ]

2 голосов
/ 15 марта 2011

Вы уверены, что ваш контроллер представления не был выпущен преждевременно?

Похоже, что headerLabel выпущен (пере), но это не происходит из вашего кода, так как он сохраняется containerView, который сохраняется в self.view, который хранится в вашем контроллере представления.

Поэтому попробуйте изменить метод контроллеров представления dealloc следующим образом:

-(void)dealloc
{
     NSLog(@"View controller deallocing");
     // ... whatever you had here
     [super dealloc];
}

и проверьтеконсоль для сообщения журнала.

1 голос
/ 15 марта 2011

Обновление (основано на комментариях):

Какао делает много магии обзора за кулисами, поэтому я думаю, что вижу, в чем проблема.Уведомление viewWillAppear автоматически отправляется всем контроллерам представления, которые должны появиться.Это означает, что он будет отправлен вашему корневому контроллеру и всем трем контроллерам представлений, чьи представления вы только что выпустили! .Проще говоря, вы удаляете части иерархии представлений, пока Какао находится в процессе отправки сообщений представлениям внутри этой иерархии.

Чтобы решить эту проблему, я бы порекомендовал что-то менее решительное.Вам не нужно полностью уничтожать и заново создавать три подпредставления каждый раз, когда появится ваше представление.Ваш корневой viewController должен сохранить ссылку на три создаваемых им viewController, а затем просто отправить им сообщение, чтобы обновить или заново отобразить их содержимое, когда придет время.

0 голосов
/ 17 марта 2011

Спасибо @ e.James и @mvds за помощь в поиске этого.Основываясь на истории malloc в этом посте и первоначальном предложении @ e.James, чтобы исследовать выборку, мне удалось углубиться в детали и найти проблему виновника.вызывается для создания и размещения UIViewControllers, найденных в UIScrollView:

// 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;

}

Теперь все правильно, и все работает, как задуманоПо сути, я не использовал метод setter для сохраненного и синтезированного свойства ( headerText = header ).Добавление self. исправило все.

0 голосов
/ 15 марта 2011

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

0 голосов
/ 15 марта 2011

Попробуйте вручную запустить containerView после добавления его в self.view вместо использования автоматического выпуска?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...