Зачем назначать nil для IBOutlets в viewDidUnload? - PullRequest
3 голосов
/ 06 сентября 2011

У меня есть UIViewController, который имеет IBOutlet для UILabel с этикеткой, подключенной в XIB.

#import <Foundation/Foundation.h>

@interface MyViewController : UIViewController {
    IBOutlet UILabel *aLabel; 
}
@end

Согласно Программирование на iOS: Руководство по ранчо большого ботаника (2-е издание) глава 7

Когда [MyViewController] перезагружает свое представление, из файла XIB создается новый экземпляр UILabel.

Таким образом, рекомендуется выпустить метку в viewDidUnload.

- (void)viewDidUnload {
   [super viewDidUnload];
   [aLabel release];
   aLabel = nil;
}

Будучи программистом на C #, я понял, что присваивать nil / null вещам бессмысленно. Хотя я вижу, что в Objective-C это имеет больше смысла, это все же немного смягчает мое чувство эстетики кода *. Я удалил его, и все заработало нормально.

Однако, когда я пытался сделать аналогичную вещь с MKMapView, ошибки приложения EXC_BAD_ACCESS при попытке загрузить NIB.

#import <Foundation/Foundation.h>

@interface MyViewController : UIViewController {
    IBOutlet MKMapView *mapView;
}
@end

- (void)viewDidUnload {
    [super viewDidUnload];
    [mapView release];
    mapView = nil; // Without this line an exception is thrown.
}

Почему возникает ошибка, когда mapView не установлен на nil, но не когда aLabel не установлен на nil?

* Я понимаю, что мне нужно настроить эстетику кода под новый язык, но это требует времени.


Оказывается, я просто ошибся из-за того, что на aLabel не ссылаются. Не уверен, что заставило меня думать, что это не так.

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

Когда поле или свойство установлено, сообщение об освобождении отправляется на старое значение (либо метод набора синтезированных свойств отправляет сообщение об освобождении, либо setValue:forKey: отправляет сообщение, если это поле). Поскольку старое значение уже было освобождено, это приводит к EXC_BAD_ACCESS.

Ответы [ 2 ]

2 голосов
/ 06 сентября 2011

Это из-за управления памятью, в частности из-за отсутствия сборки мусора.

В C # (как вы знаете) объекты, которые больше не находятся в области видимости, удаляются.В объективе-c этого не происходит.Вы должны полагаться на сохранение / освобождение, чтобы сообщить объекту, когда вы закончите с ним.

Недостаток метода подсчета ссылок Objective-C, который имеет ваша ошибка mapView.Вызов release объекта может привести к его освобождению.Тем не менее, ваш указатель на объект все еще будет указывать на то же место - вашего объекта просто больше не будет.

Например,

// We create an object.
MyObject *object = [[MyObject alloc] init];

// At this point, `object` points to the memory location of a MyObject instance
// (the one we created earlier). We can output that if we want :
NSLog(@"0x%08X", (int)myObject);

// You should see a number appear in the console - that's the memory address that
// myObject points to.
// It should look something like 0x8f3e4f04

// What happens if we release myObject?
[myObject release];

// Now, myObject no longer exists - it's been deallocated and it's memory has been
// marked as free

// The myObject pointer doesn't know that it's gone - see :
NSLog(@"0x%08X", (int)myObject);

// This outputs the same number as before. However, if we call a method on myObject
// it will crash :
NSLog(@"%@", myObject);

В target-c, если вы попытаетесьчтобы позвонить на nil, ничего не происходит.Поэтому, если каждый раз, когда вы заканчиваете работу с объектом и вызываете функцию release, вы должны также установить для него значение nil - это означает, что если вы попытаетесь использовать этот указатель снова, он не потерпит крах!

2 голосов
/ 06 сентября 2011

viewDidUnload обычно вызывается, когда устройство получает предупреждение памяти.

В стеке просмотра могут быть ситуации, когда устройство может освободить память, освободив объекты, которые не используются.Представьте, что у вас есть стек навигации с несколькими контроллерами представления.Контроллеры представления интерфейса ниже в стеке не доступны, но все еще используют память.Поэтому обычно хорошей идеей является удаление любых элементов интерфейса, к которым нет доступа.Затем они будут перезагружены с помощью viewDidLoad при необходимости.

Обычно в ViewDidUnload вы должны освобождать любые объекты представления, созданные из файла Nib или выделенные в вашем методе ViewDidLoad

Your mapView вызывает исключение, потому что ваш контроллер представления пытается получить доступ к MapView, но mapView был освобожден.Если вы установите нулевое значение для торговой точки, все отправленные ей сообщения будут игнорироваться.

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