Статический анализатор показывает неправильную утечку ?? (XCode 4.0, iOS 4.3 и выше)

Всех с ноябрем,

Что ж, я попробовал Xcode Build и проанализировал свой проект, и он показал несколько необычных утечек, с которыми я не мог полностью согласиться с моим знанием Objective C.

Вот и решил поставить тестовый проект и спросить здесь ..

MemoryTestController.h

@interface MemoryTestController : UIViewController{
  UIImageView *tstImageView;
}
@property(nonatomic,retain) UIImageView *tstImageView;
@end

MemoryTestController.m

@implementation MemoryTestController
@synthesize tstImageView;

- (void)viewDidLoad{
  [super viewDidLoad];

  self.tstImageView  = [[UIImageView alloc] //<==This object is leaking
                           initWithFrame:<SomeFrame>]; 
  self.tstImageView.image = [UIImage imageNamed:@"SomeImage.png"];
  [self.view addSubview:tstImageView];
  [tstImageView release];
}

-(void)dealloc{
  [tstImageView release];
  [super dealloc];
}
@end

Когда я пытаюсь построить и анализировать, статический анализатор говорит:

Возможная утечка объекта на линии xx

И линия виновника

self.tstImageView  = [[UIImageView alloc]initWithFrame:<SomeFrame>];

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

РЕДАКТИРОВАТЬ: Есть ли утечка?

Что ж, я запустил вышеуказанный проект, используя инструмент Leak в приборе ... Он не показал никаких утечек, хотя я пытался много раз ... Кому я должен верить? Статический анализатор или прибор для проверки утечек?


person Krishnabhadra    schedule 03.11.2011    source источник


Ответы (2)


ваша проблема в том, как вы его выпускаете:

- (void)viewDidLoad{
  [super viewDidLoad];

  self.tstImageView  = [[UIImageView alloc] //<==This object is leaking
                           initWithFrame:<SomeFrame>]; 
  self.tstImageView.image = [UIImage imageNamed:@"SomeImage.png"];
  [self.view addSubview:tstImageView];
  [tstImageView release]; // << here
}

вы должны сделать это так:

- (void)viewDidLoad{
  [super viewDidLoad];

  UIImageView * imageView  = [[UIImageView alloc] initWithFrame:<SomeFrame>]; 
  imageView.image = [UIImage imageNamed:@"SomeImage.png"];
  self.tstImageView  = imageView;
  [imageView release];
  [self.view addSubview:self.tstImageView];
}

Программа проверки верна, потому что она не может предполагать, что переменная идентична той, которую вы установили. Следовательно, форма, которую вы используете в OP, может привести к дисбалансу счетчика ссылок, потому что значение ivar может не совпадать с тем, что вы ему присвоили к моменту выпуска сообщения на ivar.

Эти случаи маловероятны для UIImageView и весьма маловероятны в контексте вашей программы, но эти примеры должны дать вам представление о том, почему средство проверки предполагает, что ассоциациям object-> ivar нельзя доверять:

Между созданием представления изображения и сообщением о его выпуске через ivar у вас есть:

  self.tstImageView  = [[UIImageView alloc] initWithFrame:<SomeFrame>]; 
  self.tstImageView.image = [UIImage imageNamed:@"SomeImage.png"];
  [self.view addSubview:tstImageView];

1) назначение вида изображения через сеттер 2) доступ к просмотру изображения через геттер 3) прямой доступ к ivar при добавлении в self.view

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

Самый простой пример:

- (void)setName:(NSString *)inName {
  NSString * prev = name;
  if (inName == prev) return;
  if (0 == [inName count]) name = @"";
  else name = [inName copy];
  [prev release];
}
  • Стоимость ивара за это время может измениться. вряд ли проблема в этом случае, но предположим, что добавление представления изображения в качестве подпредставления может привести к обратному вызову и изменению self в процессе / эффекте добавления подпредставления и замене или удалению переданного представления изображения. В этом случае представление переменной, которое вы передали, утекло бы, а представление, которым оно его заменило, имело бы отрицательный дисбаланс.

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

РЕДАКТИРОВАТЬ: Есть ли утечка?

Хорошо, я запустил вышеуказанный проект, используя инструмент Leak в приборе. Он не показал никаких утечек, хотя я пробовал это много раз. Кому я должен верить? Статический анализатор или прибор для проверки утечек?

Статический анализатор сообщает, что существует потенциальная утечка, поскольку он не может гарантировать, что ссылка / выделение, за которым он следует, правильно сохранены / освобождены. Вы можете гарантировать правильный подсчет ссылок и порадовать статический анализатор, изменив свою программу так, чтобы она выглядела так, как я написал ее в моем примере.

То, как вы это написали, лишило анализатор возможности следовать ссылке.

Если у вас нет утечек и нет зомби, значит, утечки нет. Но решение легко исправить - и программы могут меняться в процессе разработки. Гораздо проще использовать опубликованную мною форму, чтобы облегчить набору инструментов и вам проверить правильность программы. Статический анализатор не всегда работает правильно, но вы должны настроить свои программы так, чтобы ему было удобно, потому что статический анализ очень полезен. Программу, которую я разместил, также легче понять и подтвердить, что она верна.

person justin    schedule 03.11.2011
comment
программа проверки верна, потому что она не может предполагать, что переменная идентична той, которую вы установили. следовательно, форма, которую вы используете в OP, может привести к дисбалансу счетчика ссылок, потому что он может измениться к тому времени, когда вы ее отпустите -------- вы можете немного объяснить это .. - person Krishnabhadra; 03.11.2011
comment
@Krishnabhadra в ответ на ваш первый комментарий: расширен. работаю над вторым вопросом. - person justin; 03.11.2011
comment
хмм ... @Justin, вы действительно помогли ... И вы правы, всегда полезно следовать некоторому принципу кодирования, который легко понять всем (даже если это человек или программа, такая как статический анализатор) .. Принимаю ваш ответ .. - person Krishnabhadra; 03.11.2011
comment
еще кое-что ... Вы уверены, что в ответе на функцию setName имели в виду if (name == prev) return;? - person Krishnabhadra; 03.11.2011
comment
@Krishnabhadra восклицает! спасибо, что указали на это =) это должно было быть if (inName == prev) return; ура - person justin; 03.11.2011

когда вы объявляете свойство с сохранением, как это

@property(nonatomic,retain) UIImageView *tstImageView;

добавлен сеттер, который будет увеличивать значение keepCount при назначении свойству. Когда вы делаете, как показано ниже, объект, который вы создали, уже имеет keepCount == 1

self.tstImageView  = [[UIImageView alloc] 
                           initWithFrame:<SomeFrame>];

поэтому объект tstImageView имеет 2 в keepCount.

делай вместо этого

UIImageView* view = [[UIImageView alloc] initWithFrame:<SomeFrame>];
self.tstImageView  = view;
[view release];

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

self.tstImageView = nil;

так как сеттер затем правильно установит keepCount

person AndersK    schedule 03.11.2011
comment
Абсолютные удержания бессмысленны. - person bbum; 03.11.2011