Objective - C отсчет ссылок, когда указатель установлен в ноль (без дуги)
Я пытаюсь понять, как работает подсчет ссылок, поэтому отключил ARC и написал простой класс: (Foo.h не вставляется, так как он немодифицирован)
Фу.m
@implementation Foo
- (instancetype)init
{
NSLog(@"Init object");
return [super init];
}
- (void)dealloc
{
NSLog(@"Dealloc object");
[super dealloc];
}
@end
Главная.m
#import <Foundation/Foundation.h>
#import "Foo.h"
int main(int argc, const char * argv[]) {
Foo *obj = [[Foo alloc] init];
obj = nil;
return 0;
}
Теперь я ожидаю увидеть журнал dealloc object
, потому что единственная ссылка на объект Foo
исчезла, но единственное сообщение, которое я получаю, - это init object
.
Почему я не вижу его? Разве объект не освобождается, когда я назначаю obj = nil
?
2 ответа:
Нет. Если вы не используете ARC, объект освобождается при вызове
[obj release];
. (ARC вставляет эти вызовы для вас.) Установкаobj
вnil
ничего не делает с точки зрения управления памятью (хотя и создает объект, до которого вы больше не можете добраться!).В основном, в какао без дуги:
- вы вызываете
[obj retain]
, Если хотите стать владельцем объекта. (alloc
делает это для вас.)- вы вызываете
[obj release]
, когда хотите отказаться от права собственности на объект.release
в свою очередь вызываетdealloc
когда счетчик удержания объекта достигает 0.- вы вызываете
[obj autorelease]
, когда хотите отказаться от владения объектом вне его текущей области действия. Чаще всего это происходит, когда вы возвращаете объект из метода (и не хотите сохранять право собственности на него).
Пара дополнительных наблюдений, чтобы расширить превосходный ответ мипади:
В коде без дуги установка переменной в
nil
не будет освобождать объект. Вы должны явным образомrelease
/autorelease
это.Чтобы быть более точным об этом, если право собственности было передано вам (то есть вы получаете объект от метода, имя которого начинается с любого
Суть в том, что просто установить переменную вalloc
,new
,copy
, илиmutableCopy
), то вы несете ответственность за явный вызовrelease
/autorelease
.nil
недостаточно.- Я не решаюсь упомянуть об этом, опасаясь затуманить проблему, но когда речь идет о свойствах, это немного отличается. При использовании свойств вызов задатчика свойства
retain
автоматически создаст объектretain
для вас. Если вы позже установите свойство вnil
, то сеттер будет автоматическиrelease
его для вас.Представьте себе класс, подобный Итак:
@interface Bar : NSObject @property (nonatomic, retain) Foo *foo; @end
Если вы хотите установить свойство foo, вы можете сделать следующее
Foo *f = [[Foo alloc] init]; // create Foo object with +1 retain count self.foo = f; // the `retain` property will increase retain count to +2 [f release]; // resolve the local strong reference, reducing retain count back to +1
Или, проще говоря:
self.foo = [[[Foo alloc] init] autorelease];
Что они делают, так это создают объект
Foo
с числом удержания +1. Задавая свойствоfoo
, вы создаете другую ссылкуstrong
и, таким образом, получаете число удержания +2. А когда ты тогда ...release
/autorelease
это, число удержания падает обратно до +1.Позже, когда вы закончите с
foo
и захотите освободить его, вы просто вызовете снова сеттер, на этот раз со значениемnil
:Это снимает сильную ссылку, которую свойствоself.foo = nil; // resolve the `retain` done by the property, reducing the retain count to +0
foo
сохраняет на объект. Вы не звонитеrelease
себе (по крайней мере, в свойстве). Очевидно, что когда вы вызываете сеттер со значениемnil
, предыдущий объект не только освобождается,но если это была последняя сильная ссылка, объект будет освобожден и для вас.Короче говоря, исходное
alloc
смещено на arelease
/autorelease
, не от кого бы то ни было установка переменной вnil
. Тем не менее, установка свойстваretain
разрешена путем установки этого свойства вnil
.
Пара заключительных замечаний.
Я бы предложил, чтобы при написании кода без дуги вы внимательно изучили руководство по программированиюAdvanced Memory Management, особенно главуMemory Management Policy .
Статический анализатор Xcode (сдвиг+команда+B , или "Analyze "в меню" Product " Xcode) отлично справляется с выявлением проблем с памятью, которые беспокоят код без дуги. Всегда убедитесь, что у вас нет абсолютно никаких предупреждений от статического анализатора.
При использовании инструментов инструмент распределения имеет функцию, называемую "учет отсчетов ссылок" (см. https://stackoverflow.com/a/14105056/1271826 ). если вы обнаруживаете, что какой-то объект не освобождается, этот инструмент может быть полезно при диагностике полного жизненного цикла объекта и всех его сохраняемых отсчетов.