Obj-C, потенциальная утечка объекта, выделенного в строке, выделение UIBarButtonItem

#objective-c #xcode #cocoa-touch #analyzer

#objective-c #xcode #cocoa-touch #анализатор

Вопрос:

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

Что я делаю не так?

Заголовочный файл:

 @interface myViewController : UIViewController <UITableViewDataSource, 
               UITableViewDelegate> {

    UIBarButtonItem *addButton;
}
@property (nonatomic, retain) UIBarButtonItem *addButton;
  

Основной файл:

 @synthesize addButton;
- (void)viewDidLoad {

    NSMutableArray* buttons = [[NSMutableArray alloc] initWithCapacity:3];


    addButton = [[UIBarButtonItem alloc]
                            initWithBarButtonSystemItem:UIBarButtonSystemItemAdd 
                            target:self action:@selector(btnNavAddPressed:)];
    addButton.style = UIBarButtonItemStyleBordered;
    [buttons addObject:addButton];

    [tools setItems:buttons animated:NO];
    [buttons release];

    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] 
                                initWithCustomView:tools];

    addButton.enabled = FALSE;

- (void)dealloc {
    [addButton release];
  

Ответ №1:

когда вы используете свойство и присваиваете ему указанные вами атрибуты, определите, увеличивается ли retainCount, если вы присваиваете свойство. В вашем случае вы указали « retain «, что означает, что функция setter, которая обрабатывает присвоение вашему свойству, автоматически увеличит количество сохранений для объекта.

Однако, когда вы пишете

  addButton = [[UIBarButtonItem alloc]
                            initWithBarButtonSystemItem:UIBarButtonSystemItemAdd 
                            target:self action:@selector(btnNavAddPressed:)];
  

вы создаете opject с уже сохраненным количеством == 1, поэтому при назначении у него будет сохраненное количество 2. правильный способ сделать это — создать временную переменную и создать объект, затем присвоить временную переменную свойству, после чего отпустить temp. переменная:

 UIBarButtonItem* tmp = [[UIBarButtonItem alloc]
                            initWithBarButtonSystemItem:UIBarButtonSystemItemAdd 
                            target:self action:@selector(btnNavAddPressed:)];
self.addButton = tmp;
[tmp release];
  

конечно, я бы рекомендовал в качестве имени переменной более описательное имя, чем ‘temp’.

Комментарии:

1. это вводящий в заблуждение совет. Код операционной системы wrt addButton в порядке как есть. Он правильно сохраняет выделенный объект непосредственно в iVar и освобождает его в dealloc .

Ответ №2:

Вы не используете установщик, код должен быть:

 self.addButton = [[[UIBarButtonItem alloc]
                        initWithBarButtonSystemItem:UIBarButtonSystemItemAdd 
                        target:self action:@selector(btnNavAddPressed:)] autorelease];
  

Такого рода проблем можно избежать, используя ivar, имя которого отличается от имени свойства. Это выполняется в @synthesize инструкции:

 @synthesize addButton = _addButton;
  

Таким образом, любое упущение self приведет к сообщению об ошибке.

Вот полная реализация (за исключением того, что tools не определено), свойство addButton обрабатывается во всех местах:

 @interface myViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> {
}
@property (nonatomic, retain) UIBarButtonItem *addButton;
@end

@implementation myViewController
@synthesize addButton = _addButton;

- (void)viewDidLoad {
    NSMutableArray* buttons = [NSMutableArray array];

    self.addButton = [[UIBarButtonItem alloc]
                 initWithBarButtonSystemItem:UIBarButtonSystemItemAdd 
                 target:self action:@selector(btnNavAddPressed:)];
    self.addButton.style = UIBarButtonItemStyleBordered;
    [buttons addObject:self.addButton];

    [tools setItems:buttons animated:NO];

    self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithCustomView:tools] autorelease];

    self.addButton.enabled = FALSE;
}
- (void)dealloc {
    [_addButton release];
}
@end
  

Комментарии:

1. Не приведет ли это к утечке памяти? (эти комментарии стали намного сложнее после ARC!)

2. @jrturton Упс, исправлено в ответе. Спасибо! Конечно, в ARC авторелиз не требуется.

3. Вот что я имею в виду, раньше было действительно легко обнаружить утечки, теперь появился целый дополнительный слой. Я предпочитаю старые добрые времена…

4. Пусть это будут «старые добрые времена». 🙂 Я считаю, что писать под ARC намного проще.

5. @CocoaFu, похоже, это не исправляет проблему и @synthesize addButton = _ addButton; выдает мне ошибку Parse Issue: Expected ';' after @synthesize . Также пробовал использовать self. перед всеми вызовами addButton. Есть дополнительные советы?

Ответ №3:

Оба приведенных выше ответа вводят в заблуждение. Вам не нужно использовать установщик, вполне нормально назначать объекты непосредственно iVars. Однако вам нужно освободить все, что вы выделяете или сохраняете. Проблема, с которой вы столкнулись, заключается вот в чем:

 self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:tools];
  

Эта строка выделяет UIBarButtonItem экземпляр и присваивает ему rightBarButtonItem свойство navigationItem . Это означает, что navigationItem сохраняет UIBarButtonItem и несет ответственность за это сохранение. Вы несете ответственность за его освобождение с помощью alloc , а вы нет. Измените код на этот:

 self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithCustomView:tools] autorelease];
  

и эта утечка устраняется.

Комментарии:

1. Проблемы такого типа являются одним из наиболее распространенных препятствий для разработчиков iOS. Это действительно становится проще / естественнее по мере того, как вы набираетесь опыта. Если вы используете xcode 4.2, я рекомендую прочитать документы по ARC (автоматический подсчет ссылок) и перенести ваш проект на это. Это не меняет правила управления памятью, но позволяет компилятору выполнять почти всю работу за вас.

Ответ №4:

Вы не используете заявленное свойство, но я не вижу никакой проблемы с addButton . Утечка, похоже, больше в:

 self.navigationItem.rightBarButtonItem = 
                             [[UIBarButtonItem alloc] initWithCustomView:tools];
  

Просто добавьте autorelease , и утечка исчезнет.

Комментарии:

1. Это странно, я скопировал ваш код, и отсутствие автоматического освобождения — единственная проблема, обнаруженная мной и анализатором. Очистить и проанализировать? Или вы можете опубликовать скриншот анализатора (тот, что со стрелками)?