Сообщения анализатора о «Неправильном уменьшении» и «Потенциальной утечке»

#objective-c #memory-management #properties #static-analysis

#objective-c #управление памятью #свойства #статический анализ

Вопрос:

Когда я компилирую с анализатором, я получаю пару сообщений. У меня объявлены эти свойства:

 @property (nonatomic, retain) SyncServicePrimary *syncAndCartOne;
@property (nonatomic, retain) SyncServiceSecondary *syncAndCartTwo;
  

Этот метод вызывается из applicationDidBecomeActive , и я получаю «Потенциальную утечку выделенного объекта».

 -(void)makeTheCartObjectsForCountry:(NSString*)country_key{
    self.syncAndCartOne = [[SyncServicePrimary alloc] init];
    self.syncAndCartTwo = [[SyncServiceSecondary alloc] init];
}
  

Это вызывается applicationWillResignActive ; здесь я получаю «Неправильное уменьшение количества ссылок объекта».

 -(void) removeTheCartObjects{
    [self.syncAndCartOne release];
    self.syncAndCartOne = Nil;    
    [self.syncAndCartTwo release];
    self.syncAndCartTwo = Nil; 
}
  

Если я установлю для объектов значение autorelease , ошибка исчезнет, но я хочу, чтобы объекты освобождались, когда приложение скрывается.

Это то, что я делаю правильно, но это слишком далеко, чтобы анализатор мог видеть начало и конец, или это то, что я могу сделать лучше / правильно, чтобы он не жаловался?

Более чем вероятно, что мне не хватает простой концепции в отношении release alloc циклов и (я пришел из PHP и C #).

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

1. Обратите внимание, что Nil это нулевой литерал для указателей на классы. Вместо указателей на объекты следует использовать nil .

2. Просто чтобы довести это до конца, если я выполняю нулевую проверку экземпляра класса; использование » == Nil» — это не то же самое, что «== nil»?

Ответ №1:

Ваша проблема здесь:

 -(void)makeTheCartObjectsForCountry:(NSString*)country_key{
    self.syncAndCartOne = [[SyncServicePrimary alloc] init];
    self.syncAndCartTwo = [[SyncServiceSecondary alloc] init];
}
  

Вы создаете объекты, а затем сохраняете их (из-за объявления свойства), поэтому у них количество ссылок равно 2, когда на них ссылается только один объект.

Вы должны сделать это следующим образом:

 -(void)makeTheCartObjectsForCountry:(NSString*)country_key{
    SyncServicePrimary *primary = [[SyncServicePrimary alloc] init];
    self.syncAndCartOne = primary;
    [primary release];

    SyncServiceSecondary *secondary = [[SyncServiceSecondary alloc] init];
    self.syncAndCartTwo = secondary;
    [secondary release];
}
  

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

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

2. И это всегда лучше, потому что …?

3. Я бы хотел избежать автоматического освобождения, особенно с этими объектами, потому что они порождают несколько потоков на основе таймеров и nsnotifications, если я неявно не очищаю их, я не могу быть на 100% уверен, что не будет двух потоков, обращающихся к одной и той же службе rest

4. Итак, просто отметим, что в случае автоматически выпущенных методов класса и сохраненных свойств я могу просто оставить их как есть? например [self setTitleString:[NSString stringWithFormat:@"%@", mystring]];

5. @misterkoz Вы можете оставить их как есть.

Ответ №2:

Вы определили свойства с помощью атрибута retain , поэтому анализатор предполагает, что метод установки для свойства выглядит следующим образом:

 - (void)setSyncAndCartOne:(SyncServicePrimary *)newValue
{
    [newValue retain];
    [self->_syncAndCartOne release]; // access the instance variable holding the property value
    self->_syncAndCartOne = newValue;
}
  

Если вы используете @synthesize , метод setter будет выглядеть так.

Итак, при makeTheCartObjectsForCountry: возврате объект в syncAndCartOne имеет счетчик сохранения 2, но должен иметь только счетчик сохранения 1. Вот почему using autorelease исправляет это.

Вы не должны делать [self.syncAndCartOne release] по той же причине. Метод setter отправит старый объект a release при присвоении nil свойства.

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

1. Потрясающе, так что, похоже, я дважды сохраняю и создаю избыточную строку с помощью вызова = nil . хотелось бы, чтобы я мог установить два в качестве принятого ответа…